diff --git a/agent-team/agent_team/db/schema.py b/agent-team/agent_team/db/schema.py index d354753..a569a55 100644 --- a/agent-team/agent_team/db/schema.py +++ b/agent-team/agent_team/db/schema.py @@ -274,15 +274,14 @@ def init_db(db_path: Path) -> None: conn.execute(TASK_TRANSITIONS_DDL) for stmt in _split_statements(TASK_TRANSITIONS_INDEXES_DDL): conn.execute(stmt) - # Record the schema version (single-row table). DO NOTHING leaves an - # existing row's version untouched (an already-stamped DB just gains any - # IF-NOT-EXISTS tables above); migrate() is what steps the version stamp - # forward on an existing DB. - conn.execute( - "INSERT INTO schema_meta (id, schema_version) VALUES (1, ?) " - "ON CONFLICT(id) DO NOTHING", - (SCHEMA_VERSION,), - ) + # Step the version stamp forward AND apply any version-gated migrations. + # init_db is the only schema entry point the daemon calls (Coordinator. + # setup -> init_db), so it MUST drive migrate() — otherwise an existing + # DB's schema_version is never advanced (migrate() upserts it; init_db's + # own writes do not) and version-gated steps in migrate() never run in + # production. migrate() is idempotent, so re-running the create/ensure + # statements above is harmless. + migrate(conn) finally: conn.close() diff --git a/agent-team/tests/test_schema.py b/agent-team/tests/test_schema.py index 3904b68..2bcdb2a 100644 --- a/agent-team/tests/test_schema.py +++ b/agent-team/tests/test_schema.py @@ -696,6 +696,39 @@ def test_migrate_helper_adds_kind_to_legacy_db(tmp_path: Path) -> None: assert ver == SCHEMA_VERSION +def test_init_db_advances_existing_version_stamp(tmp_path: Path) -> None: + """init_db (the daemon's only schema entry point) bumps a stale version stamp. + + Regression: init_db's own schema_meta write was ON CONFLICT DO NOTHING, so an + already-stamped DB (e.g. an old v3 ledger) kept its stale version forever — + the daemon calls init_db, never migrate(), so the stamp never advanced even + though the column was ensured. init_db now drives migrate(), which upserts. + """ + db = tmp_path / "stale.sqlite" + conn = connect(db) + try: + conn.execute(_LEGACY_PENDING_QUESTIONS_DDL) + conn.execute( + "CREATE TABLE IF NOT EXISTS schema_meta " + "(id INTEGER PRIMARY KEY CHECK (id = 1), schema_version INTEGER NOT NULL)" + ) + conn.execute("INSERT INTO schema_meta (id, schema_version) VALUES (1, 3)") + finally: + conn.close() + + init_db(db) + + conn = connect(db) + try: + ver = conn.execute( + "SELECT schema_version FROM schema_meta WHERE id = 1" + ).fetchone()[0] + assert "kind" in _pq_columns(conn) + finally: + conn.close() + assert ver == SCHEMA_VERSION + + def test_kind_plan_decision_round_trips(tmp_path: Path) -> None: """A row written with kind='plan_decision' round-trips; default is 'clarify'.""" db = tmp_path / "db.sqlite"