--- phase: 3 slug: printer-configuration status: draft nyquist_compliant: true wave_0_complete: false created: 2026-04-10 nyquist_audited: 2026-04-13 nyquist_auditor: Claude (gsd-executor, plan 08-03) --- # Phase 3 — Validation Strategy > Per-phase validation contract for feedback sampling during execution. --- ## Test Infrastructure | Property | Value | |----------|-------| | **Framework** | pytest 8.x | | **Config file** | none — discovered via standard pytest directory scan | | **Quick run command** | `pytest tests/test_printer_crud.py -x -q` | | **Full suite command** | `pytest tests/ -v` | | **Estimated runtime** | ~5 seconds | --- ## Sampling Rate - **After every task commit:** Run `pytest tests/test_printer_crud.py -x -q` - **After every plan wave:** Run `pytest tests/ -v` - **Before `/gsd:verify-work`:** Full suite must be green - **Max feedback latency:** 5 seconds --- ## Per-Task Verification Map | Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status | |---------|------|------|-------------|-----------|-------------------|-------------|--------| | 03-01-01 | 01 | 1 | PRNT-01, PRNT-02 | integration | `pytest tests/test_printer_crud.py::test_create_printer_persisted -x` | ❌ W0 | ⬜ pending | | 03-01-02 | 01 | 1 | PRNT-04 | integration | `pytest tests/test_printer_crud.py::test_create_printer_duplex -x` | ❌ W0 | ⬜ pending | | 03-01-03 | 01 | 1 | PRNT-05 | integration | `pytest tests/test_printer_crud.py::test_create_printer_color_mode -x` | ❌ W0 | ⬜ pending | | 03-01-04 | 01 | 1 | PRNT-06 | integration | `pytest tests/test_printer_crud.py::test_create_printer_paper_size -x` | ❌ W0 | ⬜ pending | | 03-01-05 | 01 | 1 | PRNT-07 | integration | `pytest tests/test_printer_crud.py::test_create_printer_collate -x` | ❌ W0 | ⬜ pending | | 03-01-06 | 01 | 1 | PRNT-09 | integration | `pytest tests/test_printer_crud.py::test_printer_survives_page_refresh -x` | ❌ W0 | ⬜ pending | | 03-02-01 | 02 | 1 | PRNT-08 | integration | `pytest tests/test_printer_crud.py::test_printer_grouped_by_client -x` | ❌ W0 | ⬜ pending | | 03-03-01 | 03 | 2 | PRNT-10 | integration | `pytest tests/test_printer_crud.py::test_printer_detail_shows_driver -x` | ❌ W0 | ⬜ pending | | 03-01-XX | 01 | 1 | PRNT-03 | manual | Manual browser test | n/a | ⬜ pending | *Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky* --- ## Wave 0 Requirements - [ ] `tests/test_printer_crud.py` — stubs for PRNT-01 through PRNT-10 (integration tests) - [ ] `tests/test_client_crud.py` — stubs for client creation and listing (can merge into test_printer_crud.py) *Existing infrastructure covers framework and conftest.py — no new infrastructure needed.* --- ## Manual-Only Verifications | Behavior | Requirement | Why Manual | Test Instructions | |----------|-------------|------------|-------------------| | Alpine.js auto-populates port name from IP | PRNT-03 | Browser-side Alpine.js reactivity cannot be tested with pytest TestClient | 1. Open /printers/new 2. Type "192.168.1.100" in IP field 3. Verify port field shows "IP_192_168_1_100" 4. Edit port field manually 5. Change IP field 6. Verify port field keeps manual value | --- ## Nyquist Record > Audited 2026-04-13 by Claude (gsd-executor, plan 08-03). One row per Phase 3 success criterion derived from `milestones/v1.0-ROADMAP.md` Phase 3 goal + plan outcomes (PRNT-01..10), cross-checked against `03-VERIFICATION.md` (10/10 observable truths verified 2026-04-10, 9 automated + 1 human-needed on PRNT-03) and `REQUIREMENTS.md` v1.0 PRNT-0x block. Evidence cites committed tests, source lines, and the dated VERIFICATION report. Status values: `pass` / `fail-fix-v1.1` / `deferred-v1.2` / `wont-do`. > > **Phase 3 goal (v1.0-ROADMAP.md):** *"Technicians configure all printer parameters, assign printers to clients, and regenerate saved configs without re-uploading drivers."* | # | Success Criterion | Observable Check | Evidence | Status | Notes | |---|-------------------|------------------|----------|--------|-------| | 1 | **PRNT-01** — User can set a printer display name | `pytest tests/test_printer_crud.py::test_create_printer_persisted` verifies `name` field posted to `POST /printers` is persisted and rendered in `GET /printers` | `tests/test_printer_crud.py::test_create_printer_persisted`; `imptune/api/printers.py` `POST /printers` handler (commit 356c2ee); `imptune/templates/partials/printer_form.html` `name` input; 03-VERIFICATION.md row 1 (2026-04-10) | pass | | | 2 | **PRNT-02** — User can set a printer IP address or hostname | `pytest tests/test_printer_crud.py::test_create_printer_persisted` includes `ip_address` field in POST body; persisted value round-trips through `GET /printers` | `tests/test_printer_crud.py::test_create_printer_persisted`; `imptune/api/printers.py` POST handler (`ip_address: str = Form(...)`); `printer_form.html` IP field with `x-model`; 03-VERIFICATION.md row 1 | pass | | | 3 | **PRNT-03** — System auto-suggests port name from IP (user can override, manual edits preserved) | `pytest tests/e2e/test_port_autofill.py` — real Chromium via Playwright, types IP into form, asserts port field auto-fills with `IP_x_x_x_x`, then edits port manually, changes IP, asserts manual value preserved | `tests/e2e/test_port_autofill.py` (Phase 9 UX-02 commit 322fc20 test, 37a06da docs); `imptune/templates/partials/printer_form.html` Alpine.js `x-data`/`x-model`/`portEdited` guard (commit 356c2ee); `.planning/phases/09-ux-tech-debt-closure/09-02-SUMMARY.md` (UX-02 complete 2026-04-13); REQUIREMENTS.md v1.1 UX-02 = Complete; 03-VERIFICATION.md row 2 | pass | **Historical gap closed via Phase 9 / UX-02 fixing commits.** At v1.0 audit (03-VERIFICATION.md 2026-04-10) this was the sole `NEEDS HUMAN` truth — Alpine.js reactivity cannot execute inside FastAPI TestClient. Phase 9 Plan 02 added a Playwright headless-chromium live-browser e2e test that exercises the @input handler and the `portEdited` manual-edit lock. Closed as `pass` citing the fixing commits, consistent with the 08-01 (row 14 → Phase 10 RTVAL-01) and 08-02 (row 6 → Phase 9 UX-01) precedents. | | 4 | **PRNT-04** — User can set duplex mode | `pytest tests/test_printer_crud.py::test_create_printer_duplex` posts `duplex_mode=LongEdge` and verifies persisted value | `tests/test_printer_crud.py::test_create_printer_duplex`; `printer_form.html` `duplex_mode` select (OneSided/LongEdge/ShortEdge); `imptune/api/printers.py` POST handler mapping; 03-VERIFICATION.md row 1 + PRNT-04 coverage row | pass | | | 5 | **PRNT-05** — User can set color vs. grayscale default | `pytest tests/test_printer_crud.py::test_create_printer_color_mode` posts form without the `color_mode` checkbox and asserts the persisted value is `False` (checkbox-to-bool conversion) | `tests/test_printer_crud.py::test_create_printer_color_mode`; `printer_form.html` `color_mode` checkbox; `imptune/api/printers.py` checkbox-to-bool conversion in POST handler; 03-VERIFICATION.md PRNT-05 row | pass | | | 6 | **PRNT-06** — User can set paper size | `pytest tests/test_printer_crud.py::test_create_printer_paper_size` posts `paper_size=A4` (plus Letter/Legal variants) and verifies persisted value | `tests/test_printer_crud.py::test_create_printer_paper_size`; `printer_form.html` `paper_size` select (A4/Letter/Legal); 03-VERIFICATION.md PRNT-06 row | pass | | | 7 | **PRNT-07** — User can set collate on/off | `pytest tests/test_printer_crud.py::test_create_printer_collate` posts form without `collate` checkbox and asserts persisted value is `False` | `tests/test_printer_crud.py::test_create_printer_collate`; `printer_form.html` `collate` checkbox; `imptune/api/printers.py` checkbox-to-bool conversion; 03-VERIFICATION.md PRNT-07 row | pass | | | 8 | **PRNT-08** — User can assign a printer to a client/tenant label | `pytest tests/test_printer_crud.py::test_printer_grouped_by_client` creates printers under distinct clients and asserts `GET /printers` renders `