--- 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*