8Runtime migration for roadmap source fields v0.3.2.
12Apply the additive schema changes that V0.22 §0 [OP-1] confirmed for the
13roadmap source-of-truth cutover (Plan 142): ``purpose`` columns on
14``roadmap_phases`` and ``roadmap_items_v3``; ``successor_item_id`` on
15``roadmap_items_v3``; retirement of the ``roadmap_exec_order``/``roadmap_todo``
16``file_kind``s via a SQLite table-rebuild of ``roadmap_render_sections``; and
17the base-capability ``registry_meta.store_scope`` column.
19Spec: ``REGISTRY_ROADMAP_SCHEMA_V0_3_1.md`` (L-a), §1–§6, §9.3.
23- Add ``purpose TEXT NOT NULL DEFAULT ''`` to ``roadmap_phases`` and
24 ``roadmap_items_v3`` (§2).
25- Add ``successor_item_id TEXT`` self-FK to ``roadmap_items_v3`` (§3).
26- Rebuild ``roadmap_render_sections`` with the six-token ``file_kind`` CHECK
27 and deactivate retired ``file_kind`` rows (§5).
28- Add ``registry_meta.store_scope`` base-capability column (§9.3).
29- Bump SQLite ``user_version`` to 32.
35 L2 - migration lifecycle
36 L3 - schema version decisions
37 L4 - table definition checks
41- Callers own transaction boundaries.
42- The migration is idempotent (guarded by ``is_applied``).
43- ``rollback`` reverts bookkeeping only; table shape is backward-compatible.
47- sqlite3 (migration connection)
48- oc_diagnostics (_dbg structured logger)
50from __future__
import annotations
54from oc_diagnostics
import _dbg
56_dbg(
"OC_CHAT.MAIN",
"module",
"loaded", 4)
58ROADMAP_SOURCE_FIELDS_SCHEMA_VERSION = 32
59ROADMAP_SOURCE_FIELDS_SCHEMA_DESCRIPTION = (
61 "roadmap source fields REGISTRY_ROADMAP_SCHEMA_V0_3_1"
66_ROADMAP_EXTENDED_TOKEN =
"'roadmap_extended'"
67_STORE_SCOPE_COLUMN =
"store_scope"
71 """Return SQLite's integer schema user version."""
73 return int(conn.execute(
"PRAGMA user_version").fetchone()[0])
76def apply(conn: sqlite3.Connection) ->
None:
77 """Apply the v0.3.2 roadmap source fields migration if needed."""
81 f
"PRAGMA user_version = {ROADMAP_SOURCE_FIELDS_SCHEMA_VERSION}"
86 f
"PRAGMA user_version = {ROADMAP_SOURCE_FIELDS_SCHEMA_VERSION}"
91 """Reset user_version for tests; table shape remains backward-compatible.
93 Mirrors ``v0_3_1_auth_events.rollback``: revert bookkeeping only.
96 conn.execute(
"PRAGMA user_version = 31")
98 "DELETE FROM schema_migrations WHERE version = ?",
99 (ROADMAP_SOURCE_FIELDS_SCHEMA_VERSION,),
104 """Return True when all four migration effects are present (L-a §5 / §9.3).
106 Probe 1 — ``roadmap_phases.purpose`` AND ``roadmap_items_v3.purpose`` exist.
107 Probe 2 — ``roadmap_items_v3.successor_item_id`` exists.
108 Probe 3 — ``roadmap_render_sections.file_kind`` CHECK contains ``'roadmap_extended'``.
109 Probe 4 — ``registry_meta.store_scope`` exists.
112 if not _has_column(conn,
"roadmap_phases",
"purpose"):
115 if "purpose" not in items_cols
or "successor_item_id" not in items_cols:
119 "SELECT sql FROM sqlite_master"
120 " WHERE type = 'table' AND name = 'roadmap_render_sections'"
126 except (TypeError, KeyError, IndexError):
128 if _ROADMAP_EXTENDED_TOKEN
not in str(sql):
130 return _has_column(conn,
"registry_meta", _STORE_SCOPE_COLUMN)
134 """Return SQL used when building a fresh schema in one executescript call.
136 This string is appended after ``v0_3_1_auth_events.migration_sql()`` in
137 ``_run_schema_script`` inside an outer ``BEGIN IMMEDIATE`` transaction.
138 No inner BEGIN/COMMIT — the PRAGMA foreign_keys lines are no-ops inside a
139 transaction but take effect when ``apply()`` calls executescript in
143 return _MIGRATION_SQL + f
"\nPRAGMA user_version = {ROADMAP_SOURCE_FIELDS_SCHEMA_VERSION};\n"
147 conn.executescript(_MIGRATION_SQL)
151 rows = conn.execute(f
"PRAGMA table_info({table})").fetchall()
152 result: set[str] = set()
155 result.add(str(row[
"name"]))
156 except (TypeError, KeyError, IndexError):
157 result.add(str(row[1]))
161def _has_column(conn: sqlite3.Connection, table: str, column: str) -> bool:
171ALTER TABLE roadmap_phases ADD COLUMN purpose TEXT NOT NULL DEFAULT '';
172ALTER TABLE roadmap_items_v3 ADD COLUMN purpose TEXT NOT NULL DEFAULT '';
173ALTER TABLE roadmap_items_v3 ADD COLUMN successor_item_id TEXT REFERENCES roadmap_items_v3(item_id);
175PRAGMA foreign_keys = OFF;
177CREATE TABLE roadmap_render_sections_new (
178 id INTEGER PRIMARY KEY AUTOINCREMENT,
179 file_kind TEXT NOT NULL
181 'roadmap','roadmap_exec_order','roadmap_todo','roadmap_changelog',
182 'roadmap_extended','root_changelog'
184 section_path TEXT NOT NULL,
185 sort_order INTEGER NOT NULL,
186 query_kind TEXT NOT NULL
187 CHECK(query_kind IN (
190 'phase_section_with_subrows',
191 'backlog_track_table',
192 'git_workflow_table',
193 'completed_in_version',
194 'recently_completed_window',
197 query_params_json TEXT,
198 required_min_version TEXT REFERENCES roadmap_versions(version_id),
199 active INTEGER NOT NULL DEFAULT 1 CHECK(active IN (0,1))
202INSERT INTO roadmap_render_sections_new
203 (id, file_kind, section_path, sort_order, query_kind,
204 query_params_json, required_min_version, active)
206 id, file_kind, section_path, sort_order, query_kind,
207 query_params_json, required_min_version, active
208FROM roadmap_render_sections;
210DROP TABLE roadmap_render_sections;
212ALTER TABLE roadmap_render_sections_new RENAME TO roadmap_render_sections;
214CREATE INDEX idx_roadmap_render_sections_file
215 ON roadmap_render_sections(file_kind, sort_order);
217UPDATE roadmap_render_sections
219 WHERE file_kind IN ('roadmap_exec_order', 'roadmap_todo');
221PRAGMA foreign_key_check;
223PRAGMA foreign_keys = ON;
225ALTER TABLE registry_meta
226 ADD COLUMN store_scope TEXT NOT NULL DEFAULT 'live_store'
227 CHECK(store_scope IN ('live_store', 'authored_projection'));
set[str] _column_names(sqlite3.Connection conn, str table)
bool _has_column(sqlite3.Connection conn, str table, str column)
int current_user_version(sqlite3.Connection conn)
bool is_applied(sqlite3.Connection conn)
None apply(sqlite3.Connection conn)
None rollback(sqlite3.Connection conn)
None _run_migration(sqlite3.Connection conn)