oc_chat
Loading...
Searching...
No Matches
v0_3_roadmap.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# oc_chat/registry/migrations/v0_3_roadmap.py
4
5# OCT-LINT: disable=tests_exist reason="Covered by oc_chat/tests/registry/roadmap/test_schema_migration.py"
6"""
7Runtime migration for the roadmap v0.3 registry extension.
8
9Purpose
10-------
11Apply and roll back the activated roadmap v0.3 SQL extension.
12
13Responsibilities
14----------------
15- Load the canonical REGISTRY_SCHEMA_V0_3.sql file.
16- Create roadmap tables idempotently and set PRAGMA user_version to 3.
17- Drop roadmap tables during rollback for migration tests and recovery.
18
19Diagnostics
20-----------
21Domain: OC_CHAT.MAIN
22Levels:
23 L2 - migration lifecycle
24 L3 - schema version decisions
25 L4 - table existence checks
26
27Contracts
28---------
29- The SQL file is the single source of truth for created objects.
30- The module does not import wall-clock APIs; callers provide timestamps.
31
32Dependencies
33------------
34- sqlite3 (migration connection)
35- oc_diagnostics (_dbg structured logger)
36"""
37from __future__ import annotations
38
39from pathlib import Path
40import sqlite3
41
42from oc_diagnostics import _dbg
43
44_dbg("OC_CHAT.MAIN", "module", "loaded", 4)
45
46ROADMAP_SCHEMA_VERSION = 3
47ROADMAP_SCHEMA_DESCRIPTION = "roadmap REGISTRY_SCHEMA_V0_3"
48
49ROADMAP_TABLES = (
50 "roadmap_eras",
51 "roadmap_versions",
52 "roadmap_phases",
53 "roadmap_tracks",
54 "roadmap_items_v3",
55 "roadmap_item_tracks",
56 "roadmap_item_dependencies",
57 "roadmap_item_events",
58 "roadmap_plan_links",
59 "roadmap_render_sections",
60)
61
62
63def schema_sql_path() -> Path:
64 """Return the canonical v0.3 roadmap SQL extension path."""
65
66 return (
67 Path(__file__).resolve().parents[2]
68 / "docs"
69 / "architecture"
70 / "orchestration"
71 / "REGISTRY_SCHEMA_V0_3.sql"
72 )
73
74
75def load_schema_sql() -> str:
76 """Load the canonical v0.3 roadmap SQL extension."""
77
78 return schema_sql_path().read_text(encoding="utf-8")
79
80
81def current_user_version(conn: sqlite3.Connection) -> int:
82 """Return SQLite's integer schema user version."""
83
84 return int(conn.execute("PRAGMA user_version").fetchone()[0])
85
86
87def apply(conn: sqlite3.Connection) -> None:
88 """Apply the roadmap v0.3 schema extension idempotently."""
89
90 if current_user_version(conn) >= ROADMAP_SCHEMA_VERSION and tables_exist(conn):
91 return
92 conn.executescript(load_schema_sql())
93
94
95def rollback(conn: sqlite3.Connection) -> None:
96 """Drop roadmap v0.3 tables and reset SQLite user_version to 2."""
97
98 for name in reversed(ROADMAP_TABLES):
99 conn.execute(f"DROP TABLE IF EXISTS {name}")
100 # OCT-LINT: disable=no_hardcoded_secrets reason="Schema object names are migration constants, not secrets"
101 conn.execute("DROP TRIGGER IF EXISTS roadmap_item_events_no_update")
102 conn.execute("DROP TRIGGER IF EXISTS roadmap_item_events_no_delete")
103 conn.execute("PRAGMA user_version = 2")
104 conn.execute(
105 "DELETE FROM schema_migrations WHERE version = ?",
106 (ROADMAP_SCHEMA_VERSION,),
107 )
108
109
110def tables_exist(conn: sqlite3.Connection) -> bool:
111 """Return True when all roadmap v0.3 tables exist."""
112
113 rows = conn.execute(
114 "SELECT name FROM sqlite_master WHERE type = 'table'"
115 ).fetchall()
116 existing = {str(row[0]) for row in rows}
117 return set(ROADMAP_TABLES) <= existing
118
119
120def table_names() -> tuple[str, ...]:
121 """Return roadmap v0.3 table names in dependency order."""
122
123 return ROADMAP_TABLES
None apply(sqlite3.Connection conn)
None rollback(sqlite3.Connection conn)
int current_user_version(sqlite3.Connection conn)
bool tables_exist(sqlite3.Connection conn)