feat(engineering): enforce V1-0 write invariants
This commit is contained in:
@@ -146,6 +146,28 @@ def _apply_migrations(conn: sqlite3.Connection) -> None:
|
||||
"CREATE INDEX IF NOT EXISTS idx_memories_graduated ON memories(graduated_to_entity_id)"
|
||||
)
|
||||
|
||||
# V1-0 (Engineering V1 completion): shared header fields per
|
||||
# engineering-v1-acceptance.md:45. Three columns on `entities`:
|
||||
# - extractor_version: which extractor produced this row. Lets old
|
||||
# candidates be re-evaluated with a newer extractor per
|
||||
# promotion-rules.md:268.
|
||||
# - canonical_home: which layer holds the canonical record. Always
|
||||
# "entity" for rows written via create_entity; reserved for future
|
||||
# cross-layer bookkeeping.
|
||||
# - hand_authored: 1 when the row was created directly by a human
|
||||
# without source provenance. Enforced by the write path so every
|
||||
# non-hand-authored row must carry non-empty source_refs (F-8).
|
||||
# The entities table itself is created by init_engineering_schema
|
||||
# (see engineering/service.py); these ALTERs cover existing DBs
|
||||
# where the original CREATE TABLE predates V1-0.
|
||||
if _table_exists(conn, "entities"):
|
||||
if not _column_exists(conn, "entities", "extractor_version"):
|
||||
conn.execute("ALTER TABLE entities ADD COLUMN extractor_version TEXT DEFAULT ''")
|
||||
if not _column_exists(conn, "entities", "canonical_home"):
|
||||
conn.execute("ALTER TABLE entities ADD COLUMN canonical_home TEXT DEFAULT 'entity'")
|
||||
if not _column_exists(conn, "entities", "hand_authored"):
|
||||
conn.execute("ALTER TABLE entities ADD COLUMN hand_authored INTEGER DEFAULT 0")
|
||||
|
||||
# Phase 4 (Robustness V1): append-only audit log for memory mutations.
|
||||
# Every create/update/promote/reject/supersede/invalidate/reinforce/expire/
|
||||
# auto_promote writes one row here. before/after are JSON snapshots of the
|
||||
@@ -352,6 +374,14 @@ def _column_exists(conn: sqlite3.Connection, table: str, column: str) -> bool:
|
||||
return any(row["name"] == column for row in rows)
|
||||
|
||||
|
||||
def _table_exists(conn: sqlite3.Connection, table: str) -> bool:
|
||||
row = conn.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name=?",
|
||||
(table,),
|
||||
).fetchone()
|
||||
return row is not None
|
||||
|
||||
|
||||
@contextmanager
|
||||
def get_connection() -> Generator[sqlite3.Connection, None, None]:
|
||||
"""Get a database connection with row factory."""
|
||||
|
||||
Reference in New Issue
Block a user