Archive v1.0 MVP: 7 phases, 13 plans, 27/27 requirements. - Archive roadmap to milestones/v1.0-ROADMAP.md - Archive requirements to milestones/v1.0-REQUIREMENTS.md - Move milestone audit into milestones/ - Create MILESTONES.md with v1.0 entry - Evolve PROJECT.md: move shipped requirements to Validated, update Context with stack/LOC, log Key Decisions with outcomes - Collapse ROADMAP.md to one-line milestone summary - Update STATE.md to shipped status - Back-fill stale requirements-completed frontmatter on 02-01, 04-01, 05-01, 06-01 SUMMARY.md files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8.1 KiB
Milestone v1.0: ImpTune MVP
Status: ✅ SHIPPED 2026-04-13 Phases: 1-7 Total Plans: 13 Timeline: 2026-04-10 → 2026-04-13 (4 days)
Overview
Initial release of ImpTune — a self-hosted single-container webapp that lets IT technicians configure printer deployments and export ready-to-deploy packages for Microsoft Intune (.intunewin) or NinjaRMM (ZIP). Ships driver ZIP upload with INF parsing, full printer configuration with client/tenant grouping, PowerShell script generation (install/uninstall/detect) with UAC elevation and WOW64 guards, Python-native .intunewin assembly with embedded icon, and NinjaRMM ZIP export — all behind a no-auth HTMX/Alpine.js browser UI.
Phases
Phase 1: Foundation
Goal: A running Docker container with the app scaffold, data schema, and validated .intunewin generation capability Depends on: Nothing Requirements: INFRA-01, INFRA-02 Plans: 3 plans
Plans:
- 01-01: Docker container scaffold (python:3.12-slim-bookworm, FastAPI, Jinja2, HTMX, Alpine.js, Pico CSS, offline static baking, healthcheck, sidebar nav shell)
- 01-02: SQLite schema — Peewee WAL mode, full 4-table ORM (Client/Driver/Printer/Icon) created upfront, SHA256 content-addressed DriverStore, auto-init via lifespan
- 01-03: Python-native .intunewin format spike —
build_intunewin()with AES-256-CBC, HMAC-SHA256, detection.xml, 14 byte-level validation tests
Phase 2: Driver Management
Goal: Technicians upload driver packages and select driver names from parsed INF data — no free-text entry Depends on: Phase 1 Requirements: DRV-01, DRV-02, DRV-03, DRV-04, DRV-05 Plans: 2 plans
Plans:
- 02-01: INF parser service — TDD, RawConfigParser(strict=False), BOM/UTF-16 detection, %TOKEN% resolution, multi-model support, unused-files detection
- 02-02: Driver upload endpoint + drivers page — POST /drivers/upload with ZIP validation, SHA256 dedup, Peewee persistence, HTMX partial refresh, 8 integration tests
Phase 3: Printer Configuration
Goal: Technicians configure all printer parameters, assign printers to clients, and regenerate saved configs without re-uploading drivers Depends on: Phase 2 Requirements: PRNT-01 through PRNT-10 Plans: 2 plans
Plans:
- 03-01: Printer + Client CRUD — form with all fields, Alpine.js IP→port auto-derivation (preserves manual edits), grouped list with LEFT OUTER JOIN, HTMX outerHTML swap, integration tests covering PRNT-01..09
- 03-02: Printer detail page — full-page template with all config fields, driver association, regenerate placeholder, clickable links in printer list
Phase 4: Script Generation
Goal: System produces correct, production-ready PowerShell scripts handling all Intune and RMM execution contexts Depends on: Phase 3 Requirements: SCRPT-01, SCRPT-02, SCRPT-03, SCRPT-04, SCRPT-05 Plans: 2 plans
Plans:
- 04-01:
render_install()with Jinja2 template — WOW64 64-bit relaunch guard, UAC self-elevation, pnputil two-step staging, duplex mapping, idempotency, plain-string args for DB-free unit testability - 04-02:
render_uninstall()+render_detect()templates, 3 script download endpoints (/install, /uninstall, /detect),_get_printer_and_driver()shared helper, PlainTextResponse with Content-Disposition
Phase 5: Package Export
Goal: Technicians download a complete, ready-to-deploy package for either Intune or NinjaRMM in one click Depends on: Phase 4 Requirements: PKG-01, PKG-02, PKG-03, PKG-04, PKG-05 Plans: 2 plans
Plans:
- 05-01:
/printers/{id}/packages/ninja+/packages/intunewinendpoints — in-memory ZIP assembly with BytesIO, TemporaryDirectory staging for intunewin, driver ZIP existence validation - 05-02: Icon upload with Pillow validation (PNG 256x256 ≤750KB), SHA256-addressed icon storage, printer detail page with Intune Commands section (copy buttons), Export section, Icon Upload form
Phase 6: Wire Icon into .intunewin Export (gap closure)
Goal: Uploaded PNG icon is embedded in the .intunewin package so Intune displays it as the app icon Depends on: Phase 5 Requirements: PKG-04 (closes gap from first audit) Plans: 1 plan
Plans:
- 06-01: Wire
Icon.get_or_none()lookup intopackages.py,shutil.copy2()icon to tmpdir asicon.png, silent-skip on missing record/file, integration test verifying icon presence in exported package
Details: Added as gap-closure phase after first milestone audit flagged PKG-04 as unsatisfied — icon was uploaded and stored but never embedded in the .intunewin output.
Phase 7: Dashboard & Navigation Polish (gap closure)
Goal: Navigation links work correctly and dashboard shows real data instead of empty placeholders Depends on: Phase 3 Requirements: None (UX/integration fixes) Plans: 1 plan
Plans:
- 07-01: Add
GET /packagesroute (LEFT OUTER join on Client + Driver,switch(Printer)), wire dashboardrecent_printers/recent_packagesto live DB queries, newpackages.htmltemplate, clickable nav links
Details: Added as gap-closure phase after first milestone audit flagged base.html → /packages 404 (route missing) and hardcoded [] in dashboard queries.
Milestone Summary
Key Decisions:
- Python-native .intunewin — IntuneWinAppUtil.exe is a Windows PE binary, cannot run in Linux container. Reimplemented the AES-256-CBC / HMAC-SHA256 format in Python with pycryptodome, validated byte-level against the C# reference. ✓ Good
- Stack: Python 3.12 + FastAPI + Jinja2 + HTMX + Alpine.js + SQLite + Peewee + pycryptodome + Pillow. ✓ Good — minimal runtime, single container, no Node.js
- Full 4-table schema upfront (Phase 1) — Client/Driver/Printer/Icon all created in 01-02 so later phases add routes only, no schema migrations. ✓ Good
- Sync FastAPI routes — runs in thread pool, Peewee-compatible without async ORM complexity. ✓ Good
- Content-addressed storage — SHA256 for drivers and icons, deduplication for free, consistent pattern. ✓ Good
- Plain-string args for script generators (not ORM objects) — keeps unit tests DB-free. ✓ Good
- Test isolation — TestClient used as context manager for Starlette 0.46+ lifespan; thread-local Peewee connections closed in conftest teardown;
list(Model.select())wrapper avoids cursor caching across DB re-inits. ✓ Good - Silent-skip on missing icon — export always succeeds regardless of icon presence, optional feature. ✓ Good
- Gap-closure phases 6 & 7 — added post-audit rather than shipping with known defects; cleaner than carrying PKG-04 and /packages 404 as tech debt into v1.1. ✓ Good
Issues Resolved:
- Peewee
datetime.utcnow()deprecation warning (root cause was project-level_utcnow()usage, not library) - DriverStore path mismatch bug —
.zipsuffix inconsistency between save and lookup broke upload→export flow in production (masked by pre-staged test fixtures); centralized inDriverStore.get_path()and addedtests/test_upload_export_roundtrip.pyregression test - Uninstall copy button mislabel ("Uninstall copy" → "Copy")
- Stale
requirements-completedfrontmatter in 5 SUMMARY.md files (back-filled) - PKG-04 icon→.intunewin wiring break (Phase 6)
/packages404 + dashboard hardcoded[](Phase 7)
Issues Deferred to v1.1 (Tech Debt):
- Printer form driver dropdown requires manual page reload after uploading a new driver on /drivers (Phase 2)
- PRNT-03 Alpine.js port auto-derivation — code correct, needs live browser verification (Phase 3)
- No UI links to individual script downloads — only accessible via package export or direct URL (Phase 5)
Nyquist Validation: All 7 phases have draft VALIDATION.md files but none are Nyquist-compliant. Wave 0 not complete. Not a milestone blocker — separate validation track for v1.1.
Known Runtime Validations Pending:
.intunewinbyte-level format must be validated against a real Intune tenantpnputil+$PSScriptRootpath resolution under SYSTEM context on a real Intune-managed device
For current project status, see .planning/ROADMAP.md