Imported from Ashish54/model_documentation_llm (
.github/skills/db-migration-safety/SKILL.md). Install upstream withnpx skills add Ashish54/model_documentation_llm --skill db-migration-safety. Copyright stays with the author.
Database Migration Safety
The data model is append-only by contract (ADR 0001). A migration that rewrites or drops historical evidence is a governance incident, not a bug.
Rules
- Additive only. New tables and nullable columns are fine. Never drop or
reshape
artifact_version,source_locator,extraction_run,llm_interaction,*_evidence, or any existing version row. - Portable DDL. Use
JSONBCompat(not raw JSON/JSONB), VARCHAR enums (native_enum=False), and the naming convention already onBase.metadata. - Break FK cycles with
ForeignKey(..., use_alter=True, name=...)— PostgreSQL rejects mutually-dependent CREATE TABLEs (seeSchemaVersion.proposal_run_id).
Steps
- Edit the ORM in
src/modelkb/db/models/following the existing versioning patterns (identity+version tables, or append-onlyref+is_currentrows). - Autogenerate:
.venv/bin/alembic -x dburl=sqlite:///var/_scratch.db revision --autogenerate -m "<change>". Done when the revision file exists. - Read the generated migration line by line — autogenerate misses intent.
Verify: no drops of historical tables;
modelkb.db.typesis imported (the mako template adds it — keep it); indexes match the query patterns. - Round-trip:
upgrade head→downgrade -1→upgrade headon scratch SQLite. Done when all three succeed. - Extend
tests/integration/test_migration.pyfor the new structure. Done when pytest, ruff, and mypy all pass.
Gotchas
- UUID primary keys need
uuid.UUID(...)objects on SQLite (session.getwith a raw string raises). - Re-ingestion must stay idempotent: changing what feeds a deterministic ID
(
core/ids.py) orphans existing evidence rows — treat those inputs as frozen.