---
phase: 01-foundation
plan: 02
type: execute
wave: 2
depends_on: ["01-01"]
files_modified:
  - imptune/db/__init__.py
  - imptune/db/database.py
  - imptune/db/models.py
  - imptune/storage/__init__.py
  - imptune/storage/driver_store.py
  - imptune/main.py
  - tests/test_db.py
autonomous: true
requirements:
  - INFRA-01
  - INFRA-02

must_haves:
  truths:
    - "SQLite database initializes automatically on first run with all tables (Client, Driver, Printer, Icon)"
    - "Database uses WAL journal mode and has foreign keys enabled"
    - "Database file is created inside the DATA_DIR volume path, not inside the container filesystem"
    - "Schema creation is idempotent — repeated startups do not fail or duplicate tables"
  artifacts:
    - path: "imptune/db/database.py"
      provides: "Peewee SqliteDatabase instance with WAL mode and init_db function"
      exports: ["db", "init_db"]
    - path: "imptune/db/models.py"
      provides: "All ORM models for phases 1-5 (BaseModel, Client, Driver, Printer, Icon)"
      exports: ["BaseModel", "Client", "Driver", "Printer", "Icon"]
    - path: "imptune/storage/driver_store.py"
      provides: "SHA256 content-addressed file storage abstraction for driver packages"
      exports: ["DriverStore"]
    - path: "tests/test_db.py"
      provides: "Database initialization and schema validation tests"
  key_links:
    - from: "imptune/main.py"
      to: "imptune/db/database.py"
      via: "startup event calling init_db()"
      pattern: "init_db"
    - from: "imptune/db/models.py"
      to: "imptune/db/database.py"
      via: "BaseModel.Meta.database = db"
      pattern: "database = db"
    - from: "imptune/db/database.py"
      to: "imptune/config.py"
      via: "DB_PATH from config"
      pattern: "DB_PATH|DATA_DIR"
---

<objective>
Create the full SQLite schema using Peewee ORM (all tables for phases 1-5) and the content-addressed driver storage abstraction. Wire database initialization into the FastAPI startup event.

Purpose: Establish the data layer that all subsequent phases depend on. The full schema is created upfront per the locked user decision, so later phases only add routes and logic — not schema changes. Satisfies INFRA-01 (SQLite auto-init) and INFRA-02 (no external DB).
Output: Working database module with all models, driver storage helper, and startup wiring.
</objective>

<execution_context>
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01-foundation/01-CONTEXT.md
@.planning/phases/01-foundation/01-RESEARCH.md

<interfaces>
<!-- From plan 01-01: key exports the executor needs -->

From imptune/config.py:
```python
DATA_DIR: str   # env var, default "/data"
DB_PATH: str    # DATA_DIR + "/imptune.db"
DRIVERS_DIR: str  # DATA_DIR + "/drivers"
```

From imptune/main.py:
```python
app = FastAPI(title="ImpTune")
# startup event already creates DATA_DIR/DRIVERS_DIR directories
# Executor must ADD init_db() call to the existing startup event
```
</interfaces>
</context>

<tasks>

<task type="auto" tdd="true">
  <name>Task 1: Create Peewee models, database init, and driver storage</name>
  <files>
    imptune/db/__init__.py,
    imptune/db/database.py,
    imptune/db/models.py,
    imptune/storage/__init__.py,
    imptune/storage/driver_store.py
  </files>
  <behavior>
    - test_create_tables: calling init_db() creates all 4 tables (client, driver, printer, icon) in a fresh SQLite file
    - test_wal_mode: after init_db(), PRAGMA journal_mode returns "wal"
    - test_foreign_keys: after init_db(), PRAGMA foreign_keys returns 1
    - test_idempotent: calling init_db() twice does not raise an error
    - test_driver_store_save: saving bytes returns their SHA256 hex digest and creates a file at DRIVERS_DIR/{sha256}
    - test_driver_store_dedup: saving the same bytes twice results in one file on disk (not two)
    - test_driver_store_get_path: get_path(sha256) returns the correct file path
  </behavior>
  <action>
    **imptune/db/database.py**:
    - Import SqliteDatabase from peewee, import DB_PATH from imptune.config
    - Create db = SqliteDatabase(None) (deferred init — path set at runtime so tests can override)
    - init_db() function: call db.init(DB_PATH, pragmas={"journal_mode": "wal", "foreign_keys": 1}), then db.connect(reuse_if_open=True), then import all models and call db.create_tables([Client, Driver, Printer, Icon], safe=True)
    - Use deferred database pattern so tests can point at a temp file

    **imptune/db/models.py** (full schema for all phases per locked decision):
    - BaseModel with Meta.database = db
    - Client: name (CharField unique), created_at (DateTimeField default utcnow)
    - Driver: sha256 (CharField unique, indexed), original_filename (CharField), size_bytes (IntegerField), uploaded_at (DateTimeField default utcnow), driver_desc (CharField null=True), inf_filename (CharField null=True), architecture (CharField null=True), has_cat_file (BooleanField default=False)
    - Printer: name (CharField), ip_address (CharField), port_name (CharField), client (ForeignKeyField Client null=True backref="printers"), driver (ForeignKeyField Driver null=True backref="printers"), duplex_mode (CharField default="OneSided"), color_mode (BooleanField default=True), paper_size (CharField default="A4"), collate (BooleanField default=True), created_at, updated_at (both DateTimeField default utcnow)
    - Icon: printer (ForeignKeyField Printer unique backref="icons"), sha256 (CharField), original_filename (CharField), size_bytes (IntegerField), uploaded_at (DateTimeField default utcnow)

    **imptune/storage/driver_store.py**:
    - Class DriverStore with __init__(self, base_dir: str)
    - save(self, data: bytes) -> str: compute SHA256, write to base_dir/{sha256} if not exists, return hex digest
    - get_path(self, sha256: str) -> Path: return Path(base_dir) / sha256
    - exists(self, sha256: str) -> bool: check if file exists
  </action>
  <verify>
    <automated>cd C:/Users/SebastienQUEROL/Documents/projets/ImpTune && python -m pytest tests/test_db.py -x -v</automated>
  </verify>
  <done>
    - All 7 tests pass
    - init_db() creates Client, Driver, Printer, Icon tables
    - WAL mode and foreign keys enabled
    - Idempotent — second call is no-op
    - DriverStore deduplicates by SHA256
  </done>
</task>

<task type="auto">
  <name>Task 2: Wire database init into FastAPI startup</name>
  <files>
    imptune/main.py
  </files>
  <action>
    Modify the existing imptune/main.py (created by plan 01-01) to add database initialization on startup:

    - Import init_db from imptune.db.database
    - In the existing startup event handler, add a call to init_db() AFTER the directory creation logic
    - This ensures the SQLite database is created inside DATA_DIR (which was just created/verified)
    - Keep all existing code (StaticFiles mount, router includes, directory creation) — only ADD the init_db() call

    Do NOT use async def for the startup handler — Peewee is sync-only. Use regular def with FastAPI's @app.on_event("startup") which already exists from plan 01-01.
  </action>
  <verify>
    <automated>cd C:/Users/SebastienQUEROL/Documents/projets/ImpTune && python -m pytest tests/test_health.py tests/test_db.py -x -v</automated>
  </verify>
  <done>
    - main.py imports and calls init_db() on startup
    - Existing health and static tests still pass (no regression)
    - Database tests pass with init triggered via app startup
  </done>
</task>

</tasks>

<verification>
- `python -m pytest tests/ -x -v` — all tests pass (health + static + db)
- `python -c "from imptune.db.models import Client, Driver, Printer, Icon; print('Models OK')"` — imports without error
- `python -c "from imptune.storage.driver_store import DriverStore; print('DriverStore OK')"` — imports without error
</verification>

<success_criteria>
- SQLite database auto-creates on app startup with 4 tables
- WAL journal mode and foreign keys enabled via pragmas
- Database file lives at DATA_DIR/imptune.db (volume-mounted path)
- DriverStore saves files by SHA256 with deduplication
- All existing tests continue to pass (no regression)
- 7+ new tests pass for db and storage
</success_criteria>

<output>
After completion, create `.planning/phases/01-foundation/01-02-SUMMARY.md`
</output>
