diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index f9d5852..c219319 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -12,7 +12,7 @@ ImpTune ships in five phases ordered by risk and dependency. The foundation phas Decimal phases appear between their surrounding integers in numeric order. -- [ ] **Phase 1: Foundation** - Docker container scaffold, SQLite schema, and .intunewin format spike +- [x] **Phase 1: Foundation** - Docker container scaffold, SQLite schema, and .intunewin format spike (completed 2026-04-10) - [ ] **Phase 2: Driver Management** - Driver ZIP upload, INF parsing, DriverDesc dropdown, volume persistence - [ ] **Phase 3: Printer Configuration** - Full printer config form, client/tenant labels, SQLite persistence - [ ] **Phase 4: Script Generation** - PowerShell install/uninstall/detection scripts with all correctness guards @@ -108,7 +108,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 | Phase | Plans Complete | Status | Completed | |-------|----------------|--------|-----------| -| 1. Foundation | 2/3 | In Progress| | +| 1. Foundation | 3/3 | Complete | 2026-04-10 | | 2. Driver Management | 0/3 | Not started | - | | 3. Printer Configuration | 0/3 | Not started | - | | 4. Script Generation | 0/3 | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index 23569bb..c55e376 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,14 +3,14 @@ gsd_state_version: 1.0 milestone: v1.0 milestone_name: milestone status: executing -stopped_at: Completed 01-01-PLAN.md -last_updated: "2026-04-10T09:27:00.000Z" -last_activity: 2026-04-10 — Plan 01-01 complete: Docker scaffold, FastAPI app shell, test suite +stopped_at: Completed 01-02-PLAN.md +last_updated: "2026-04-10T09:33:07.640Z" +last_activity: "2026-04-10 — Plan 01-01 complete: Docker scaffold, FastAPI app shell, sidebar templates, 3-test green suite" progress: total_phases: 5 - completed_phases: 0 + completed_phases: 1 total_plans: 3 - completed_plans: 2 + completed_plans: 3 percent: 7 --- @@ -50,6 +50,7 @@ Progress: [░░░░░░░░░░] 7% - Trend: Consistent *Updated after each plan completion* +| Phase 01-foundation P01-02 | 3 | 2 tasks | 7 files | ## Accumulated Context @@ -64,6 +65,9 @@ Recent decisions affecting current work: - [Phase 01]: Inner .intunewin ZIP uses DEFLATE compression; outer ZIP uses STORED (matches C# reference implementation) - [Plan 01-01]: Use asynccontextmanager lifespan instead of deprecated @app.on_event — required for FastAPI 0.115+ / Starlette 0.40+ - [Plan 01-01]: TemplateResponse uses request= kwarg signature (not positional dict) — Starlette 0.40+ compatibility +- [Phase 01-foundation]: Deferred SqliteDatabase(None) pattern so tests can patch DB_PATH via monkeypatch without module reload +- [Phase 01-foundation]: Full 4-table schema created upfront in phase 1 — later phases add routes/logic only, no schema changes +- [Phase 01-foundation]: init_db() added to lifespan (not @app.on_event) consistent with 01-01 established pattern ### Pending Todos @@ -76,6 +80,6 @@ None yet. ## Session Continuity -Last session: 2026-04-10T09:27:00Z -Stopped at: Completed 01-01-PLAN.md -Resume file: .planning/phases/01-foundation/01-02-PLAN.md +Last session: 2026-04-10T09:33:07.637Z +Stopped at: Completed 01-02-PLAN.md +Resume file: None diff --git a/.planning/phases/01-foundation/01-02-SUMMARY.md b/.planning/phases/01-foundation/01-02-SUMMARY.md new file mode 100644 index 0000000..b96ed14 --- /dev/null +++ b/.planning/phases/01-foundation/01-02-SUMMARY.md @@ -0,0 +1,139 @@ +--- +phase: 01-foundation +plan: "02" +subsystem: database +tags: [peewee, sqlite, wal, orm, content-addressed-storage, sha256] + +# Dependency graph +requires: + - phase: 01-01 + provides: "FastAPI app shell with lifespan, config.py with DATA_DIR/DB_PATH/DRIVERS_DIR" +provides: + - "Peewee SqliteDatabase instance with WAL mode + foreign_keys pragma (imptune/db/database.py)" + - "Full ORM schema: Client, Driver, Printer, Icon models for phases 1-5 (imptune/db/models.py)" + - "SHA256 content-addressed DriverStore with deduplication (imptune/storage/driver_store.py)" + - "Auto-initializing database via FastAPI lifespan startup" +affects: + - phase-02-clients + - phase-03-drivers + - phase-04-printers + - phase-05-export + +# Tech tracking +tech-stack: + added: [peewee==3.17.9] + patterns: + - "Deferred SqliteDatabase init (db.init() at runtime so tests can override DB_PATH)" + - "safe=True on create_tables() for idempotent schema creation" + - "SHA256 content-addressed file storage for deduplication" + +key-files: + created: + - imptune/db/__init__.py + - imptune/db/database.py + - imptune/db/models.py + - imptune/storage/__init__.py + - imptune/storage/driver_store.py + - tests/test_db.py + modified: + - imptune/main.py + +key-decisions: + - "Deferred SqliteDatabase pattern (SqliteDatabase(None)) so tests can patch imptune.config.DB_PATH without module reload" + - "Full schema created upfront in phase 1 per locked user decision — later phases only add routes/logic, no schema changes" + - "init_db() placed in lifespan (not @app.on_event) consistent with 01-01 decision — plan text was outdated" + +patterns-established: + - "TDD: RED (failing tests) then GREEN (implementation) for all db/storage modules" + - "ORM: All models extend BaseModel which references shared db instance via Meta.database = db" + - "Storage: DriverStore encapsulates all filesystem operations for driver packages" + +requirements-completed: [INFRA-01, INFRA-02] + +# Metrics +duration: 3min +completed: 2026-04-10 +--- + +# Phase 1 Plan 2: Database Schema and Driver Storage Summary + +**Peewee ORM with deferred SQLiteDatabase, full 4-table schema (Client/Driver/Printer/Icon) for all phases, and SHA256-deduplicating DriverStore — wired into FastAPI lifespan** + +## Performance + +- **Duration:** ~3 min +- **Started:** 2026-04-10T09:29:54Z +- **Completed:** 2026-04-10T09:32:07Z +- **Tasks:** 2 (Task 1 with TDD + Task 2) +- **Files modified:** 7 + +## Accomplishments + +- Full Peewee ORM schema with 4 tables covering all phases 1-5 (locked-in upfront design decision) +- WAL journal mode and foreign_keys pragma enforced via init_db() on every startup +- Deferred database pattern allows tests to safely redirect DB_PATH to tmp dirs without module reloads +- DriverStore provides SHA256 content-addressed storage with automatic deduplication on write +- init_db() integrated into FastAPI lifespan — database auto-creates at DATA_DIR/imptune.db on startup + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Peewee models, database init, and driver storage** - `dea4148` (feat — TDD GREEN) +2. **Task 2: Wire database init into FastAPI startup** - `88d9c5f` (feat) + +**Plan metadata:** (docs commit follows) + +_Note: TDD — tests written first (RED), then implementation (GREEN). No separate refactor pass needed._ + +## Files Created/Modified + +- `imptune/db/__init__.py` - Package marker +- `imptune/db/database.py` - Deferred SqliteDatabase instance + init_db() with WAL/FK pragmas +- `imptune/db/models.py` - BaseModel, Client, Driver, Printer, Icon ORM models +- `imptune/storage/__init__.py` - Package marker +- `imptune/storage/driver_store.py` - SHA256 content-addressed DriverStore class +- `tests/test_db.py` - 7 TDD tests (table creation, WAL, FK, idempotency, save, dedup, get_path) +- `imptune/main.py` - Added import and call to init_db() in lifespan + +## Decisions Made + +- **Deferred database pattern**: Used `SqliteDatabase(None)` + `db.init()` at runtime so pytest's `monkeypatch` on `imptune.config.DB_PATH` works without module reload side effects. +- **Lifespan over @app.on_event**: Plan text referenced `@app.on_event("startup")` but 01-01 established the lifespan pattern. Followed existing code — no deviation registered as this was alignment with an existing decision. +- **Full schema upfront**: All 4 tables created in phase 1 per user's locked decision, so phases 2-5 only add application logic. + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 3 - Blocking] Installed missing peewee package** +- **Found during:** Task 1 setup +- **Issue:** peewee was in requirements.txt but not installed in the active Python environment +- **Fix:** Ran `python -m pip install peewee==3.17.*` +- **Files modified:** None (environment only) +- **Verification:** `import peewee` succeeds, all 7 tests pass +- **Committed in:** Not committed (environment dependency install) + +--- + +**Total deviations:** 1 auto-fixed (1 blocking — missing dependency) +**Impact on plan:** No scope creep. peewee install was a prerequisite, not new scope. + +## Issues Encountered + +- Plan Task 2 referenced `@app.on_event("startup")` but the existing `main.py` from plan 01-01 already uses `asynccontextmanager lifespan` (per a decision recorded in STATE.md). Added `init_db()` to the lifespan function instead — no regression. + +## User Setup Required + +None - no external service configuration required. + +## Next Phase Readiness + +- Database layer complete — all ORM models importable and tested +- `init_db()` wired in — first app startup creates the database automatically in DATA_DIR +- DriverStore ready for driver upload routes (phase 3) +- All 24 tests pass (7 new + 17 existing), zero regressions + +--- +*Phase: 01-foundation* +*Completed: 2026-04-10*