Skip to content

Persist the DHF store in SQLite behind a TraceabilityStore interface - #1

Merged
LSaiko merged 1 commit into
mainfrom
sqlite-store
Sep 23, 2026
Merged

LSaiko merged 1 commit into
mainfrom
sqlite-store

Conversation

@LSaiko

@LSaiko LSaiko commented Sep 23, 2026

Copy link
Copy Markdown
Owner

Replaces the in-memory projects dict with a persistent store. The API, the Pydantic schemas and the Archivist's behaviour are unchanged.

Changes

  • app/store.py defines the TraceabilityStore protocol (get/put) and two implementations:
    • InMemoryStore
    • SqliteStore, which uses stdlib sqlite3 and keeps one table per stored record type, with columns that mirror the Pydantic fields.
  • Setup: an idempotent CREATE TABLE IF NOT EXISTS runs on startup, or standalone with python -m app.store.
  • DB file: DHF_DB_PATH, default data/dhf.db, which is gitignored.
  • app/main.py: each endpoint gets the store through Depends(get_store). The server opens SqliteStore at startup, so there is no global.
  • Tests: plain pytest runs the fast suite against InMemoryStore. pytest -m integration covers:
    • a round trip of every record type across a store restart, compared byte-for-byte
    • the API end to end across an app restart
  • CI: runs both suites and fails if combined coverage drops below 100%, so the badge stays accurate.
  • Docs: the README and docs/architecture.md describe the persistence model and its limits.

Not changed

  • Trace links and gaps are still recomputed by build_matrix on every read and are not stored, so the gap report can't go stale.
  • The GitHub Pages demo is a static seed build with no backend, so it is unaffected.

Known limits (also in the README)

  • No locking for concurrent writes. When two writes hit the same project at once, the last one wins, and two simultaneous evidence uploads can both be numbered VAL-n. Only single-writer use has been tested.
  • No revision history or audit trail. A write replaces the project's current records, so this is not yet a configuration-managed record under 21 CFR 820.30(j) / IEC 62304 §8.
  • No schema changes after creation. If a model gains a field, an existing DB needs a manual ALTER TABLE or a fresh file.

Verification

  • CI is green on the branch (Python 3.12 and 3.13, dashboard).
  • Combined coverage is 100%.
  • Manual check: stopped and restarted uvicorn, and the data was still there.

🤖 Generated with Claude Code

- app/store.py: TraceabilityStore protocol, InMemoryStore, SqliteStore
  (one table per stored record type, columns mirror the Pydantic fields,
  idempotent CREATE TABLE IF NOT EXISTS migration, DHF_DB_PATH env var)
- app/main.py: store injected via Depends; SQLite opened at startup
- tests: unit suite injects InMemoryStore; `pytest -m integration` covers
  the SQLite round trip and API restart end to end
- CI runs both suites and enforces 100% combined coverage
- README / architecture: persistence model and its limits (no concurrent-
  write locking, no revision history, no schema evolution)

Trace links and gaps stay derived by build_matrix, not stored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@LSaiko
LSaiko merged commit a9e6f89 into main Sep 23, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant