docs(01): create phase 1 foundation plans
Three plans covering Docker scaffold, SQLite schema, and .intunewin spike. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
---
|
||||
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>
|
||||
Reference in New Issue
Block a user