"""SQLite persistence for governance-specific semantic state.""" from __future__ import annotations import json import sqlite3 from pathlib import Path from typing import Iterable from .governance_models import EcosystemGovernancePortfolio, PlatformGovernanceCard, GovernanceCheckResult from .human_readiness_registry import ReadinessRegistry, ReadinessRegistryEntry from .mcp_contract import McpContractCoverage, McpContractReport from .models import as_plain_data, utc_now from .round_assurance import AssuranceSuite, AssuranceCase from .runtime_budget import RoundLineBudget, RepositoryLineBudget from .service_order_lifecycle import RoundExecutionPackage, OrderLifecycleDecision from .workflow_registry import WorkflowPortfolio, WorkflowEvaluation from .governance_scenarios import ScenarioPortfolio, ScenarioEvaluation GOVERNANCE_SCHEMA = """ CREATE TABLE IF NOT EXISTS governance_cards ( id INTEGER PRIMARY KEY AUTOINCREMENT, platform_id TEXT UNIQUE NOT NULL, status_label TEXT NOT NULL, governance_score INTEGER NOT NULL, human_score INTEGER NOT NULL, maturity TEXT NOT NULL, blocker_count INTEGER NOT NULL, warning_count INTEGER NOT NULL, payload_json TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS governance_checks ( id INTEGER PRIMARY KEY AUTOINCREMENT, check_key TEXT UNIQUE NOT NULL, platform_id TEXT NOT NULL, check_id TEXT NOT NULL, axis TEXT NOT NULL, domain TEXT NOT NULL, status TEXT NOT NULL, severity TEXT NOT NULL, score INTEGER NOT NULL, next_action TEXT NOT NULL, payload_json TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS readiness_registry ( id INTEGER PRIMARY KEY AUTOINCREMENT, entry_id TEXT UNIQUE NOT NULL, platform_id TEXT NOT NULL, profile_id TEXT NOT NULL, human_score INTEGER NOT NULL, governance_score INTEGER NOT NULL, status TEXT NOT NULL, recommended_action TEXT NOT NULL, payload_json TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS workflow_evaluations ( id INTEGER PRIMARY KEY AUTOINCREMENT, workflow_id TEXT UNIQUE NOT NULL, status TEXT NOT NULL, score INTEGER NOT NULL, passed_steps INTEGER NOT NULL, total_steps INTEGER NOT NULL, payload_json TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS scenario_evaluations ( id INTEGER PRIMARY KEY AUTOINCREMENT, scenario_id TEXT UNIQUE NOT NULL, status TEXT NOT NULL, score INTEGER NOT NULL, assertion_count INTEGER NOT NULL, failed_assertion_count INTEGER NOT NULL, payload_json TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS assurance_cases ( id INTEGER PRIMARY KEY AUTOINCREMENT, case_id TEXT UNIQUE NOT NULL, passed INTEGER NOT NULL, severity TEXT NOT NULL, required INTEGER NOT NULL, title TEXT NOT NULL, next_action TEXT NOT NULL, payload_json TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS lifecycle_decisions ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id TEXT UNIQUE NOT NULL, final_status TEXT NOT NULL, platform_id TEXT NOT NULL, pending_count INTEGER NOT NULL, resulting_candidate_count INTEGER NOT NULL, payload_json TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS line_budgets ( id INTEGER PRIMARY KEY AUTOINCREMENT, repo_name TEXT UNIQUE NOT NULL, exists_flag INTEGER NOT NULL, files_seen INTEGER NOT NULL, files_counted INTEGER NOT NULL, code_lines INTEGER NOT NULL, technical_lines INTEGER NOT NULL, warnings_json TEXT NOT NULL, payload_json TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS mcp_contracts ( id INTEGER PRIMARY KEY AUTOINCREMENT, contract_id TEXT UNIQUE NOT NULL, kind TEXT NOT NULL, platform_id TEXT NOT NULL, profile_id TEXT NOT NULL, tool_id TEXT NOT NULL, status TEXT NOT NULL, truth_state TEXT NOT NULL, score INTEGER NOT NULL, same_source INTEGER NOT NULL, source_payload_hash TEXT NOT NULL, source_records_hash TEXT NOT NULL, blocker_count INTEGER NOT NULL, next_action TEXT NOT NULL, payload_json TEXT NOT NULL, updated_at TEXT NOT NULL ); """ def ensure_governance_schema(conn: sqlite3.Connection) -> None: conn.executescript(GOVERNANCE_SCHEMA) def payload(value: object) -> str: return json.dumps(as_plain_data(value), ensure_ascii=False, sort_keys=True) def upsert_governance_card(conn: sqlite3.Connection, card: PlatformGovernanceCard, now: str) -> None: conn.execute( """ INSERT INTO governance_cards ( platform_id, status_label, governance_score, human_score, maturity, blocker_count, warning_count, payload_json, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(platform_id) DO UPDATE SET status_label=excluded.status_label, governance_score=excluded.governance_score, human_score=excluded.human_score, maturity=excluded.maturity, blocker_count=excluded.blocker_count, warning_count=excluded.warning_count, payload_json=excluded.payload_json, updated_at=excluded.updated_at """, ( card.platform_id, card.status_label, card.governance_score, card.human_score, card.maturity.value, len(card.blockers), len(card.warnings), payload(card), now, ), ) def upsert_governance_check(conn: sqlite3.Connection, check: GovernanceCheckResult, now: str) -> None: key = f"{check.platform_id}:{check.check_id}" conn.execute( """ INSERT INTO governance_checks ( check_key, platform_id, check_id, axis, domain, status, severity, score, next_action, payload_json, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(check_key) DO UPDATE SET axis=excluded.axis, domain=excluded.domain, status=excluded.status, severity=excluded.severity, score=excluded.score, next_action=excluded.next_action, payload_json=excluded.payload_json, updated_at=excluded.updated_at """, ( key, check.platform_id, check.check_id, check.axis.value, check.domain.value, check.status.value, check.severity.value, check.score, check.next_action, payload(check), now, ), ) def upsert_registry_entry(conn: sqlite3.Connection, entry: ReadinessRegistryEntry, now: str) -> None: conn.execute( """ INSERT INTO readiness_registry ( entry_id, platform_id, profile_id, human_score, governance_score, status, recommended_action, payload_json, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(entry_id) DO UPDATE SET human_score=excluded.human_score, governance_score=excluded.governance_score, status=excluded.status, recommended_action=excluded.recommended_action, payload_json=excluded.payload_json, updated_at=excluded.updated_at """, ( entry.entry_id, entry.platform_id, entry.profile_id, entry.human_score, entry.governance_score, entry.status, entry.recommended_action, payload(entry), now, ), ) def upsert_workflow(conn: sqlite3.Connection, evaluation: WorkflowEvaluation, now: str) -> None: conn.execute( """ INSERT INTO workflow_evaluations ( workflow_id, status, score, passed_steps, total_steps, payload_json, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(workflow_id) DO UPDATE SET status=excluded.status, score=excluded.score, passed_steps=excluded.passed_steps, total_steps=excluded.total_steps, payload_json=excluded.payload_json, updated_at=excluded.updated_at """, (evaluation.workflow_id, evaluation.status, evaluation.score, evaluation.passed_steps, evaluation.total_steps, payload(evaluation), now), ) def upsert_scenario(conn: sqlite3.Connection, evaluation: ScenarioEvaluation, now: str) -> None: failed = sum(1 for item in evaluation.assertion_results if not item.passed) conn.execute( """ INSERT INTO scenario_evaluations ( scenario_id, status, score, assertion_count, failed_assertion_count, payload_json, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(scenario_id) DO UPDATE SET status=excluded.status, score=excluded.score, assertion_count=excluded.assertion_count, failed_assertion_count=excluded.failed_assertion_count, payload_json=excluded.payload_json, updated_at=excluded.updated_at """, (evaluation.scenario_id, evaluation.status, evaluation.score, len(evaluation.assertion_results), failed, payload(evaluation), now), ) def upsert_assurance_case(conn: sqlite3.Connection, case: AssuranceCase, now: str) -> None: conn.execute( """ INSERT INTO assurance_cases ( case_id, passed, severity, required, title, next_action, payload_json, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(case_id) DO UPDATE SET passed=excluded.passed, severity=excluded.severity, required=excluded.required, title=excluded.title, next_action=excluded.next_action, payload_json=excluded.payload_json, updated_at=excluded.updated_at """, (case.case_id, 1 if case.passed else 0, case.severity, 1 if case.required else 0, case.title, case.next_action, payload(case), now), ) def upsert_lifecycle_decision(conn: sqlite3.Connection, decision: OrderLifecycleDecision, now: str) -> None: conn.execute( """ INSERT INTO lifecycle_decisions ( order_id, final_status, platform_id, pending_count, resulting_candidate_count, payload_json, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(order_id) DO UPDATE SET final_status=excluded.final_status, platform_id=excluded.platform_id, pending_count=excluded.pending_count, resulting_candidate_count=excluded.resulting_candidate_count, payload_json=excluded.payload_json, updated_at=excluded.updated_at """, ( decision.order.order_id, decision.final_status.value, decision.platform_id, len(decision.pending_items), len(decision.resulting_candidates), payload(decision), now, ), ) def upsert_line_budget(conn: sqlite3.Connection, repo: RepositoryLineBudget, now: str) -> None: conn.execute( """ INSERT INTO line_budgets ( repo_name, exists_flag, files_seen, files_counted, code_lines, technical_lines, warnings_json, payload_json, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(repo_name) DO UPDATE SET exists_flag=excluded.exists_flag, files_seen=excluded.files_seen, files_counted=excluded.files_counted, code_lines=excluded.code_lines, technical_lines=excluded.technical_lines, warnings_json=excluded.warnings_json, payload_json=excluded.payload_json, updated_at=excluded.updated_at """, ( repo.repo_name, 1 if repo.exists else 0, repo.files_seen, repo.files_counted, repo.code_lines, repo.technical_lines, json.dumps(list(repo.warnings), ensure_ascii=False), payload(repo), now, ), ) def upsert_mcp_contract(conn: sqlite3.Connection, coverage: McpContractCoverage, now: str) -> None: conn.execute( """ INSERT INTO mcp_contracts ( contract_id, kind, platform_id, profile_id, tool_id, status, truth_state, score, same_source, source_payload_hash, source_records_hash, blocker_count, next_action, payload_json, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(contract_id) DO UPDATE SET kind=excluded.kind, platform_id=excluded.platform_id, profile_id=excluded.profile_id, tool_id=excluded.tool_id, status=excluded.status, truth_state=excluded.truth_state, score=excluded.score, same_source=excluded.same_source, source_payload_hash=excluded.source_payload_hash, source_records_hash=excluded.source_records_hash, blocker_count=excluded.blocker_count, next_action=excluded.next_action, payload_json=excluded.payload_json, updated_at=excluded.updated_at """, ( coverage.contract_id, coverage.kind.value, coverage.platform_id, coverage.profile_id, coverage.tool_id, coverage.status.value, coverage.truth_state.value, coverage.score, 1 if coverage.same_source else 0, coverage.source_payload_hash, coverage.source_records_hash, len(coverage.blockers), coverage.next_action, payload(coverage), now, ), ) def write_governance_semantic_state( sqlite_path: Path, portfolio: EcosystemGovernancePortfolio, registry: ReadinessRegistry, workflows: WorkflowPortfolio, scenarios: ScenarioPortfolio, assurance: AssuranceSuite | None = None, lifecycle: RoundExecutionPackage | None = None, budget: RoundLineBudget | None = None, mcp_contract_report: McpContractReport | None = None, ) -> None: sqlite_path.parent.mkdir(parents=True, exist_ok=True) now = utc_now() with sqlite3.connect(sqlite_path) as conn: ensure_governance_schema(conn) for card in portfolio.cards: upsert_governance_card(conn, card, now) for check in card.checks: upsert_governance_check(conn, check, now) for entry in registry.entries: upsert_registry_entry(conn, entry, now) for evaluation in workflows.evaluations: upsert_workflow(conn, evaluation, now) for evaluation in scenarios.evaluations: upsert_scenario(conn, evaluation, now) if assurance is not None: for case in assurance.cases: upsert_assurance_case(conn, case, now) if lifecycle is not None: for decision in lifecycle.decisions: upsert_lifecycle_decision(conn, decision, now) if budget is not None: for repo in budget.repositories: upsert_line_budget(conn, repo, now) if mcp_contract_report is not None: for coverage in mcp_contract_report.coverage: upsert_mcp_contract(conn, coverage, now) conn.commit() def governance_table_counts(sqlite_path: Path) -> dict[str, int]: tables = ( "governance_cards", "governance_checks", "readiness_registry", "workflow_evaluations", "scenario_evaluations", "assurance_cases", "lifecycle_decisions", "line_budgets", "mcp_contracts", ) if not sqlite_path.exists(): return {table: 0 for table in tables} with sqlite3.connect(sqlite_path) as conn: ensure_governance_schema(conn) return {table: int(conn.execute(f"SELECT COUNT(*) FROM {table}").fetchone()[0]) for table in tables}