131 lines
6.7 KiB
Markdown
131 lines
6.7 KiB
Markdown
---
|
|
phase: 09-ux-tech-debt-closure
|
|
plan: "01"
|
|
subsystem: ui
|
|
tags: [fastapi, htmx, jinja2, oob-swap, driver-upload, pytest]
|
|
|
|
# Dependency graph
|
|
requires:
|
|
- phase: 03-printer-configuration
|
|
provides: printer_form.html with Alpine.js x-data and driver <select>
|
|
- phase: 04-driver-management
|
|
provides: upload_driver handler, DriverStore, Driver model
|
|
provides:
|
|
- HTMX OOB swap: POST /drivers/upload emits driver_list + printer-form-driver-select refresh
|
|
- Inline driver upload form inside printer form (sibling, not nested)
|
|
- Stable id="printer-form-driver-select" on driver <select> for OOB targeting
|
|
- caller=printer_form sentinel-based OOB branching in upload handler
|
|
- 4 new integration tests (500 regression x2 + OOB contract x2) + 1 printer form test
|
|
affects:
|
|
- 09-02-playwright-port-autofill (depends on final printer_form.html shape)
|
|
- 10-rtval (runtime validation uses driver upload flow)
|
|
|
|
# Tech tracking
|
|
tech-stack:
|
|
added: []
|
|
patterns:
|
|
- "HTMX OOB swap via hx-swap-oob=\"true\" on sibling element in same response body"
|
|
- "Caller-context sentinel: hidden form field name=caller value=printer_form"
|
|
- "FastAPI mixed multipart: UploadFile + Form() parameters in same handler"
|
|
- "TDD RED-GREEN: write failing tests, diagnose from output, implement fix"
|
|
|
|
key-files:
|
|
created:
|
|
- imptune/templates/partials/driver_upload_with_oob.html
|
|
- tests/test_printer_form.py
|
|
modified:
|
|
- imptune/api/drivers.py
|
|
- imptune/templates/partials/printer_form.html
|
|
- tests/test_driver_upload.py
|
|
|
|
key-decisions:
|
|
- "Sentinel field (caller=printer_form) chosen over HX-Target header for caller detection — explicit and testable without HTTP header manipulation"
|
|
- "OOB template includes driver_list.html as primary swap + sibling <select> with hx-swap-oob — clean separation of concerns"
|
|
- "Inline upload form placed as sibling after </form>, within Alpine x-data div — required by HTML spec (no nested forms)"
|
|
- "Hidden #driver-list anchor added to printer form page — provides HTMX outerHTML swap target without full driver list UI on the form"
|
|
|
|
patterns-established:
|
|
- "Pattern: HTMX OOB via separate template (driver_upload_with_oob.html) includes primary fragment + appends OOB elements"
|
|
- "Pattern: caller-aware handler branches on form field, not HTTP header"
|
|
|
|
requirements-completed: [UX-01]
|
|
|
|
# Metrics
|
|
duration: 5min
|
|
completed: "2026-04-13"
|
|
---
|
|
|
|
# Phase 09 Plan 01: Driver Upload Fix and Inline OOB Summary
|
|
|
|
**HTMX OOB driver-select refresh on upload: POST /drivers/upload now emits hx-swap-oob select when caller=printer_form, with printer form wired as sibling inline upload form**
|
|
|
|
## Performance
|
|
|
|
- **Duration:** ~5 min
|
|
- **Started:** 2026-04-13T08:46:30Z
|
|
- **Completed:** 2026-04-13T08:51:30Z
|
|
- **Tasks:** 3 (TDD: 2 TDD tasks + 1 template wiring task)
|
|
- **Files modified:** 5
|
|
|
|
## Accomplishments
|
|
|
|
- Fixed `POST /drivers/upload` caller-awareness: handler now accepts `caller: str = Form("")` parameter and branches on `caller == "printer_form"` to emit OOB-enabled response
|
|
- Created `driver_upload_with_oob.html` template: primary `#driver-list` fragment + sibling `<select hx-swap-oob="true" id="printer-form-driver-select">` with new driver auto-selected
|
|
- Wired inline driver upload form in `printer_form.html`: sibling `<form hx-post="/drivers/upload">` with `caller=printer_form` sentinel, outside the printer `<form>` to comply with HTML spec
|
|
- Added stable `id="printer-form-driver-select"` to driver `<select>` for OOB targeting
|
|
- Delivered 5 new tests: 2 parametrized 500 regression variants, 2 OOB contract tests, 1 printer form wiring test
|
|
|
|
## Task Commits
|
|
|
|
1. **Task 1: Write failing driver-upload regression + OOB contract tests** - `d1de839` (test)
|
|
2. **Task 2: Fix handler + add OOB template** - `10ee09a` (fix)
|
|
3. **Task 3: Wire inline upload form into printer_form.html** - `72c6a98` (feat)
|
|
|
|
## Files Created/Modified
|
|
|
|
- `imptune/api/drivers.py` - Added Form import, caller parameter, new_driver capture, OOB branch
|
|
- `imptune/templates/partials/driver_upload_with_oob.html` - New template: primary fragment include + OOB select
|
|
- `imptune/templates/partials/printer_form.html` - Stable select id, sibling upload form, hidden driver-list anchor
|
|
- `tests/test_driver_upload.py` - 4 new tests: 500 regression (x2 parametrized), OOB contract, no-OOB-on-standalone
|
|
- `tests/test_printer_form.py` - New file: test_printer_form_has_inline_driver_upload
|
|
|
|
## Decisions Made
|
|
|
|
- Sentinel field `caller=printer_form` chosen over `HX-Target` header — simpler, more explicit, testable without HTTP header manipulation in tests
|
|
- OOB template uses `{% include "partials/driver_list.html" %}` to avoid duplication; OOB select appended as sibling after the include
|
|
- Hidden `<div id="driver-list" style="display:none">` added to printer form to provide HTMX outerHTML swap target — keeps driver list hidden on printer form but enables HTMX to find the target
|
|
- Upload form placed AFTER `</form>` of the printer form but inside the Alpine `x-data` div — avoids invalid HTML nested forms while preserving Alpine scope
|
|
|
|
## Deviations from Plan
|
|
|
|
### Auto-fixed Issues
|
|
|
|
**1. [Rule 3 - Blocking] test_upload_500_regression passed without a real 500 repro**
|
|
|
|
- **Found during:** Task 1 (writing RED tests)
|
|
- **Issue:** The synthetic ZIP fixture doesn't reproduce the 500 that was reported. Both parametrized variants (plain UTF-8 and BOM/UTF-16 LE) returned 200. The handler was already robust enough for these cases.
|
|
- **Fix:** Kept both variants as documented regression guards. The 500 was pre-surfaced as a concern from Phase 8 kickoff; adding regression coverage is still correct even if the synthetic fixture doesn't repro it. The OOB tests were RED (the actual broken behavior).
|
|
- **Files modified:** tests/test_driver_upload.py (kept parametrized variants)
|
|
- **Verification:** 2 OOB tests went RED as expected; 500 variants green (correct behavior)
|
|
- **Committed in:** d1de839 (Task 1 test commit)
|
|
|
|
---
|
|
|
|
**Total deviations:** 1 auto-handled (plan expected 500 to repro; it didn't — OOB tests were the actual failures driving the fix)
|
|
**Impact on plan:** No scope change. Both the regression guard and OOB fix were delivered. Handler correctly returns 200 for tested fixtures.
|
|
|
|
## Issues Encountered
|
|
|
|
- Python bytes concatenation error in initial test code (`b"\xff\xfe" + str` instead of `b"\xff\xfe" + str.encode()`). Fixed inline before committing.
|
|
- File was overwritten by linter between edits; used `cat >>` bash append to reliably add new test functions to the file.
|
|
|
|
## Next Phase Readiness
|
|
|
|
- `printer_form.html` is in its final shape for 09-02 (Playwright test can assert Alpine port autofill against this version)
|
|
- OOB driver upload flow is fully wired and test-covered
|
|
- Full non-e2e suite: 112 passed, 0 failures
|
|
|
|
---
|
|
*Phase: 09-ux-tech-debt-closure*
|
|
*Completed: 2026-04-13*
|