Files
ImpTune/.planning/phases/01-foundation/01-01-SUMMARY.md
T
kawa f614a3e480 docs(01-01): complete Docker scaffold and app shell plan
- 01-01-SUMMARY.md: FastAPI app shell, offline static assets, sidebar, 3-test suite
- STATE.md: advance plan to 01-01 complete, add TemplateResponse and lifespan decisions
- ROADMAP.md: mark 01-01 complete, update progress table (2/3 plans in Phase 1)
- REQUIREMENTS.md: mark INFRA-01 complete
2026-04-10 11:28:49 +02:00

7.0 KiB


phase: 01-foundation plan: 01 subsystem: infra tags: [docker, fastapi, jinja2, htmx, pico-css, alpine-js, pytest, uvicorn]

Dependency graph

requires: [] provides:

  • Running FastAPI app with GET /health and dashboard page
  • Docker scaffold with offline static asset baking (Pico CSS, HTMX, Alpine.js)
  • Sidebar navigation shell with 5 sections (Dashboard, Drivers, Printers, Clients, Packages)
  • Test scaffold with health and no-CDN-URL tests passing affects: [01-02, 01-03, 02-drivers, 03-printers, 04-clients, 05-packages]

Tech tracking

tech-stack: added: [fastapi==0.115.x, uvicorn[standard]==0.30.x, jinja2==3.1.x, python-multipart==0.0.9, pycryptodome==3.20.x, python-dotenv==1.0.x, peewee==3.17.x, pytest, httpx] patterns: - Sync def route handlers (FastAPI runs in thread pool — Peewee-compatible) - StaticFiles mount from pathlib.Path(file).parent / "static" - asynccontextmanager lifespan for startup hooks (not deprecated on_event) - TemplateResponse with request= kwarg for Starlette 0.40+ compatibility - Docker offline asset baking — curl in RUN layer, assets in /app/imptune/static/

key-files: created: - Dockerfile - docker-compose.yml - requirements.txt - requirements-dev.txt - imptune/__init__.py - imptune/main.py - imptune/config.py - imptune/api/__init__.py - imptune/api/health.py - imptune/api/pages.py - imptune/templates/base.html - imptune/templates/dashboard.html - imptune/static/app.css - tests/__init__.py - tests/conftest.py - tests/test_health.py - tests/test_static.py modified: []

key-decisions:

  • "Use asynccontextmanager lifespan instead of deprecated @app.on_event (FastAPI/Starlette best practice)"
  • "TemplateResponse uses request= keyword arg (not positional context dict) for Starlette 0.40+ compatibility"
  • "Static dir resolved via pathlib.Path(file).parent / static — works inside Docker and local dev"
  • "Sync def route handlers throughout — FastAPI auto-threads, compatible with Peewee ORM"

patterns-established:

  • "Pattern 1: All static asset references use /static/ paths — no CDN URLs anywhere in templates"
  • "Pattern 2: TemplateResponse(request=request, name=..., context={...}) — Starlette 0.40+ signature"
  • "Pattern 3: config.py loads from env with sensible defaults; all paths derived from DATA_DIR"
  • "Pattern 4: TestClient fixture in conftest.py with monkeypatched tmp_data_dir for isolation"

requirements-completed: [INFRA-01, INFRA-02]

Metrics

duration: 3min completed: 2026-04-10

Phase 1, Plan 01: Docker Scaffold and App Shell Summary

FastAPI app with Pico CSS sidebar shell, offline-baked static assets (HTMX, Alpine.js), GET /health, and 3-test green suite — all in a single python:3.12-slim-bookworm container

Performance

  • Duration: 3 min
  • Started: 2026-04-10T09:23:15Z
  • Completed: 2026-04-10T09:26:30Z
  • Tasks: 2
  • Files modified: 17

Accomplishments

  • Docker scaffold with python:3.12-slim-bookworm base; curl downloads Pico CSS v2, HTMX 2.x, Alpine.js 3.x at build time and purges curl — zero CDN at runtime
  • FastAPI app with asynccontextmanager lifespan, StaticFiles mount, health router, and dashboard page router
  • Sidebar layout template (base.html) with data-theme="auto" for OS dark/light preference and 5 flat equal-weight nav sections
  • Test suite: 3 passing tests covering health endpoint, no-CDN-URLs scan, and dashboard 200 response

Task Commits

  1. Task 1: Docker scaffold, FastAPI app shell, templates - bd4e132 (feat)
  2. Task 2: Test scaffold, health and static tests - 34c7cb3 (feat)

Files Created/Modified

  • Dockerfile — python:3.12-slim-bookworm, curl-baked static assets, stdlib healthcheck, uvicorn CMD
  • docker-compose.yml — imptune_data:/data volume, DATA_DIR env, restart unless-stopped
  • requirements.txt — all phase 1-5 deps (fastapi, uvicorn, jinja2, peewee, pycryptodome, etc.)
  • requirements-dev.txt — pytest, httpx
  • imptune/main.py — FastAPI app with lifespan, StaticFiles, router registration
  • imptune/config.py — DATA_DIR/PORT env loading, DB_PATH/DRIVERS_DIR derivation
  • imptune/api/health.py — GET /health → {"status": "ok"}
  • imptune/api/pages.py — GET / → dashboard.html (sync def, new TemplateResponse signature)
  • imptune/templates/base.html — data-theme="auto", /static/ assets only, sidebar nav
  • imptune/templates/dashboard.html — quick actions + empty state recent activity
  • imptune/static/app.css — sidebar flex layout, active link highlight, quick action styling
  • tests/conftest.py — client and tmp_data_dir fixtures
  • tests/test_health.py — health endpoint 200 test
  • tests/test_static.py — no-CDN-URL scan + dashboard 200 test

Decisions Made

  • Used asynccontextmanager lifespan instead of deprecated @app.on_event("startup") — avoids DeprecationWarning on FastAPI 0.115+ / Python 3.13
  • Used TemplateResponse(request=request, name=..., context={...}) signature — the old positional dict form triggers a TypeError: unhashable type: 'dict' on Starlette 0.40+ due to LRUCache key behavior
  • Static directory resolved from pathlib.Path(__file__).parent / "static" — works in Docker and local dev without hardcoded paths

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] Fixed Starlette TemplateResponse signature incompatibility

  • Found during: Task 2 (test_dashboard_returns_200 failed)
  • Issue: templates.TemplateResponse("dashboard.html", {"request": request, ...}) raises TypeError: unhashable type: 'dict' on Starlette 0.40+ — context dict used as LRUCache key
  • Fix: Changed to templates.TemplateResponse(request=request, name="dashboard.html", context={...})
  • Files modified: imptune/api/pages.py
  • Verification: test_dashboard_returns_200 passes
  • Committed in: 34c7cb3 (Task 2 commit)

2. [Rule 1 - Bug] Replaced deprecated on_event with asynccontextmanager lifespan

  • Found during: Task 2 (DeprecationWarning on test run)
  • Issue: @app.on_event("startup") is deprecated in FastAPI 0.95+ / Starlette 0.37+; triggers warning on every test run
  • Fix: Replaced with @asynccontextmanager async def lifespan(app) passed to FastAPI(lifespan=lifespan)
  • Files modified: imptune/main.py
  • Verification: Tests pass with zero warnings
  • Committed in: 34c7cb3 (Task 2 commit)

Total deviations: 2 auto-fixed (both Rule 1 - Bug) Impact on plan: Both fixes required for compatibility with installed library versions. No scope creep.

Issues Encountered

  • Starlette's TemplateResponse API changed in 0.40.0 — old positional-dict form breaks silently until test run. Fixed inline.

Next Phase Readiness

  • App shell and health endpoint ready — next plan (01-02) can build the SQLite schema and Peewee models on this foundation
  • Docker image can be built once assets are downloaded; local dev works without Docker via python3 -m pytest and direct uvicorn run
  • No blockers for 01-02

Phase: 01-foundation Completed: 2026-04-10