459 lines
16 KiB
Python
459 lines
16 KiB
Python
"""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}
|