7SQLite backend for the orchestration registry.
11Own all SQL used by the M11 registry storage foundation, including
12canonical schema loading, connection PRAGMAs, init-time seeding, and
13backend maintenance operations.
17- Load the canonical registry schema from the design-lock SQL file.
18- Apply required SQLite PRAGMAs on every connection.
19- Seed singleton rows for ``python -m oc_chat.registry init``.
20- Execute the single mutating seam and insert one operation-log row.
21- Own lock acquisition, release, renewal, and expiry sweep SQL.
22- Provide integrity check, backup/restore, and vacuum hooks.
34- SQL remains in this module; higher registry layers call backend methods.
35- Init is all-or-nothing: schema load and seed inserts share one transaction.
36- No SQLite ``datetime('now')`` defaults are used; timestamps come from Clock.
40- oc_diagnostics (structured debug logging via _dbg)
41- sqlite3 (registry storage engine)
42- oc_chat.registry.clock (injected time)
43- oc_chat.registry.backends.base (StorageBackend contract, capability error and return types)
44- oc_chat.registry.canonical (canonical_json for operation payloads and lock metadata)
45- oc_chat.registry.errors (registry error types raised by the mutating seam and lock SQL)
46- oc_chat.registry.migrations.v0_3_1_auth_events (auth events schema migration and version row)
47- oc_chat.registry.migrations.v0_3_2_roadmap_source_fields (roadmap source fields schema migration and version row)
48- oc_chat.registry.migrations.v0_3_roadmap (roadmap schema SQL, version row and upgrade checks)
49- oc_chat.registry.store_scope (assert_live_store guard before each domain command)
51from __future__
import annotations
53from contextlib
import contextmanager
54from dataclasses
import dataclass
56from pathlib
import Path
59from typing
import Iterator, Mapping
62from oc_diagnostics
import _dbg
65 BackendCapabilityError,
73 ActorTypeMismatchError,
78 PayloadSchemaViolationError,
79 PayloadVersionMismatchError,
85 v0_3_2_roadmap_source_fields,
90_dbg(
"OC_CHAT.MAIN",
"module",
"loaded", 4)
93SCHEMA_DESCRIPTION =
"baseline REGISTRY_SCHEMA_V0_2_3"
94_PRAGMA_PREFIX =
"PRAGMA "
95_ACTOR_TYPES_BY_SLOT_KIND = {
96 "operator":
"human_operator",
97 "orchestrator":
"orchestrator_agent",
98 "worker":
"worker_agent",
100_MAX_PARENT_DEPTH = 1000
103@dataclass(frozen=True)
105 """Values substituted into the canonical seed template at init time."""
115 secret_fingerprint: str
120 """SQLite implementation of the registry storage backend."""
125 clock: Clock |
None =
None,
127 check_capabilities: bool =
True,
132 self.
path.parent.mkdir(parents=
True, exist_ok=
True)
133 if check_capabilities:
139 return str(self.
path) ==
":memory:"
142 """Open a connection with all required PRAGMAs applied."""
144 conn = sqlite3.connect(str(self.
path))
145 conn.row_factory = sqlite3.Row
150 """Apply the canonical schema baseline atomically."""
156 """Apply schema and seed singleton rows in one transaction."""
164 raise ValueError(f
"registry database is not empty: {self.path}")
168 conn.execute(
"COMMIT")
170 conn.execute(
"ROLLBACK")
176 def transaction(self, mode: str =
"immediate") -> Iterator[sqlite3.Connection]:
179 begin =
"BEGIN IMMEDIATE" if mode
in {
"immediate",
"write"}
else "BEGIN"
183 conn.execute(
"COMMIT")
185 conn.execute(
"ROLLBACK")
193 filters: Mapping[str, object] |
None =
None,
195 raise NotImplementedError(
"read models are implemented in a later M11 plan")
201 payload: Mapping[str, object],
204 payload_version: int = 1,
205 command: str |
None =
None,
206 parent_operation_id: int |
None =
None,
207 related_plan_id: str |
None =
None,
208 related_round: int |
None =
None,
209 operation_uuid: str |
None =
None,
210 hmac_key_id: str |
None =
None,
211 system_actor: bool =
False,
212 ) -> OperationLogRow:
214 _store_scope.assert_live_store(conn)
220 actor_type=actor_type,
221 payload_version=payload_version,
223 parent_operation_id=parent_operation_id,
224 related_plan_id=related_plan_id,
225 related_round=related_round,
226 operation_uuid=operation_uuid,
227 hmac_key_id=hmac_key_id,
228 system_actor=system_actor,
233 projection: Mapping[str, object],
237 ) -> list[OperationLogRow]:
238 """Apply a legacy migration projection and emit seam audit rows."""
240 emitted: list[OperationLogRow] = []
247 "migrate.import.agent",
250 actor_type=actor_type,
251 related_plan_id=
None,
258 import_rows_by_plan: dict[str, OperationLogRow] = {}
262 plan_id = str(plan[
"plan_id"])
265 "migrate.import.plan",
268 actor_type=actor_type,
269 related_plan_id=plan_id,
272 import_rows_by_plan[plan_id] = import_row
273 emitted.append(import_row)
275 backfill_payload = plan.get(
"backfill_payload")
276 if isinstance(backfill_payload, Mapping):
280 "migrate.backfill_completed",
283 actor_type=actor_type,
284 parent_operation_id=import_row.id,
285 related_plan_id=plan_id,
289 blocked_payload = plan.get(
"blocked_payload")
290 if isinstance(blocked_payload, Mapping):
294 "migrate.note_blocked",
297 actor_type=actor_type,
298 parent_operation_id=import_row.id,
299 related_plan_id=plan_id,
303 for round_plan
in _mapping_list(projection.get(
"round_plans")):
306 for queue_entry
in _mapping_list(projection.get(
"queue_entries")):
307 plan_id = str(queue_entry[
"plan_id"])
311 "migrate.import.queue_entry",
314 actor_type=actor_type,
315 parent_operation_id=(
316 import_rows_by_plan[plan_id].id
317 if plan_id
in import_rows_by_plan
320 related_plan_id=plan_id,
324 max_plan_number = conn.execute(
326 "SELECT COALESCE(MAX(plan_number), 0) FROM plans"
330 "UPDATE plan_seq SET next_number = ? WHERE id = 1",
331 (int(max_plan_number) + 1,),
336 """Return the current-state rows that ``migrate verify`` compares."""
341 for row
in conn.execute(
343 "SELECT slot, slot_kind, identity_kind, model_class, "
344 "orchestrator_eligible, provider, profile, active "
345 "FROM agents WHERE identity_kind = 'agent' "
351 for row
in conn.execute(
353 "SELECT plan_id, plan_number, title, plan_type, status, "
354 "assigned_slot, queue_position, branch, worktree, body_path, "
355 "body_sha256, body_observed_at, model_override, "
356 "thinking_override, session_override, ready_for_merge_at, "
357 "completed_at, closed_round FROM plans ORDER BY plan_id"
362 for row
in conn.execute(
364 "SELECT plan_id, assigned_slot AS slot, queue_position AS position "
365 "FROM plans WHERE queue_position IS NOT NULL "
366 "ORDER BY slot, position"
371 for row
in conn.execute(
372 "SELECT round_no, status, cleanup_status, orchestrator_slot, "
373 "opened_at, closed_at, final_commit_sha FROM rounds "
379 for row
in conn.execute(
380 "SELECT round_no, plan_id, merge_order, merge_commit_sha, "
381 "test_result FROM round_plans ORDER BY round_no, merge_order"
387 "queue_entries": queue_entries,
389 "round_plans": round_plans,
393 """Return completed null-round plans missing a backfill audit marker."""
398 for row
in conn.execute(
400 "SELECT plan_id FROM plans "
401 "WHERE status = 'completed' AND closed_round IS NULL "
405 backfilled: set[str] = set()
406 for row
in conn.execute(
408 "SELECT args_json FROM operation_log "
409 "WHERE operation_type = 'migrate.backfill_completed'"
412 payload = json.loads(str(row[
"args_json"]))
413 except (TypeError, ValueError):
415 plan_id = payload.get(
"plan_id")
if isinstance(payload, dict)
else None
416 if isinstance(plan_id, str):
417 backfilled.add(plan_id)
418 return [plan_id
for plan_id
in plan_ids
if plan_id
not in backfilled]
422 conn: sqlite3.Connection,
425 payload: Mapping[str, object],
428 payload_version: int = 1,
429 command: str |
None =
None,
430 parent_operation_id: int |
None =
None,
431 related_plan_id: str |
None =
None,
432 related_round: int |
None =
None,
433 operation_uuid: str |
None =
None,
434 hmac_key_id: str |
None =
None,
435 system_actor: bool =
False,
436 ) -> OperationLogRow:
437 command_text = command
or operation_type
438 operation_uuid = operation_uuid
or str(uuid.uuid4())
446 expected_actor_type = _ACTOR_TYPES_BY_SLOT_KIND[slot_kind]
447 if actor_type != expected_actor_type:
449 actor_slot=actor_slot,
450 expected_actor_type=expected_actor_type,
451 supplied_actor_type=actor_type,
459 if parent_operation_id
is not None:
471 next_version = current_version + 1
472 now = self.
clock.now_wall()
473 cursor = conn.execute(
475 "INSERT INTO operation_log("
476 "operation_uuid, ts, state_version, actor_slot, actor_type, "
477 "hmac_key_id, command, operation_type, payload_version, "
478 "args_json, before_hash, after_hash, auth_ok, parent_operation_id, "
479 "related_plan_id, related_round"
480 ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, 1, ?, ?, ?)",
498 "UPDATE registry_meta SET state_version = ?, updated_at = ? "
506 actor_slot=actor_slot,
508 related_round=related_round,
509 source_operation_id=int(cursor.lastrowid),
512 id=int(cursor.lastrowid),
513 operation_uuid=operation_uuid,
515 state_version=next_version,
516 actor_slot=actor_slot,
517 actor_type=actor_type,
518 command=command_text,
519 operation_type=operation_type,
520 payload_version=payload_version,
522 parent_operation_id=parent_operation_id,
523 related_plan_id=related_plan_id,
524 related_round=related_round,
525 hmac_key_id=row_hmac_key_id,
530 conn: sqlite3.Connection,
532 payload: Mapping[str, object],
536 related_round: int |
None,
537 source_operation_id: int,
539 if operation_type ==
"plan.create":
541 elif operation_type ==
"plan.set_status":
543 elif operation_type ==
"plan.assign":
545 elif operation_type ==
"plan.supersede":
547 elif operation_type ==
"plan.reimport":
549 elif operation_type ==
"queue.enqueue":
551 elif operation_type ==
"queue.move":
553 elif operation_type ==
"queue.dequeue":
555 elif operation_type ==
"queue.complete":
557 elif operation_type
in {
"role.handover.start",
"handover.initiate"}:
559 elif operation_type
in {
"role.handover.complete",
"handover.accept"}:
561 elif operation_type ==
"roadmap_item.create":
569 elif operation_type ==
"roadmap_item.set_status":
576 elif operation_type ==
"roadmap_item.add_dependency":
584 elif operation_type ==
"roadmap_version.bump":
586 elif operation_type ==
"roadmap_phase.complete":
593 elif operation_type ==
"roadmap_section.upsert":
595 elif operation_type ==
"roadmap_plan_link.create":
597 elif operation_type ==
"round.open":
599 elif operation_type ==
"round.merge":
601 elif operation_type ==
"round.test":
603 elif operation_type ==
"round.lint":
605 elif operation_type ==
"round.consolidate":
607 elif operation_type ==
"round.ff_main":
609 elif operation_type ==
"round.cleanup":
611 elif operation_type ==
"round.abort":
616 conn: sqlite3.Connection,
617 payload: Mapping[str, object],
622 if state[
"handover_status"] !=
"stable":
624 state=
"handover_in_flight",
625 actor_slot=actor_slot,
626 operation_type=
"role.handover.start",
628 from_slot = str(payload[
"from_slot"])
629 to_slot = str(payload[
"to_slot"])
632 "UPDATE orchestration_state SET "
633 "orchestrator_slot = COALESCE(orchestrator_slot, ?), "
634 "handover_status = 'requested', handover_outgoing = ?, "
635 "handover_incoming = ?, handover_requested_at = ?, "
636 "ready_to_handover = 0, updated_at = ?, updated_by = ? "
638 (from_slot, from_slot, to_slot, ts, ts, actor_slot),
643 conn: sqlite3.Connection,
644 payload: Mapping[str, object],
649 to_slot = str(payload[
"to_slot"])
650 if state[
"handover_status"]
not in {
"requested",
"draining"}:
652 state=
"no_handover_pending",
653 actor_slot=actor_slot,
654 operation_type=
"role.handover.complete",
655 handover_id=
_optional_int(payload.get(
"parent_operation_id")),
657 if str(state[
"handover_incoming"]) != to_slot:
659 state=
"handover_target_mismatch",
660 actor_slot=actor_slot,
661 operation_type=
"role.handover.complete",
662 handover_id=
_optional_int(payload.get(
"parent_operation_id")),
664 if actor_slot != to_slot
and self.
_actor_slot_kind(conn, actor_slot) !=
"operator":
666 state=
"wrong_accepting_slot",
667 actor_slot=actor_slot,
668 operation_type=
"role.handover.complete",
669 handover_id=
_optional_int(payload.get(
"parent_operation_id")),
673 "UPDATE orchestration_state SET "
674 "orchestrator_slot = ?, handover_status = 'stable', "
675 "handover_outgoing = NULL, handover_incoming = NULL, "
676 "handover_requested_at = NULL, ready_to_handover = 0, "
677 "updated_at = ?, updated_by = ? WHERE id = 1",
678 (to_slot, ts, actor_slot),
683 conn: sqlite3.Connection,
684 payload: Mapping[str, object],
687 plan_number = int(payload[
"plan_number"])
691 "plan_id, plan_number, title, plan_type, status, assigned_slot, "
692 "queue_position, branch, worktree, body_path, body_sha256, "
693 "body_observed_at, model_override, thinking_override, "
694 "session_override, created_at, updated_at"
695 ") VALUES (?, ?, ?, ?, 'draft', NULL, NULL, NULL, NULL, ?, ?, "
697 "ON CONFLICT(plan_id) DO UPDATE SET "
698 "plan_number = excluded.plan_number, "
699 "title = excluded.title, "
700 "plan_type = excluded.plan_type, "
701 "body_path = excluded.body_path, "
702 "body_sha256 = excluded.body_sha256, "
703 "body_observed_at = excluded.body_observed_at, "
704 "model_override = excluded.model_override, "
705 "thinking_override = excluded.thinking_override, "
706 "session_override = excluded.session_override, "
707 "updated_at = excluded.updated_at",
709 str(payload[
"plan_id"]),
711 str(payload[
"title"]),
713 str(payload[
"body_path"]),
714 str(payload[
"body_sha256"]),
725 "UPDATE plan_seq SET next_number = CASE "
726 "WHEN next_number <= ? THEN ? ELSE next_number END WHERE id = 1",
727 (plan_number, plan_number + 1),
732 conn: sqlite3.Connection,
733 payload: Mapping[str, object],
737 plan_id = str(payload[
"plan_id"])
738 to_status = str(payload[
"to_status"])
740 "INSERT INTO plan_status_events("
741 "plan_id, from_status, to_status, actor_slot, source, created_at, note"
742 ") VALUES (?, ?, ?, ?, ?, ?, ?)",
753 queue_position_expr = (
755 if to_status
in {
"pending",
"implementing",
"ready_for_merge"}
762 f
"queue_position = {queue_position_expr}, "
763 "ready_for_merge_at = CASE "
764 "WHEN ? = 'ready_for_merge' AND ready_for_merge_at IS NULL THEN ? "
765 "ELSE ready_for_merge_at END, "
766 "completed_at = CASE "
767 "WHEN ? = 'completed' AND completed_at IS NULL THEN ? "
768 "ELSE completed_at END, "
771 (to_status, to_status, ts, to_status, ts, ts, plan_id),
776 conn: sqlite3.Connection,
777 payload: Mapping[str, object],
782 "UPDATE plans SET assigned_slot = ?, branch = ?, worktree = ?, "
783 "queue_position = ?, status = CASE "
784 "WHEN status = 'draft' THEN 'pending' ELSE status END, "
785 "updated_at = ? WHERE plan_id = ?",
787 str(payload[
"to_slot"]),
788 str(payload[
"branch"]),
789 str(payload[
"worktree"]),
790 int(payload[
"queue_position"]),
792 str(payload[
"plan_id"]),
798 conn: sqlite3.Connection,
799 payload: Mapping[str, object],
804 "UPDATE plans SET status = 'superseded', queue_position = NULL, "
805 "superseded_by = ?, updated_at = ? WHERE plan_id = ?",
807 str(payload[
"superseded_by_plan_id"]),
809 str(payload[
"plan_id"]),
815 conn: sqlite3.Connection,
816 payload: Mapping[str, object],
821 "UPDATE plans SET body_sha256 = ?, body_observed_at = ?, "
822 "updated_at = ? WHERE plan_id = ?",
824 str(payload[
"new_body_sha256"]),
825 str(payload[
"new_body_observed_at"]),
827 str(payload[
"plan_id"]),
833 conn: sqlite3.Connection,
834 payload: Mapping[str, object],
837 plan_id = str(payload[
"plan_id"])
838 slot = str(payload[
"slot"])
839 position = int(payload[
"position"])
843 if current
is not None:
848 "UPDATE plans SET assigned_slot = ?, queue_position = ?, "
849 "status = CASE WHEN status IN "
850 "('draft','partial','failed','superseded','completed') "
851 "THEN 'pending' ELSE status END, updated_at = ? "
853 (slot, position, ts, plan_id),
858 conn: sqlite3.Connection,
859 payload: Mapping[str, object],
862 plan_id = str(payload[
"plan_id"])
866 from_slot = current[0]
if current
is not None else str(payload[
"from_slot"])
867 to_slot = str(payload[
"to_slot"])
868 from_position = current[1]
if current
is not None else int(payload[
"from_position"])
869 to_position = int(payload[
"to_position"])
873 "UPDATE plans SET assigned_slot = ?, queue_position = ?, "
874 "updated_at = ? WHERE plan_id = ?",
875 (to_slot, to_position, ts, plan_id),
880 conn: sqlite3.Connection,
881 payload: Mapping[str, object],
884 plan_id = str(payload[
"plan_id"])
886 slot = current[0]
if current
is not None else str(payload[
"slot"])
887 position = current[1]
if current
is not None else int(payload[
"position"])
888 reason = str(payload[
"reason"])
890 if reason ==
"transition_to_failed":
892 "UPDATE plans SET status = 'failed', updated_at = ? WHERE plan_id = ?",
895 elif reason ==
"transition_to_integrating":
897 "UPDATE plans SET status = 'integrating', updated_at = ? "
903 "UPDATE plans SET updated_at = ? WHERE plan_id = ?",
909 conn: sqlite3.Connection,
910 payload: Mapping[str, object],
912 related_round: int |
None,
914 round_no = int(payload[
"round_no"])
917 "UPDATE plans SET status = 'completed', queue_position = NULL, "
918 "completed_at = COALESCE(completed_at, ?), closed_round = ?, "
919 "updated_at = ? WHERE plan_id = ?",
922 related_round
if related_round
is not None else round_no,
924 str(payload[
"plan_id"]),
930 conn: sqlite3.Connection,
931 payload: Mapping[str, object],
934 source_operation_id: int,
936 item_id = str(payload[
"item_id"])
937 track_kind = str(payload.get(
"track_kind")
or "m_phase")
940 if track_kind ==
"m_phase":
942 if phase_code
is None:
943 raise ValueError(
"roadmap m_phase item requires phase_code")
948 label=
_optional_str(payload.get(
"phase_label"))
or phase_code,
950 parent_item_id =
_optional_str(payload.get(
"parent_item_id"))
951 if parent_item_id
is not None:
952 parent_row = conn.execute(
954 "SELECT 1 FROM roadmap_items_v3 WHERE item_id = ?",
957 if parent_row
is None:
958 parent_item_id =
None
959 introduced =
_optional_str(payload.get(
"introduced_in_version_id"))
960 status = str(payload.get(
"status")
or "todo")
963 "INSERT INTO roadmap_items_v3("
964 "item_id, track_kind, phase_id, parent_item_id, title, prose, prose_path, "
965 "status, priority, sort_order, target_version, introduced_in_version_id, "
966 "closed_in_version_id, closed_round_no, superseded_by_item_id, "
967 "created_at, updated_at"
968 ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
969 "ON CONFLICT(item_id) DO UPDATE SET "
970 "track_kind = excluded.track_kind, "
971 "phase_id = excluded.phase_id, "
972 "parent_item_id = excluded.parent_item_id, "
973 "title = excluded.title, "
974 "prose = excluded.prose, "
975 "prose_path = excluded.prose_path, "
976 "status = excluded.status, "
977 "priority = excluded.priority, "
978 "sort_order = excluded.sort_order, "
979 "target_version = excluded.target_version, "
980 "introduced_in_version_id = COALESCE("
981 "roadmap_items_v3.introduced_in_version_id, "
982 "excluded.introduced_in_version_id"
984 "closed_in_version_id = excluded.closed_in_version_id, "
985 "closed_round_no = excluded.closed_round_no, "
986 "superseded_by_item_id = excluded.superseded_by_item_id, "
987 "updated_at = excluded.updated_at",
993 str(payload[
"title"]),
998 int(payload.get(
"sort_order")
or 0),
1012 from_value_json=
None,
1013 to_value_json=canonical_json(dict(payload)),
1014 in_version_id=introduced,
1015 source_operation_id=source_operation_id,
1017 note=f
"created by {actor_slot}",
1022 conn: sqlite3.Connection,
1023 payload: Mapping[str, object],
1025 source_operation_id: int,
1027 item_id = str(payload[
"item_id"])
1029 to_status = str(payload[
"to_status"])
1033 "UPDATE roadmap_items_v3 SET status = ?, "
1034 "closed_in_version_id = CASE "
1035 "WHEN ? IN ('done','deferred','superseded') THEN COALESCE(?, closed_in_version_id) "
1036 "ELSE closed_in_version_id END, "
1037 "closed_round_no = COALESCE(?, closed_round_no), "
1038 "superseded_by_item_id = COALESCE(?, superseded_by_item_id), "
1039 "updated_at = ? WHERE item_id = ?",
1054 from_value_json=canonical_json({
"status": from_status}),
1055 to_value_json=canonical_json({
"status": to_status}),
1056 in_version_id=in_version_id,
1057 source_operation_id=source_operation_id,
1064 conn: sqlite3.Connection,
1065 payload: Mapping[str, object],
1068 source_operation_id: int,
1070 from_item_id = str(payload[
"from_item_id"])
1071 to_item_id = str(payload[
"to_item_id"])
1072 kind = str(payload.get(
"kind")
or "gates")
1075 "INSERT OR IGNORE INTO roadmap_item_dependencies("
1076 "from_item_id, to_item_id, kind, created_at, created_by_slot"
1077 ") VALUES (?, ?, ?, ?, ?)",
1078 (from_item_id, to_item_id, kind, ts, actor_slot),
1084 from_value_json=
None,
1085 to_value_json=canonical_json({
"to_item_id": to_item_id,
"kind": kind}),
1087 source_operation_id=source_operation_id,
1094 conn: sqlite3.Connection,
1095 payload: Mapping[str, object],
1098 from_version =
_optional_str(payload.get(
"from_version_id"))
1099 to_version = str(payload[
"to_version_id"])
1102 status = str(payload.get(
"status")
or "draft")
1103 if status ==
"current":
1106 "UPDATE roadmap_versions SET status = 'superseded', "
1107 "superseded_by_version_id = ?, last_updated_at = ? "
1108 "WHERE era_id = ? AND status = 'current'",
1109 (to_version, ts, era_id),
1113 "INSERT INTO roadmap_versions("
1114 "version_id, era_id, released_at, status, parent_version_id, "
1115 "supersedes_version_id, superseded_by_version_id, last_updated_at, "
1116 "source_round_no, bump_kind, notes"
1117 ") VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?) "
1118 "ON CONFLICT(version_id) DO UPDATE SET "
1119 "status = excluded.status, "
1120 "last_updated_at = excluded.last_updated_at, "
1121 "source_round_no = excluded.source_round_no, "
1122 "bump_kind = excluded.bump_kind, "
1123 "notes = excluded.notes",
1133 str(payload.get(
"bump_kind")
or "patch"),
1140 conn: sqlite3.Connection,
1141 payload: Mapping[str, object],
1143 source_operation_id: int,
1145 phase_code = str(payload[
"phase_code"])
1148 "SELECT item_id FROM roadmap_items_v3 WHERE item_id = ?",
1154 "UPDATE roadmap_items_v3 SET status = 'done', "
1155 "closed_in_version_id = COALESCE(?, closed_in_version_id), "
1156 "updated_at = ? WHERE item_id = ?",
1157 (in_version_id, ts, phase_code),
1163 from_value_json=
None,
1164 to_value_json=canonical_json({
"status":
"done"}),
1165 in_version_id=in_version_id,
1166 source_operation_id=source_operation_id,
1172 "UPDATE roadmap_phases SET retired_in_version_id = COALESCE(?, retired_in_version_id), "
1173 "updated_at = ? WHERE phase_code = ?",
1174 (in_version_id, ts, phase_code),
1179 conn: sqlite3.Connection,
1180 payload: Mapping[str, object],
1184 "DELETE FROM roadmap_render_sections "
1185 "WHERE file_kind = ? AND section_path = ?",
1186 (str(payload[
"file_kind"]), str(payload[
"section_path"])),
1190 "INSERT INTO roadmap_render_sections("
1191 "file_kind, section_path, sort_order, query_kind, "
1192 "query_params_json, required_min_version, active"
1193 ") VALUES (?, ?, ?, ?, ?, ?, ?)",
1195 str(payload[
"file_kind"]),
1196 str(payload[
"section_path"]),
1197 int(payload[
"sort_order"]),
1198 str(payload[
"query_kind"]),
1207 conn: sqlite3.Connection,
1208 payload: Mapping[str, object],
1214 "INSERT OR REPLACE INTO roadmap_plan_links("
1215 "plan_id, item_id, contribution, created_at, created_by_slot"
1216 ") VALUES (?, ?, ?, ?, ?)",
1218 str(payload[
"plan_id"]),
1219 str(payload[
"item_id"]),
1220 str(payload.get(
"contribution")
or "partial"),
1228 conn: sqlite3.Connection,
1234 "INSERT OR IGNORE INTO roadmap_eras("
1235 "era_id, era_label, major_bump_reason, started_at, ended_at, "
1236 "archived_under, sort_order"
1237 ") VALUES (?, ?, NULL, ?, NULL, NULL, ?)",
1243 conn: sqlite3.Connection,
1250 parent_phase_id =
None
1251 if parent_code
is not None and parent_code != phase_code:
1260 "INSERT INTO roadmap_phases("
1261 "phase_code, label, parent_phase_id, branch_suggestion, "
1262 "commit_prefix, target_version, priority_label, parallel_track, "
1263 "full_spec_url, sort_order, introduced_in_version_id, "
1264 "retired_in_version_id, created_at, updated_at"
1265 ") VALUES (?, ?, ?, NULL, NULL, NULL, NULL, ?, NULL, ?, NULL, NULL, ?, ?) "
1266 "ON CONFLICT(phase_code) DO UPDATE SET "
1267 "label = excluded.label, "
1268 "parent_phase_id = COALESCE(roadmap_phases.parent_phase_id, excluded.parent_phase_id), "
1269 "updated_at = excluded.updated_at",
1274 1
if phase_code ==
"M11" else 0,
1281 "SELECT phase_id FROM roadmap_phases WHERE phase_code = ?",
1285 raise ValueError(f
"failed to create roadmap phase: {phase_code}")
1286 return int(row[
"phase_id"])
1290 conn: sqlite3.Connection,
1294 from_value_json: str |
None,
1295 to_value_json: str |
None,
1296 in_version_id: str |
None,
1297 source_operation_id: int,
1303 "INSERT INTO roadmap_item_events("
1304 "item_id, event_kind, from_value_json, to_value_json, "
1305 "in_version_id, source_operation_id, source_version_seen, ts, note"
1306 ") VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?)",
1313 source_operation_id,
1320 row = conn.execute(
"SELECT 1 FROM plans WHERE plan_id = ?", (plan_id,)).fetchone()
1321 return row
is not None
1325 conn: sqlite3.Connection,
1327 ) -> tuple[str, int] |
None:
1330 "SELECT assigned_slot, queue_position FROM plans WHERE plan_id = ?",
1333 if row
is None or row[
"assigned_slot"]
is None or row[
"queue_position"]
is None:
1335 return str(row[
"assigned_slot"]), int(row[
"queue_position"])
1339 conn: sqlite3.Connection,
1341 start_position: int,
1343 exclude_plan_id: str |
None =
None,
1345 if exclude_plan_id
is None:
1348 "UPDATE plans SET queue_position = queue_position + 100000 "
1349 "WHERE assigned_slot = ? AND queue_position >= ?",
1350 (slot, start_position),
1355 "UPDATE plans SET queue_position = queue_position + 100000 "
1356 "WHERE assigned_slot = ? AND queue_position >= ? AND plan_id != ?",
1357 (slot, start_position, exclude_plan_id),
1360 "UPDATE plans SET queue_position = queue_position - 99999 "
1361 "WHERE assigned_slot = ? AND queue_position >= ?",
1362 (slot, start_position + 100000),
1367 conn: sqlite3.Connection,
1374 "UPDATE plans SET queue_position = NULL WHERE plan_id = ?",
1379 "UPDATE plans SET queue_position = queue_position + 100000 "
1380 "WHERE assigned_slot = ? AND queue_position > ?",
1385 "UPDATE plans SET queue_position = queue_position - 100001 "
1386 "WHERE assigned_slot = ? AND queue_position >= ?",
1387 (slot, position + 100001),
1392 conn: sqlite3.Connection,
1393 agent: Mapping[str, object],
1395 now = self.
clock.now_wall()
1398 "INSERT INTO agents("
1399 "slot, slot_kind, identity_kind, model_class, orchestrator_eligible, "
1400 "provider, profile, active, created_at"
1401 ") VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?) "
1402 "ON CONFLICT(slot) DO UPDATE SET "
1403 "slot_kind = excluded.slot_kind, "
1404 "identity_kind = excluded.identity_kind, "
1405 "model_class = excluded.model_class, "
1406 "orchestrator_eligible = excluded.orchestrator_eligible, "
1407 "provider = excluded.provider, "
1408 "profile = excluded.profile, "
1412 str(agent[
"slot_kind"]),
1413 str(agent[
"identity_kind"]),
1414 str(agent[
"model_class"]),
1415 _bool_int(agent[
"orchestrator_eligible"]),
1426 conn: sqlite3.Connection,
1427 payload: Mapping[str, object],
1431 round_no = int(payload[
"round_no"])
1432 existing = conn.execute(
1433 "SELECT status FROM rounds WHERE round_no = ?",
1436 if existing
is not None and str(existing[
"status"])
in {
"closed",
"aborted"}:
1439 status=str(existing[
"status"]),
1440 operation_type=
"round.open",
1442 orchestrator_slot =
_optional_str(payload.get(
"orchestrator_slot"))
or actor_slot
1446 "INSERT INTO rounds(round_no, status, cleanup_status, "
1447 "orchestrator_slot, opened_at) VALUES (?, 'open', 'pending', ?, ?) "
1448 "ON CONFLICT(round_no) DO NOTHING",
1449 (round_no, orchestrator_slot, ts),
1452 if plan_id
is not None:
1454 if merge_order
is None:
1458 "INSERT INTO round_plans(round_no, plan_id, merge_order) "
1459 "VALUES (?, ?, ?) ON CONFLICT(round_no, plan_id) DO NOTHING",
1460 (round_no, plan_id, merge_order),
1465 conn: sqlite3.Connection,
1470 "SELECT COALESCE(MAX(merge_order), 0) AS hi FROM round_plans "
1471 "WHERE round_no = ?",
1474 return int(row[
"hi"]) + 1
1478 conn: sqlite3.Connection,
1479 payload: Mapping[str, object],
1482 round_no = int(payload[
"round_no"])
1483 plan_id = str(payload[
"plan_id"])
1484 staging_branch =
_optional_str(payload.get(
"staging_branch"))
1485 merge_base_sha =
_optional_str(payload.get(
"merge_base_sha"))
1488 "UPDATE rounds SET status = 'closing', "
1489 "staging_branch = COALESCE(?, staging_branch), "
1490 "merge_base_sha = COALESCE(?, merge_base_sha) "
1491 "WHERE round_no = ? AND status IN ('open', 'closing')",
1492 (staging_branch, merge_base_sha, round_no),
1495 if merge_order
is None:
1499 "INSERT INTO round_plans(round_no, plan_id, merge_order, base_sha, "
1500 "merge_commit_sha) VALUES (?, ?, ?, ?, ?) "
1501 "ON CONFLICT(round_no, plan_id) DO UPDATE SET "
1502 "base_sha = COALESCE(excluded.base_sha, round_plans.base_sha), "
1503 "merge_commit_sha = COALESCE(excluded.merge_commit_sha, "
1504 "round_plans.merge_commit_sha)",
1516 conn: sqlite3.Connection,
1517 payload: Mapping[str, object],
1519 round_no = int(payload[
"round_no"])
1522 "UPDATE rounds SET "
1523 "test_count_before = COALESCE(?, test_count_before), "
1524 "test_count_after = COALESCE(?, test_count_after) "
1525 "WHERE round_no = ?",
1534 "UPDATE round_plans SET test_result = ? "
1535 "WHERE round_no = ? AND plan_id = ?",
1539 str(entry[
"plan_id"]),
1545 conn: sqlite3.Connection,
1546 payload: Mapping[str, object],
1554 conn: sqlite3.Connection,
1555 payload: Mapping[str, object],
1557 round_no = int(payload[
"round_no"])
1560 "UPDATE rounds SET status = 'committed', final_commit_sha = ? "
1561 "WHERE round_no = ? AND status IN ('open', 'closing', 'committed')",
1567 conn: sqlite3.Connection,
1568 payload: Mapping[str, object],
1575 conn: sqlite3.Connection,
1576 payload: Mapping[str, object],
1579 round_no = int(payload[
"round_no"])
1581 if outcome ==
"clean":
1584 "UPDATE rounds SET cleanup_status = 'clean', status = 'closed', "
1586 "roadmap_version = COALESCE(?, roadmap_version), "
1587 "app_version = COALESCE(?, app_version) "
1588 "WHERE round_no = ? AND status IN ('committed', 'closed')",
1599 "UPDATE rounds SET cleanup_status = 'failed' WHERE round_no = ?",
1605 conn: sqlite3.Connection,
1606 payload: Mapping[str, object],
1609 round_no = int(payload[
"round_no"])
1612 "UPDATE rounds SET status = 'aborted', closed_at = ? "
1613 "WHERE round_no = ? AND status NOT IN ('closed')",
1625 staging_branch_head_sha: str |
None =
None,
1626 deadline_at: str |
None =
None,
1628 """Insert a new resumable round-close job row."""
1630 now = self.
clock.now_wall()
1634 "INSERT INTO round_jobs(job_id, round_no, status, current_step, "
1635 "created_by, deadline_at, staging_branch_head_sha, created_at, "
1636 "updated_at) VALUES (?, ?, 'running', NULL, ?, ?, ?, ?, ?)",
1642 staging_branch_head_sha,
1649 """Return a round-job row by id, or None."""
1653 "SELECT * FROM round_jobs WHERE job_id = ?",
1656 return None if row
is None else _row_dict(row)
1662 statuses: tuple[str, ...] |
None =
None,
1663 ) -> dict[str, object] |
None:
1664 """Return the most-recent round-job for a round, optional status filter."""
1666 query =
"SELECT * FROM round_jobs WHERE round_no = ?"
1667 params: list[object] = [round_no]
1669 placeholders =
", ".join(
"?" for _
in statuses)
1670 query += f
" AND status IN ({placeholders})"
1671 params.extend(statuses)
1672 query +=
" ORDER BY created_at DESC, rowid DESC LIMIT 1"
1674 row = conn.execute(query, tuple(params)).fetchone()
1675 return None if row
is None else _row_dict(row)
1682 current_step: str |
None =
None,
1683 update_step: bool =
True,
1685 """Update a round-job's status (and optionally current_step)."""
1687 now = self.
clock.now_wall()
1691 "UPDATE round_jobs SET status = ?, current_step = ?, "
1692 "updated_at = ? WHERE job_id = ?",
1693 (status, current_step, now, job_id),
1697 "UPDATE round_jobs SET status = ?, updated_at = ? "
1699 (status, now, job_id),
1703 """Record the staging-branch HEAD snapshot for a round-job."""
1705 now = self.
clock.now_wall()
1709 "UPDATE round_jobs SET staging_branch_head_sha = ?, "
1710 "updated_at = ? WHERE job_id = ?",
1721 input_hash: str |
None =
None,
1722 post_step_head_sha: str |
None =
None,
1723 result_summary: str |
None =
None,
1724 error_summary: str |
None =
None,
1725 artifact_paths_json: str |
None =
None,
1726 started_at: str |
None =
None,
1727 finished_at: str |
None =
None,
1729 """Insert or update a single round-job step row."""
1734 "INSERT INTO round_job_steps(job_id, step_no, step_name, status, "
1735 "input_hash, post_step_head_sha, started_at, finished_at, "
1736 "result_summary, error_summary, artifact_paths_json) "
1737 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
1738 "ON CONFLICT(job_id, step_no) DO UPDATE SET "
1739 "step_name = excluded.step_name, status = excluded.status, "
1740 "input_hash = COALESCE(excluded.input_hash, "
1741 "round_job_steps.input_hash), "
1742 "post_step_head_sha = COALESCE(excluded.post_step_head_sha, "
1743 "round_job_steps.post_step_head_sha), "
1744 "started_at = COALESCE(excluded.started_at, "
1745 "round_job_steps.started_at), "
1746 "finished_at = COALESCE(excluded.finished_at, "
1747 "round_job_steps.finished_at), "
1748 "result_summary = COALESCE(excluded.result_summary, "
1749 "round_job_steps.result_summary), "
1750 "error_summary = excluded.error_summary, "
1751 "artifact_paths_json = COALESCE(excluded.artifact_paths_json, "
1752 "round_job_steps.artifact_paths_json)",
1764 artifact_paths_json,
1769 """Return all step rows for a round-job ordered by step number."""
1772 rows = conn.execute(
1774 "SELECT * FROM round_job_steps WHERE job_id = ? ORDER BY step_no",
1779 def get_round(self, round_no: int) -> dict[str, object] |
None:
1780 """Return a round row by number, or None."""
1784 "SELECT * FROM rounds WHERE round_no = ?",
1787 return None if row
is None else _row_dict(row)
1790 """Return round_plans rows for a round ordered by merge_order."""
1793 rows = conn.execute(
1795 "SELECT * FROM round_plans WHERE round_no = ? ORDER BY merge_order",
1804 ) -> dict[str, object] |
None:
1805 """Return a single round_plans row, or None."""
1809 "SELECT * FROM round_plans WHERE round_no = ? AND plan_id = ?",
1810 (round_no, plan_id),
1812 return None if row
is None else _row_dict(row)
1816 conn: sqlite3.Connection,
1817 round_row: Mapping[str, object],
1821 "INSERT INTO rounds("
1822 "round_no, status, cleanup_status, orchestrator_slot, opened_at, "
1823 "closed_at, final_commit_sha"
1824 ") VALUES (?, ?, ?, ?, ?, ?, ?) "
1825 "ON CONFLICT(round_no) DO UPDATE SET "
1826 "status = excluded.status, "
1827 "cleanup_status = excluded.cleanup_status, "
1828 "orchestrator_slot = excluded.orchestrator_slot, "
1829 "opened_at = excluded.opened_at, "
1830 "closed_at = excluded.closed_at, "
1831 "final_commit_sha = excluded.final_commit_sha",
1833 int(round_row[
"round_no"]),
1834 str(round_row[
"status"]),
1835 str(round_row[
"cleanup_status"]),
1836 str(round_row[
"orchestrator_slot"]),
1837 str(round_row[
"opened_at"]),
1845 conn: sqlite3.Connection,
1846 plan: Mapping[str, object],
1848 now = self.
clock.now_wall()
1851 "INSERT INTO plans("
1852 "plan_id, plan_number, title, plan_type, status, assigned_slot, "
1853 "queue_position, branch, worktree, body_path, body_sha256, "
1854 "body_observed_at, model_override, thinking_override, "
1855 "session_override, created_at, updated_at, ready_for_merge_at, "
1856 "completed_at, closed_round"
1857 ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
1858 "ON CONFLICT(plan_id) DO UPDATE SET "
1859 "plan_number = excluded.plan_number, "
1860 "title = excluded.title, "
1861 "plan_type = excluded.plan_type, "
1862 "status = excluded.status, "
1863 "assigned_slot = excluded.assigned_slot, "
1864 "queue_position = excluded.queue_position, "
1865 "branch = excluded.branch, "
1866 "worktree = excluded.worktree, "
1867 "body_path = excluded.body_path, "
1868 "body_sha256 = excluded.body_sha256, "
1869 "body_observed_at = excluded.body_observed_at, "
1870 "model_override = excluded.model_override, "
1871 "thinking_override = excluded.thinking_override, "
1872 "session_override = excluded.session_override, "
1873 "updated_at = excluded.updated_at, "
1874 "ready_for_merge_at = excluded.ready_for_merge_at, "
1875 "completed_at = excluded.completed_at, "
1876 "closed_round = excluded.closed_round",
1878 str(plan[
"plan_id"]),
1879 int(plan[
"plan_number"]),
1882 str(plan[
"status"]),
1887 str(plan[
"body_path"]),
1903 conn: sqlite3.Connection,
1904 plan: Mapping[str, object],
1909 "INSERT INTO plan_status_events("
1910 "plan_id, from_status, to_status, actor_slot, source, created_at, note"
1911 ") VALUES (?, NULL, ?, ?, 'migrate', ?, ?)",
1913 str(plan[
"plan_id"]),
1914 str(plan[
"status"]),
1916 self.
clock.now_wall(),
1923 conn: sqlite3.Connection,
1924 round_plan: Mapping[str, object],
1928 "INSERT INTO round_plans("
1929 "round_no, plan_id, merge_order, merge_commit_sha, test_result"
1930 ") VALUES (?, ?, ?, ?, ?) "
1931 "ON CONFLICT(round_no, plan_id) DO UPDATE SET "
1932 "merge_order = excluded.merge_order, "
1933 "merge_commit_sha = excluded.merge_commit_sha, "
1934 "test_result = excluded.test_result",
1936 int(round_plan[
"round_no"]),
1937 str(round_plan[
"plan_id"]),
1938 int(round_plan[
"merge_order"]),
1945 raise NotImplementedError(
"operation-log writing is implemented in plan 082")
1952 metadata: Mapping[str, object] |
None =
None,
1954 if ttl_seconds <= 0:
1955 raise ValueError(
"ttl_seconds must be positive")
1957 acquired_at = self.
clock.now_wall()
1958 expires_at = wall_plus_seconds(acquired_at, ttl_seconds)
1959 metadata_json =
None if metadata
is None else canonical_json(metadata)
1964 "INSERT INTO locks("
1965 "name, owner_slot, acquired_at, expires_at, metadata_json"
1966 ") VALUES (?, ?, ?, ?, ?)",
1967 (name, owner, acquired_at, expires_at, metadata_json),
1969 except sqlite3.IntegrityError
as exc:
1975 owner_slot=holder[
"owner_slot"],
1976 expires_at=holder[
"expires_at"],
1981 expires_at=expires_at,
1989 "SELECT owner_slot FROM locks WHERE name = ?",
1994 actual_owner = str(row[
"owner_slot"])
1995 if actual_owner != owner:
1999 actual_owner_slot=actual_owner,
2001 conn.execute(
"DELETE FROM locks WHERE name = ?", (name,))
2003 def renew_lock(self, name: str, owner: str, ttl_seconds: int) -> str |
None:
2004 if ttl_seconds <= 0:
2005 raise ValueError(
"ttl_seconds must be positive")
2006 expires_at = wall_plus_seconds(self.
clock.now_wall(), ttl_seconds)
2009 "SELECT owner_slot FROM locks WHERE name = ?",
2016 actual_owner_slot=
None,
2018 actual_owner = str(row[
"owner_slot"])
2019 if actual_owner != owner:
2023 actual_owner_slot=actual_owner,
2026 "UPDATE locks SET expires_at = ? WHERE name = ?",
2035 """Remove expired lock rows and return their names."""
2038 now = self.
clock.now_wall()
2041 for row
in conn.execute(
2043 "SELECT name FROM locks "
2044 "WHERE expires_at IS NOT NULL AND expires_at < ? "
2051 "DELETE FROM locks WHERE expires_at IS NOT NULL AND expires_at < ?",
2057 """Return the first active human operator slot, if one exists."""
2062 "SELECT slot FROM agents "
2063 "WHERE slot_kind = 'operator' AND active = 1 "
2064 "ORDER BY slot LIMIT 1"
2066 return None if row
is None else str(row[
"slot"])
2069 """Return an actor's slot kind or raise ``UnknownActorError``."""
2075 """Return credential rows needed for doctor fingerprint checks."""
2078 rows = conn.execute(
2080 "SELECT hmac_key_id, slot, secret_fingerprint, rotation_phase, "
2081 "revoked_at FROM agent_credentials ORDER BY slot, rotation_phase"
2085 "hmac_key_id": str(row[
"hmac_key_id"]),
2086 "slot": str(row[
"slot"]),
2087 "secret_fingerprint": str(row[
"secret_fingerprint"]),
2088 "rotation_phase": str(row[
"rotation_phase"]),
2089 "revoked_at": row[
"revoked_at"],
2094 def backup(self, target: str | Path) -> Path:
2095 target_path = Path(target)
2096 target_path.parent.mkdir(parents=
True, exist_ok=
True)
2097 with self.
connect()
as source, sqlite3.connect(str(target_path))
as dest:
2104 source_path = Path(source)
2105 if not source_path.is_file():
2106 raise FileNotFoundError(source_path)
2109 shutil.copy2(source_path, self.
path)
2113 errors: list[dict[str, object]] = []
2121 row[0]
for row
in conn.execute(
"PRAGMA integrity_check").fetchall()
2123 if integrity_rows != [
"ok"]:
2126 "kind":
"physical_corruption",
2127 "details": str(row),
2129 for row
in integrity_rows
2131 fk_rows = conn.execute(
"PRAGMA foreign_key_check").fetchall()
2136 except sqlite3.Error
as exc:
2137 errors.append({
"kind":
"physical_corruption",
"details": str(exc)})
2138 return {
"ok":
not errors,
"errors": errors}
2142 conn.execute(
"VACUUM")
2145 """Return the effective PRAGMA values for a fresh backend connection."""
2158 self.
path.with_name(f
"{self.path.name}-wal"),
2159 self.
path.with_name(f
"{self.path.name}-shm"),
2165 except sqlite3.Error:
2169 conn.execute(
"PRAGMA wal_checkpoint(TRUNCATE)").fetchall()
2170 except sqlite3.Error:
2178 sidecar.unlink(missing_ok=
True)
2179 except PermissionError:
2187 if pragmas[
"foreign_keys"] != 1:
2189 if str(pragmas[
"journal_mode"]).lower() !=
"wal":
2191 if int(pragmas[
"busy_timeout"]) < 5000:
2196 conn.execute(
"PRAGMA foreign_keys = ON")
2197 conn.execute(
"PRAGMA journal_mode = WAL")
2198 conn.execute(
"PRAGMA busy_timeout = 5000")
2202 "foreign_keys": conn.execute(
"PRAGMA foreign_keys").fetchone()[0],
2203 "journal_mode": conn.execute(
"PRAGMA journal_mode").fetchone()[0],
2204 "busy_timeout": conn.execute(
"PRAGMA busy_timeout").fetchone()[0],
2209 roadmap_schema = v0_3_roadmap.load_schema_sql()
2210 auth_events_schema = v0_3_1_auth_events.migration_sql()
2211 source_fields_schema = v0_3_2_roadmap_source_fields.migration_sql()
2212 suffix =
"\nCOMMIT;" if commit
else ""
2215 "BEGIN IMMEDIATE;\n"
2216 f
"{schema}\n{roadmap_schema}\n{auth_events_schema}\n"
2217 f
"{source_fields_schema}{suffix}"
2221 conn.execute(
"ROLLBACK")
2222 except sqlite3.Error:
2227 now = self.
clock.now_wall()
2230 "INSERT INTO schema_migrations(version, applied_at, description) "
2232 (SCHEMA_VERSION, now, SCHEMA_DESCRIPTION),
2236 "INSERT OR IGNORE INTO schema_migrations(version, applied_at, description) "
2239 v0_3_roadmap.ROADMAP_SCHEMA_VERSION,
2241 v0_3_roadmap.ROADMAP_SCHEMA_DESCRIPTION,
2246 "INSERT OR IGNORE INTO schema_migrations(version, applied_at, description) "
2249 v0_3_1_auth_events.AUTH_EVENTS_SCHEMA_VERSION,
2251 v0_3_1_auth_events.AUTH_EVENTS_SCHEMA_DESCRIPTION,
2256 "INSERT OR IGNORE INTO schema_migrations(version, applied_at, description) "
2259 v0_3_2_roadmap_source_fields.ROADMAP_SOURCE_FIELDS_SCHEMA_VERSION,
2261 v0_3_2_roadmap_source_fields.ROADMAP_SOURCE_FIELDS_SCHEMA_DESCRIPTION,
2266 "INSERT INTO workspaces("
2267 "workspace_id, workspace_root, git_common_dir, registry_path, "
2268 "checkpoint_path, created_at, last_seen_at, active"
2269 ") VALUES (?, ?, ?, ?, ?, ?, ?, 1)",
2272 seed.workspace_root,
2273 seed.git_common_dir,
2275 seed.checkpoint_path,
2282 "INSERT INTO registry_meta("
2283 "id, registry_id, workspace_id, schema_version, state_version, "
2284 "last_checkpoint_id, created_at, updated_at"
2285 ") VALUES (1, ?, ?, ?, 0, NULL, ?, ?)",
2286 (seed.registry_id, seed.workspace_id, SCHEMA_VERSION, now, now),
2289 conn.execute(
"INSERT INTO plan_seq(id, next_number) VALUES (1, 1)")
2292 "INSERT INTO agents("
2293 "slot, slot_kind, identity_kind, model_class, "
2294 "orchestrator_eligible, active, created_at"
2295 ") VALUES (?, 'operator', 'human', 'human', 0, 1, ?)",
2296 (seed.operator_slot, now),
2300 "INSERT INTO orchestration_state("
2301 "id, orchestrator_slot, handover_status, ready_to_handover, "
2302 "updated_at, updated_by"
2303 ") VALUES (1, NULL, 'stable', 0, ?, ?)",
2304 (now, seed.operator_slot),
2308 "INSERT INTO leases("
2309 "id, holder_slot, lease_expires_at, heartbeat_interval_sec, "
2311 ") VALUES (1, NULL, NULL, 30, NULL)"
2315 "INSERT INTO settings(key, value, updated_at, updated_by) "
2316 "VALUES (?, ?, ?, ?)",
2318 (key, value, now, seed.operator_slot)
2324 "INSERT INTO agent_credentials("
2325 "hmac_key_id, slot, secret_fingerprint, created_at, rotation_phase"
2326 ") VALUES (?, ?, ?, ?, 'active')",
2330 seed.secret_fingerprint,
2338 "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'"
2344 conn: sqlite3.Connection,
2345 errors: list[dict[str, object]],
2347 table_row = conn.execute(
2349 "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?",
2352 if table_row
is None:
2354 trigger_rows = conn.execute(
2355 "SELECT name FROM sqlite_master WHERE type = 'trigger'"
2357 triggers = {str(row[0])
for row
in trigger_rows}
2358 required = {
"operation_log_no_update",
"operation_log_no_delete"}
2359 missing = sorted(required - triggers)
2363 "kind":
"append_only_trigger",
2364 "details": f
"missing append-only triggers: {', '.join(missing)}",
2368 roadmap_table = conn.execute(
2370 "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?",
2371 (
"roadmap_item_events",),
2373 if roadmap_table
is None:
2375 roadmap_required = {
2376 "roadmap_item_events_no_update",
2377 "roadmap_item_events_no_delete",
2379 roadmap_missing = sorted(roadmap_required - triggers)
2383 "kind":
"append_only_trigger",
2384 "details":
"missing roadmap append-only triggers: "
2385 +
", ".join(roadmap_missing),
2386 "missing": roadmap_missing,
2391 """Apply the v0.3 roadmap extension to initialized registries."""
2409 "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?",
2414 now = self.
clock.now_wall()
2416 v0_3_roadmap.current_user_version(conn)
2417 >= v0_3_roadmap.ROADMAP_SCHEMA_VERSION
2418 and v0_3_roadmap.tables_exist(conn)
2421 "BEGIN IMMEDIATE;\n"
2422 + v0_3_roadmap.load_schema_sql()
2427 "INSERT OR IGNORE INTO schema_migrations("
2428 "version, applied_at, description"
2429 ") VALUES (?, ?, ?)",
2431 v0_3_roadmap.ROADMAP_SCHEMA_VERSION,
2433 v0_3_roadmap.ROADMAP_SCHEMA_DESCRIPTION,
2436 if not v0_3_1_auth_events.is_applied(conn):
2437 v0_3_1_auth_events.apply(conn)
2440 "INSERT OR IGNORE INTO schema_migrations("
2441 "version, applied_at, description"
2442 ") VALUES (?, ?, ?)",
2444 v0_3_1_auth_events.AUTH_EVENTS_SCHEMA_VERSION,
2446 v0_3_1_auth_events.AUTH_EVENTS_SCHEMA_DESCRIPTION,
2450 v0_3_1_auth_events.current_user_version(conn)
2451 < v0_3_1_auth_events.AUTH_EVENTS_SCHEMA_VERSION
2454 f
"PRAGMA user_version = "
2455 f
"{v0_3_1_auth_events.AUTH_EVENTS_SCHEMA_VERSION}"
2457 if not v0_3_2_roadmap_source_fields.is_applied(conn):
2458 v0_3_2_roadmap_source_fields.apply(conn)
2461 "INSERT OR IGNORE INTO schema_migrations("
2462 "version, applied_at, description"
2463 ") VALUES (?, ?, ?)",
2465 v0_3_2_roadmap_source_fields.ROADMAP_SOURCE_FIELDS_SCHEMA_VERSION,
2467 v0_3_2_roadmap_source_fields.ROADMAP_SOURCE_FIELDS_SCHEMA_DESCRIPTION,
2471 v0_3_1_auth_events.current_user_version(conn)
2472 < v0_3_2_roadmap_source_fields.ROADMAP_SOURCE_FIELDS_SCHEMA_VERSION
2475 f
"PRAGMA user_version = "
2476 f
"{v0_3_2_roadmap_source_fields.ROADMAP_SOURCE_FIELDS_SCHEMA_VERSION}"
2482 operation_type: str,
2483 payload: Mapping[str, object],
2484 payload_version: int,
2486 if not isinstance(operation_type, str)
or not operation_type.strip():
2488 operation_type=str(operation_type),
2489 message=
"operation_type must be a non-empty string",
2491 if not isinstance(payload, Mapping):
2493 operation_type=operation_type,
2494 message=
"payload must be a mapping",
2496 if "version" not in payload:
2498 operation_type=operation_type,
2499 message=
"payload must include version",
2501 supplied_version = payload[
"version"]
2502 if not isinstance(supplied_version, int)
or supplied_version != payload_version:
2504 operation_type=operation_type,
2505 payload_version=payload_version,
2506 supplied_version=supplied_version,
2509 return canonical_json(payload)
2510 except (TypeError, ValueError)
as exc:
2512 operation_type=operation_type,
2518 "SELECT slot_kind FROM agents WHERE slot = ?",
2523 return str(row[
"slot_kind"])
2527 conn: sqlite3.Connection,
2532 "SELECT hmac_key_id FROM agent_credentials "
2533 "WHERE slot = ? AND revoked_at IS NULL "
2534 "ORDER BY CASE rotation_phase "
2535 "WHEN 'rotating_new' THEN 0 "
2536 "WHEN 'active' THEN 1 "
2537 "WHEN 'rotating_old' THEN 2 "
2538 "ELSE 3 END LIMIT 1",
2541 return None if row
is None else str(row[
"hmac_key_id"])
2545 conn: sqlite3.Connection,
2546 operation_type: str,
2550 if not operation_type.startswith(
"round.")
or slot_kind !=
"orchestrator":
2553 holder = state[
"orchestrator_slot"]
2554 status = str(state[
"handover_status"])
2555 outgoing = state[
"handover_outgoing"]
2556 incoming = state[
"handover_incoming"]
2557 if status ==
"stable":
2560 "UPDATE orchestration_state SET orchestrator_slot = ?, "
2561 "updated_at = ?, updated_by = ? WHERE id = 1",
2562 (actor_slot, self.
clock.now_wall(), actor_slot),
2565 if str(holder) != actor_slot:
2567 state=
"handover_in_flight",
2568 actor_slot=actor_slot,
2569 operation_type=operation_type,
2572 if outgoing
is not None and str(outgoing) == actor_slot:
2575 "pending_not_accepted"
2576 if incoming
is not None and str(incoming) == actor_slot
2577 else "handover_in_flight"
2581 actor_slot=actor_slot,
2582 operation_type=operation_type,
2588 "SELECT orchestrator_slot, handover_status, handover_outgoing, "
2589 "handover_incoming, handover_requested_at FROM orchestration_state "
2598 conn: sqlite3.Connection,
2599 parent_operation_id: int,
2600 operation_uuid: str,
2602 seen: set[int] = set()
2603 current_id: int |
None = parent_operation_id
2605 while current_id
is not None:
2606 if current_id
in seen
or depth >= _MAX_PARENT_DEPTH:
2608 seen.add(current_id)
2610 "SELECT operation_uuid, parent_operation_id "
2611 "FROM operation_log WHERE id = ?",
2616 if row[
"operation_uuid"] == operation_uuid:
2618 current_id = row[
"parent_operation_id"]
2623 "SELECT state_version FROM registry_meta WHERE id = 1"
2627 return int(row[
"state_version"])
2631 return conn.execute(
2633 "SELECT owner_slot, expires_at FROM locks WHERE name = ?",
2641 if not isinstance(value, list):
2642 raise TypeError(
"migration projection sections must be lists")
2643 rows: list[Mapping[str, object]] = []
2645 if not isinstance(item, Mapping):
2646 raise TypeError(
"migration projection rows must be mappings")
2652 value = row.get(key)
2653 if not isinstance(value, Mapping):
2654 raise TypeError(f
"migration projection row missing mapping: {key}")
2659 return {key: row[key]
for key
in row.keys()}
2666 return text
if text
else None
2670 return None if value
is None else int(value)
2674 return 1
if bool(value)
else 0
2678 if not item_id.startswith(
"M"):
2680 if "." not in item_id:
2682 return item_id.split(
".", 1)[0]
2686 if "." not in phase_code:
2688 return phase_code.split(
".", 1)[0]
2692 digits =
"".join(ch
if ch.isdigit()
else " " for ch
in phase_code).split()
2695 value = int(digits[0]) * 1000
2697 value += int(digits[1])
2702 major = version_id.removeprefix(
"v").split(
".", 1)[0]
2707 text = era_id.removeprefix(
"v")
2708 return int(text)
if text.isdigit()
else 999
2713 Path(__file__).resolve().parents[2]
2717 /
"REGISTRY_SCHEMA_V0_2_3.sql"
2728 if line.lstrip().upper().startswith(_PRAGMA_PREFIX):
2731 return "\n".join(lines) +
"\n"
2736 table =
"" if len(values) < 1
else str(values[0])
2737 row_id =
None if len(values) < 2
else values[1]
2738 parent =
"" if len(values) < 3
else str(values[2])
2739 fkid =
None if len(values) < 4
else values[3]
2741 "kind":
"fk_violation",
2746 "details": repr(values),
2752 (
"workflow_version",
"1"),
2753 (
"renderer_version",
"1"),
2754 (
"roadmap_version",
"1"),
2755 (
"app_version", app_version),
2756 (
"hmac_max_skew_seconds",
"300"),
2757 (
"hmac_rotation_grace_seconds",
"86400"),
2758 (
"round_close_timeout_seconds",
"3600"),
2759 (
"round_auto_batch_limit",
"5"),
2760 (
"handover_drain_timeout_seconds",
"300"),
2761 (
"grant_default_ttl_seconds",
"3600"),
2762 (
"max_replay_rows_for_merge",
"10000"),
2763 (
"oplog_retention_days",
"90"),
2764 (
"auth_events_retention_days",
"30"),
2765 (
"auth_failure_alert_threshold_per_hour",
"10"),
AbstractContextManager[Any] transaction(self, str mode="immediate")
str|None renew_lock(self, str name, str owner, int ttl_seconds)
None release_lock(self, str name, str owner)
None _upsert_migration_round_plan(self, sqlite3.Connection conn, Mapping[str, object] round_plan)
None _project_round_test(self, sqlite3.Connection conn, Mapping[str, object] payload)
int _table_count(self, sqlite3.Connection conn)
bool _plan_exists(self, sqlite3.Connection conn, str plan_id)
str|None active_operator_slot(self)
None upsert_round_job_step(self, *, str job_id, int step_no, str step_name, str status, str|None input_hash=None, str|None post_step_head_sha=None, str|None result_summary=None, str|None error_summary=None, str|None artifact_paths_json=None, str|None started_at=None, str|None finished_at=None)
int _roadmap_phase_id(self, sqlite3.Connection conn, str phase_code, *, str ts, str label)
None initialize(self, RegistrySeed seed)
None _project_round_lint(self, sqlite3.Connection conn, Mapping[str, object] payload)
dict[str, object] _read_pragmas(self, sqlite3.Connection conn)
None _delete_sqlite_sidecars(self)
dict[str, object] integrity_check(self)
dict[str, object] migration_snapshot(self)
None _upsert_migration_agent(self, sqlite3.Connection conn, Mapping[str, object] agent)
dict[str, object]|None get_round_plan(self, int round_no, str plan_id)
str _actor_slot_kind(self, sqlite3.Connection conn, str actor_slot)
None _run_schema_script(self, sqlite3.Connection conn, *, bool commit)
list[str] migration_null_round_backfill_violations(self)
None _project_roadmap_item_create(self, sqlite3.Connection conn, Mapping[str, object] payload, str actor_slot, str ts, int source_operation_id)
None _apply_domain_projection(self, sqlite3.Connection conn, str operation_type, Mapping[str, object] payload, *, str actor_slot, str ts, int|None related_round, int source_operation_id)
None _project_round_merge(self, sqlite3.Connection conn, Mapping[str, object] payload, str ts)
None _project_round_cleanup(self, sqlite3.Connection conn, Mapping[str, object] payload, str ts)
None _check_append_only_triggers(self, sqlite3.Connection conn, list[dict[str, object]] errors)
None _insert_roadmap_event(self, sqlite3.Connection conn, str item_id, str event_kind, *, str|None from_value_json, str|None to_value_json, str|None in_version_id, int source_operation_id, str ts, str|None note)
None _project_round_ff_main(self, sqlite3.Connection conn, Mapping[str, object] payload)
list[dict[str, object]] credential_fingerprints(self)
dict[str, object]|None get_round(self, int round_no)
None _auto_migrate_in_conn(self, sqlite3.Connection conn)
str actor_slot_kind(self, str actor_slot)
list[dict[str, object]] list_round_job_steps(self, str job_id)
None _project_queue_complete(self, sqlite3.Connection conn, Mapping[str, object] payload, str ts, int|None related_round)
None _project_plan_set_status(self, sqlite3.Connection conn, Mapping[str, object] payload, str actor_slot, str ts)
None _shift_queue_positions_up(self, sqlite3.Connection conn, str slot, int start_position, *, str|None exclude_plan_id=None)
None _project_round_consolidate(self, sqlite3.Connection conn, Mapping[str, object] payload)
dict[str, object] connection_pragmas(self)
None _assert_capabilities(self)
LockHandle acquire_lock(self, str name, str owner, int ttl_seconds, Mapping[str, object]|None metadata=None)
None set_round_job_head_sha(self, str job_id, str|None sha)
None _assert_no_parent_cycle(self, sqlite3.Connection conn, int parent_operation_id, str operation_uuid)
None _project_roadmap_section_upsert(self, sqlite3.Connection conn, Mapping[str, object] payload)
None __init__(self, str|Path path, Clock|None clock=None, *, bool check_capabilities=True)
None _project_queue_dequeue(self, sqlite3.Connection conn, Mapping[str, object] payload, str ts)
None _project_roadmap_phase_complete(self, sqlite3.Connection conn, Mapping[str, object] payload, str ts, int source_operation_id)
None _remove_queue_position(self, sqlite3.Connection conn, str slot, int position, str plan_id)
list[dict[str, object]] list_round_plans(self, int round_no)
None _apply_connection_pragmas(self, sqlite3.Connection conn)
object append_operation_log(self, Mapping[str, object] entry)
None _project_plan_reimport(self, sqlite3.Connection conn, Mapping[str, object] payload, str ts)
sqlite3.Row|None _lock_holder(self, str name)
None _upsert_migration_plan(self, sqlite3.Connection conn, Mapping[str, object] plan)
None _project_roadmap_item_set_status(self, sqlite3.Connection conn, Mapping[str, object] payload, str ts, int source_operation_id)
None _project_round_abort(self, sqlite3.Connection conn, Mapping[str, object] payload, str ts)
None _project_queue_enqueue(self, sqlite3.Connection conn, Mapping[str, object] payload, str ts)
None _project_plan_create(self, sqlite3.Connection conn, Mapping[str, object] payload, str ts)
None _insert_migration_status_event(self, sqlite3.Connection conn, Mapping[str, object] plan, str actor_slot)
None _checkpoint_for_restore(self)
None _project_round_open(self, sqlite3.Connection conn, Mapping[str, object] payload, str actor_slot, str ts)
None set_round_job_status(self, str job_id, *, str status, str|None current_step=None, bool update_step=True)
sqlite3.Row _orchestration_state(self, sqlite3.Connection conn)
Path backup(self, str|Path target)
None release_lock(self, str name, str owner)
int _current_state_version(self, sqlite3.Connection conn)
None _auto_migrate_existing_database(self)
None _project_roadmap_plan_link_create(self, sqlite3.Connection conn, Mapping[str, object] payload, str actor_slot, str ts)
list[str] startup_sweep_with_names(self)
str|None _active_hmac_key_id(self, sqlite3.Connection conn, str actor_slot)
None _roadmap_era(self, sqlite3.Connection conn, str era_id, str ts)
OperationLogRow _execute_domain_command_in_conn(self, sqlite3.Connection conn, str operation_type, str actor_slot, Mapping[str, object] payload, *, str actor_type, int payload_version=1, str|None command=None, int|None parent_operation_id=None, str|None related_plan_id=None, int|None related_round=None, str|None operation_uuid=None, str|None hmac_key_id=None, bool system_actor=False)
str|None renew_lock(self, str name, str owner, int ttl_seconds)
None _seed_initial_rows(self, sqlite3.Connection conn, RegistrySeed seed)
dict[str, object]|None latest_round_job(self, int round_no, *, tuple[str,...]|None statuses=None)
None _project_roadmap_item_add_dependency(self, sqlite3.Connection conn, Mapping[str, object] payload, str actor_slot, str ts, int source_operation_id)
None create_round_job(self, *, str job_id, int round_no, str created_by, str|None staging_branch_head_sha=None, str|None deadline_at=None)
tuple[Path, Path] _sqlite_sidecars(self)
None _upsert_migration_round(self, sqlite3.Connection conn, Mapping[str, object] round_row)
None _project_roadmap_version_bump(self, sqlite3.Connection conn, Mapping[str, object] payload, str ts)
None _project_plan_supersede(self, sqlite3.Connection conn, Mapping[str, object] payload, str ts)
str _validate_payload(self, str operation_type, Mapping[str, object] payload, int payload_version)
dict[str, object]|None get_round_job(self, str job_id)
Iterator[sqlite3.Connection] transaction(self, str mode="immediate")
None _project_queue_move(self, sqlite3.Connection conn, Mapping[str, object] payload, str ts)
None _project_handover_start(self, sqlite3.Connection conn, Mapping[str, object] payload, str actor_slot, str ts)
tuple[str, int]|None _plan_queue_location(self, sqlite3.Connection conn, str plan_id)
None _project_handover_complete(self, sqlite3.Connection conn, Mapping[str, object] payload, str actor_slot, str ts)
sqlite3.Connection connect(self)
None _assert_handover_allows(self, sqlite3.Connection conn, str operation_type, str actor_slot, str slot_kind)
None restore(self, str|Path source)
int _next_round_merge_order(self, sqlite3.Connection conn, int round_no)
OperationLogRow execute_domain_command(self, str operation_type, str actor_slot, Mapping[str, object] payload, *, str actor_type, int payload_version=1, str|None command=None, int|None parent_operation_id=None, str|None related_plan_id=None, int|None related_round=None, str|None operation_uuid=None, str|None hmac_key_id=None, bool system_actor=False)
object read_model(self, str name, Mapping[str, object]|None filters=None)
None _project_plan_assign(self, sqlite3.Connection conn, Mapping[str, object] payload, str ts)
list[OperationLogRow] apply_migration_projection(self, Mapping[str, object] projection, *, str actor_slot, str actor_type)
Mapping[str, object] _required_mapping(Mapping[str, object] row, str key)
int _era_sort_order(str era_id)
str|None _parent_phase_code(str phase_code)
int|None _optional_int(object value)
int _phase_sort_order(str phase_code)
list[Mapping[str, object]] _mapping_list(object value)
str load_canonical_schema_sql()
str|None _optional_str(object value)
dict[str, object] _row_dict(sqlite3.Row row)
list[tuple[str, str]] _default_settings(str app_version)
str canonical_schema_sql_without_pragmas()
str _era_id_for_version(str version_id)
Path canonical_schema_path()
str|None _phase_code_for_item_id(str item_id)
dict[str, object] _foreign_key_error(sqlite3.Row row)
int _bool_int(object value)