- Add GET /printers/{printer_id} route to pages.py with LEFT OUTER JOINs on Client and Driver
- Create printer_detail.html full-page template showing all config fields and driver info
- Display "No driver assigned" when driver FK is null
- Add disabled "Regenerate Package" button (Phase 4 placeholder)
- Make printer names in printer_list.html clickable links to detail page
- test_printer_detail_shows_driver: asserts driver name appears on detail page
- test_printer_detail_not_found: asserts 404 for missing printer ID
- test_printer_detail_no_driver: asserts graceful display when driver is None
- imptune/api/printers.py: POST /printers (all form fields, checkbox->bool,
FK resolution), DELETE /printers/{id}, grouped list renderer with LEFT JOIN
- imptune/api/clients.py: POST /clients with duplicate-name handling
- imptune/api/pages.py: GET /printers and GET /clients page routes
- imptune/main.py: register printers + clients routers; close db on shutdown
- imptune/db/database.py: close existing connection before re-init (test isolation)
- templates: printers.html, clients.html, printer_form.html (Alpine.js port
auto-derivation), printer_list.html (grouped by client), client_list.html
- tests/conftest.py: close test-thread db connection in fixture teardown
- tests/test_printer_crud.py: updated to use list(select().where()) for DB
queries (avoids Peewee thread-local cursor caching across test boundaries)
Auto-fix [Rule 1 - Bug]: DB test isolation — Peewee thread-local connections
persisted across tests causing stale DB reads; fixed via conftest teardown and
lifespan db.close() on shutdown.
- test_create_printer_persisted: POST /printers + GET /printers
- test_create_printer_duplex/color_mode/paper_size/collate: field persistence
- test_create_client: POST /clients + GET /clients
- test_printer_grouped_by_client: client group heading in list
- test_create_printer_missing_name/invalid_ip: validation 400s
- test_delete_printer: DELETE /printers/{id} removes record
Two plans covering PRNT-01 through PRNT-10: printer+client CRUD with
Alpine.js port derivation (plan 01), and printer detail page with
regeneration placeholder (plan 02).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Import init_db from imptune.db.database
- Call init_db() after DATA_DIR/DRIVERS_DIR creation in lifespan
- Database auto-initializes on every app startup via volume-mounted path
- imptune/db/database.py: deferred SqliteDatabase with WAL + foreign_keys pragmas and init_db()
- imptune/db/models.py: full schema (Client, Driver, Printer, Icon) for phases 1-5
- imptune/storage/driver_store.py: SHA256 content-addressed DriverStore with dedup
- tests/test_db.py: 7 TDD tests covering all db and storage behaviors
- requirements-dev.txt with pytest and httpx
- tests/conftest.py: client and tmp_data_dir fixtures
- tests/test_health.py: GET /health returns 200 with {"status": "ok"}
- tests/test_static.py: no CDN URLs in templates, dashboard returns 200
- Fix imptune/api/pages.py: use request= kwarg in TemplateResponse (Starlette compat)
- Fix imptune/main.py: replace deprecated on_event with asynccontextmanager lifespan
- AES-256-CBC encryption with PKCS7 padding using pycryptodome
- HMAC-SHA256 of ciphertext prepended to blob (mac_key, 32 bytes)
- Encrypted blob layout: HMAC(32) + IV(16) + ciphertext
- IV is 16 bytes (corrected from STACK.md documentation error of 32 bytes)
- Detection.xml with all 8 EncryptionInfo sub-elements and correct namespace
- Inner ZIP uses DEFLATE compression; outer ZIP uses STORED compression
- All 14 byte-level tests pass including crypto roundtrip verification
- Dockerfile with python:3.12-slim-bookworm, curl downloads Pico CSS, HTMX, Alpine.js at build time
- docker-compose.yml with imptune_data:/data named volume and restart policy
- requirements.txt with all phase 1-5 dependencies
- imptune/config.py loading DATA_DIR/PORT from env with DB_PATH and DRIVERS_DIR derived paths
- imptune/main.py: FastAPI app with StaticFiles mount, health+pages routers, startup dir creation
- imptune/api/health.py: GET /health returning {"status": "ok"}
- imptune/api/pages.py: GET / returning dashboard.html with sync def handler
- imptune/templates/base.html: data-theme="auto", sidebar with 5 nav sections, /static/ paths only
- imptune/templates/dashboard.html: quick actions + empty state recent activity
- imptune/static/app.css: sidebar layout, active link highlight, quick action buttons
- 14 tests covering outer ZIP structure, Detection.xml schema, key sizes
- Cryptographic roundtrip: HMAC-SHA256, AES-256-CBC decryption, file digest
- IV size assertion: must be 16 bytes (critical correction from RESEARCH.md)
- Tests serve as format specification for build_intunewin() implementation
Investigates Docker scaffold, Peewee schema patterns, and .intunewin
format for the foundation phase. Flags a critical IV size correction
(16 bytes, not 32 as stated in STACK.md) verified against svrooij.io.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds STACK, FEATURES, ARCHITECTURE, PITFALLS, and SUMMARY research files
covering the full ImpTune technology stack, feature set, architecture
patterns, and critical pitfalls for the printer deployment package generator.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>