#!/usr/bin/env python3 """Apply an approved kb_stage proposal into canonical public.* rows. Stage 2 of the KB apply pipeline: approve -> APPLY -> render -> surface. Governance boundary ------------------- This tool connects to Postgres as the narrow ``kb_apply`` login role, never as superuser. ``kb_apply`` can insert reviewed claim/source/reasoning-tool rows and write the strategy/evidence/edge tables required by supported contracts. It has SELECT-only access to ``kb_stage.kb_proposals``; payload-bound SECURITY DEFINER functions are the only way it can lock and finish an approved ledger row. It holds no DELETE or DDL and no write access to unrelated tables (agents, budgets, personas, ...). This enforces "agents propose, but do not self-apply" at the database boundary rather than by convention. The tool is an operator, invoked by a human/reviewer after approval; it is deliberately not part of the agent runtime. Flow ---- 1. Read the proposal (must be ``status='approved'``). 2. Dispatch by ``proposal_type`` to build the canonical write from a strict ``apply_payload`` contract carried on the proposal payload. 3. Emit a single transaction: canonical writes, then a DO block that flips the ledger to ``applied`` asserting rowcount=1 (the concurrent double-apply guard), stamps ``applied_by_agent_id`` as a real FK, and verifies invariants (RAISE on violation -> rollback), COMMIT. Prerequisites (one-time, superuser): scripts/kb_apply_prereqs.sql bootstraps the ``kb-apply`` service-agent row, grants ``kb_apply`` SELECT on public.agents, and ensures the one-active-strategy unique index. Run it before the first apply. ``--dry-run`` prints the exact SQL and does not connect for writes. Contract (v2) ------------- Freeform Leo eval packets are NOT applied directly. Each proposal must carry a strict ``apply_payload`` object; a separate normalizer produces it. Shapes: revise_strategy: {"apply_payload": { "agent_id": "", "strategy": {"diagnosis": str, "guiding_policy": str, "proximate_objectives": [ ... ]}, "strategy_nodes": [{"node_type": "diagnosis|policy|objective", "title": str, "body": str, "rank": int}, ...]}} add_edge: {"apply_payload": {"from_claim": "", "to_claim": "", "edge_type": "", "weight": <0..1|null>}} attach_evidence: {"apply_payload": {"evidence": [{"claim_id": "", "source_id": "", "role": "", "weight": <0..1|null>}, ...]}} approve_claim: {"apply_payload": { "agent_id": "", "claims": [{"id": "", "type": "", "text": str, "status": "open", "confidence": <0..1|null>, "tags": [str, ...], "created_by": ""}, ...], "sources": [{"id": "", "source_type": "", "url": str|null, "storage_path": str|null, "excerpt": str|null, "hash": str, "created_by": ""}, ...], "evidence": [{"claim_id": "", "source_id": "", "role": "", "weight": <0..1|null>, "created_by": ""}, ...], "edges": [{"from_claim": "", "to_claim": "", "edge_type": "", "weight": <0..1|null>, "created_by": ""}, ...], "reasoning_tools": [{"id": "", "agent_id": "", "name": str, "description": str, "category": str|null}, ...]}} ``approve_claim`` is the only source-creation contract. It is an atomic, insert-only canonical bundle: existing IDs must match the reviewed payload exactly, source hashes cannot alias another ID, and every row is verified before the approved proposal ledger can flip to ``applied``. """ from __future__ import annotations import argparse import hashlib import json import shutil import subprocess import sys import uuid from pathlib import Path from typing import Any import kb_apply_replay_receipt as replay_receipt DEFAULT_SECRETS_FILE = "/home/teleo/.hermes/profiles/leoclean/secrets/kb-apply.env" DEFAULT_CONTAINER = "teleo-pg" DEFAULT_DB = "teleo" DEFAULT_HOST = "127.0.0.1" DEFAULT_ROLE = "kb_apply" V3_CUTOVER_FUNCTION = "kb_stage.teleo_v3_enforce_apply_contract_cutover()" V3_CUTOVER_FUNCTION_NAME = "teleo_v3_enforce_apply_contract_cutover" V3_CUTOVER_TRIGGER = "teleo_v3_00_enforce_apply_contract_cutover" V3_GATE_OWNER = "kb_gate_owner" V3_PROTECTED_ROLES = ("kb_gate_owner", "kb_review", "kb_apply") V3_TRIGGER_PREFIX = "teleo_v3_" V3_CATALOG_DDL_ADVISORY_LOCK_KEY = 6072343533146485505 V3_CATALOG_LOCK_FUNCTION = "teleo_v3_acquire_protected_relation_locks" V3_CATALOG_LOCK_FUNCTION_SOURCE_SHA256 = "897345944ba0f1b2a5c01b098fb17de6f881a122e906277086fa7889e76219e8" V3_TRIGGER_FUNCTION_SOURCE_SHA256 = { "teleo_v3_enforce_apply_contract_cutover": "3ab2d5e1d5d1d18b13579fe2c0cae487036c7b147cf7f1b6b9a3633274756c18", "teleo_v3_guard_immutable_fields": "d243741e552fe8c232440ffa5f853cf261f4b86b6f32d76f375734a2235c2d13", "teleo_v3_guard_linked_proposal_decision": "602ee8f81f4515b0237f09a503ecbfe4fce2dc4a799e13f833554103b0e83e32", "teleo_v3_reject_delete": "46190723ab4520954858dbd4bb6c9230e68e41c68b5aee3b8c2719e1416de43c", "teleo_v3_require_accepted_proposal": "ca398ef2f185488c0712bd5ebc22402ab9e41e5dfd2f5823b367e6a382e8a64f", "teleo_v3_require_linked_insert": "d8c190bd2fc90159b9a4824015c10a73a701a263bba90b67f8ccce75e454e6f8", "teleo_v3_validate_claim_edge": "6a7a3e9d1d5ac6e9490abc9b7b1f321bb2e0948aea2671415aad28101f4ee936", "teleo_v3_validate_claim_evidence": "4c697c497699760214aca99f255c215e393319fd4482d4b36efac839280c3733", "teleo_v3_validate_evidence_assessment": "860f939b4878abe8e2e604e03be9eeb6c23b4878af4b19f9ec42de4b821fac61", } # Handle of the service-agent row that performs canonical writes. Bootstrapped # once by superuser (scripts/kb_apply_prereqs.sql); kb_apply holds SELECT-only on # public.agents so it can resolve this into applied_by_agent_id, never INSERT. SERVICE_AGENT_HANDLE = "kb-apply" APPLYABLE_TYPES = ("revise_strategy", "add_edge", "attach_evidence", "approve_claim") CLAIM_TYPES = {"empirical", "structural", "normative", "meta", "concept"} SOURCE_TYPES = {"paper", "article", "transcript", "observation", "dataset", "post", "dm", "other"} EVIDENCE_ROLES = {"grounds", "illustrates", "contradicts"} EDGE_TYPES = { "supports", "challenges", "requires", "relates", "contradicts", "supersedes", "derives_from", "cites", "causes", "constrains", "accelerates", } MAX_BUNDLE_ROWS = { "claims": 100, "sources": 500, "evidence": 1000, "edges": 1000, "assessments": 100, "reasoning_tools": 50, } def _trigger_argument_hex(arguments: tuple[str, ...]) -> str: return b"".join(argument.encode("utf-8") + b"\0" for argument in arguments).hex() def _v3_trigger_contract( schema_name: str, table_name: str, trigger_name: str, trigger_type: int, function_name: str, *, update_columns: tuple[str, ...] = (), arguments: tuple[str, ...] = (), security_definer: bool = False, ) -> dict[str, Any]: return { "schema_name": schema_name, "table_name": table_name, "trigger_name": trigger_name, "trigger_type": trigger_type, "update_columns": sorted(update_columns), "when_expression": None, "argument_count": len(arguments), "argument_hex": _trigger_argument_hex(arguments), "function_schema": "kb_stage", "function_name": function_name, "function_identity_arguments": "", "function_owner": V3_GATE_OWNER, "function_security_definer": security_definer, "function_config": ["search_path=pg_catalog, pg_temp"], "function_source_sha256": V3_TRIGGER_FUNCTION_SOURCE_SHA256[function_name], "function_owner_only_execute": True if security_definer else None, } # PostgreSQL pg_trigger.tgtype is a bitmask: ROW=1, BEFORE=2, INSERT=4, # DELETE=8, UPDATE=16. Every V3 trigger is an ordinary row-level BEFORE trigger. V3_EXPECTED_TRIGGER_CONTRACT = ( _v3_trigger_contract( "kb_stage", "kb_proposals", V3_CUTOVER_TRIGGER, 23, V3_CUTOVER_FUNCTION_NAME, update_columns=("proposal_type", "payload", "status"), security_definer=True, ), _v3_trigger_contract( "public", "claims", "teleo_v3_00_claim_require_linked_insert", 7, "teleo_v3_require_linked_insert", arguments=("accepted_by_proposal_id", "admission_state"), ), _v3_trigger_contract( "public", "claims", "teleo_v3_claim_require_accepted_proposal", 23, "teleo_v3_require_accepted_proposal", update_columns=("accepted_by_proposal_id",), arguments=("accepted_by_proposal_id",), security_definer=True, ), _v3_trigger_contract( "public", "sources", "teleo_v3_00_source_require_linked_insert", 7, "teleo_v3_require_linked_insert", arguments=("accepted_by_proposal_id",), ), _v3_trigger_contract( "public", "claims", "teleo_v3_claim_immutable", 19, "teleo_v3_guard_immutable_fields", arguments=( "id", "owner_agent_id", "type", "text", "proposition", "body_markdown", "scope", "access_scope", "status", "admission_state", "revision_criteria", "revision_kind", "supersedes_id", "superseded_by", "confidence", "tags", "created_by", "created_at", "updated_at", "accepted_by_proposal_id", ), ), _v3_trigger_contract("public", "claims", "teleo_v3_claim_reject_delete", 11, "teleo_v3_reject_delete"), _v3_trigger_contract( "public", "claim_evidence", "teleo_v3_00_evidence_require_linked_insert", 7, "teleo_v3_require_linked_insert", arguments=("accepted_from_proposal_id",), ), _v3_trigger_contract( "public", "sources", "teleo_v3_source_require_accepted_proposal", 23, "teleo_v3_require_accepted_proposal", update_columns=("accepted_by_proposal_id",), arguments=("accepted_by_proposal_id",), security_definer=True, ), _v3_trigger_contract( "public", "claim_edges", "teleo_v3_00_edge_require_linked_insert", 7, "teleo_v3_require_linked_insert", arguments=("accepted_by_proposal_id",), ), _v3_trigger_contract( "public", "sources", "teleo_v3_source_immutable", 19, "teleo_v3_guard_immutable_fields", arguments=( "id", "source_type", "url", "storage_path", "excerpt", "hash", "canonical_url", "storage_uri", "content_hash", "access_scope", "ingestion_origin", "provenance_status", "source_class", "auto_promotion_policy", "created_by", "captured_at", "created_at", "accepted_by_proposal_id", ), ), _v3_trigger_contract("public", "sources", "teleo_v3_source_reject_delete", 11, "teleo_v3_reject_delete"), _v3_trigger_contract( "public", "claim_evidence", "teleo_v3_evidence_require_accepted_proposal", 23, "teleo_v3_require_accepted_proposal", update_columns=("accepted_from_proposal_id",), arguments=("accepted_from_proposal_id",), security_definer=True, ), _v3_trigger_contract( "public", "claim_evidence", "teleo_v3_evidence_validate_provenance", 23, "teleo_v3_validate_claim_evidence", ), _v3_trigger_contract( "public", "claim_evidence", "teleo_v3_evidence_immutable", 19, "teleo_v3_guard_immutable_fields", arguments=( "id", "claim_id", "source_id", "role", "weight", "excerpt", "locator_json", "source_content_hash", "rationale", "polarity", "created_by", "created_at", "accepted_from_proposal_id", ), ), _v3_trigger_contract( "public", "claim_evidence", "teleo_v3_evidence_reject_delete", 11, "teleo_v3_reject_delete", ), _v3_trigger_contract( "public", "claim_edges", "teleo_v3_edge_require_accepted_proposal", 23, "teleo_v3_require_accepted_proposal", update_columns=("accepted_by_proposal_id",), arguments=("accepted_by_proposal_id",), security_definer=True, ), _v3_trigger_contract( "public", "claim_edges", "teleo_v3_edge_validate_endpoints", 23, "teleo_v3_validate_claim_edge" ), _v3_trigger_contract( "public", "claim_edges", "teleo_v3_edge_immutable", 19, "teleo_v3_guard_immutable_fields", arguments=( "id", "from_claim", "to_claim", "edge_type", "weight", "relation_type", "rationale", "scope_or_conditions", "created_by", "created_at", "accepted_by_proposal_id", ), ), _v3_trigger_contract("public", "claim_edges", "teleo_v3_edge_reject_delete", 11, "teleo_v3_reject_delete"), _v3_trigger_contract( "public", "claim_evidence_assessments", "teleo_v3_assessment_require_accepted_proposal", 23, "teleo_v3_require_accepted_proposal", update_columns=("accepted_by_proposal_id",), arguments=("accepted_by_proposal_id",), security_definer=True, ), _v3_trigger_contract( "public", "claim_evidence_assessments", "teleo_v3_assessment_validate", 23, "teleo_v3_validate_evidence_assessment", ), _v3_trigger_contract( "public", "claim_evidence_assessments", "teleo_v3_assessment_immutable", 19, "teleo_v3_guard_immutable_fields", arguments=( "id", "claim_id", "support_tier", "challenge_tier", "overall_state", "rationale", "evidence_set_hash", "accepted_by_proposal_id", "supersedes_assessment_id", "created_at", ), ), _v3_trigger_contract( "public", "claim_evidence_assessments", "teleo_v3_assessment_reject_delete", 11, "teleo_v3_reject_delete", ), _v3_trigger_contract( "kb_stage", "kb_proposals", "teleo_v3_guard_linked_proposal_decision", 19, "teleo_v3_guard_linked_proposal_decision", update_columns=( "proposal_type", "status", "rationale", "payload", "reviewed_by_handle", "reviewed_by_agent_id", "reviewed_at", "review_note", "applied_by_handle", "applied_by_agent_id", "applied_at", ), ), ) V3_PROTECTED_RELATIONS = ( ("kb_stage", "kb_proposals"), ("public", "claim_edges"), ("public", "claim_evidence"), ("public", "claim_evidence_assessments"), ("public", "claims"), ("public", "sources"), ) def v3_expected_trigger_contract_json() -> str: return json.dumps(V3_EXPECTED_TRIGGER_CONTRACT, separators=(",", ":"), sort_keys=True) def build_v3_contract_state_query( *, into: str | None = None, expected_contract_expression: str | None = None, ) -> str: """Return the exact catalog query shared by worker and transaction guards.""" protected_roles = ", ".join(sql_literal(role) for role in V3_PROTECTED_ROLES) expected_contract = expected_contract_expression or sql_literal(v3_expected_trigger_contract_json()) into_clause = f"\n into {into}" if into is not None else "" return f"""with expected_triggers as ( select expected.* from pg_catalog.jsonb_to_recordset({expected_contract}::jsonb) as expected( schema_name text, table_name text, trigger_name text, trigger_type integer, update_columns jsonb, when_expression text, argument_count integer, argument_hex text, function_schema text, function_name text, function_identity_arguments text, function_owner text, function_security_definer boolean, function_config jsonb, function_source_sha256 text, function_owner_only_execute boolean ) ), expected_functions as ( select distinct function_schema, function_name, function_identity_arguments, function_owner, function_security_definer, function_config, function_source_sha256, function_owner_only_execute from expected_triggers ), actual_v3_functions as ( select procedure.oid, namespace.nspname as function_schema, procedure.proname as function_name, pg_catalog.pg_get_function_identity_arguments(procedure.oid) as function_identity_arguments, owner_role.rolname as function_owner, procedure.prosecdef as function_security_definer, pg_catalog.to_jsonb(procedure.proconfig) as function_config, pg_catalog.encode( pg_catalog.sha256(pg_catalog.convert_to(procedure.prosrc, 'UTF8')), 'hex' ) as function_source_sha256, not exists ( select 1 from pg_catalog.aclexplode( coalesce(procedure.proacl, pg_catalog.acldefault('f', procedure.proowner)) ) acl where acl.privilege_type = 'EXECUTE' and acl.grantee <> procedure.proowner ) as function_owner_only_execute, language.lanname as function_language, procedure.prokind as function_kind, procedure.pronargs as function_argument_count, procedure.prorettype from pg_catalog.pg_proc procedure join pg_catalog.pg_namespace namespace on namespace.oid = procedure.pronamespace join pg_catalog.pg_roles owner_role on owner_role.oid = procedure.proowner join pg_catalog.pg_language language on language.oid = procedure.prolang where namespace.nspname = 'kb_stage' and pg_catalog.left(procedure.proname, {len(V3_TRIGGER_PREFIX)}) = {sql_literal(V3_TRIGGER_PREFIX)} and procedure.proname <> {sql_literal(V3_CATALOG_LOCK_FUNCTION)} ), actual_v3_catalog_lock_functions as ( select procedure.oid, owner_role.rolname as function_owner, procedure.prosecdef as function_security_definer, pg_catalog.to_jsonb(procedure.proconfig) as function_config, pg_catalog.encode( pg_catalog.sha256(pg_catalog.convert_to(procedure.prosrc, 'UTF8')), 'hex' ) as function_source_sha256, language.lanname as function_language, procedure.prokind as function_kind, procedure.pronargs as function_argument_count, pg_catalog.pg_get_function_identity_arguments(procedure.oid) as function_identity_arguments, procedure.prorettype, exists ( select 1 from pg_catalog.aclexplode( coalesce(procedure.proacl, pg_catalog.acldefault('f', procedure.proowner)) ) acl join pg_catalog.pg_roles grantee on grantee.oid = acl.grantee where acl.privilege_type = 'EXECUTE' and grantee.rolname = 'kb_apply' ) as apply_execute, not exists ( select 1 from pg_catalog.aclexplode( coalesce(procedure.proacl, pg_catalog.acldefault('f', procedure.proowner)) ) acl where acl.privilege_type = 'EXECUTE' and acl.grantee <> procedure.proowner and acl.grantee <> ( select role.oid from pg_catalog.pg_roles role where role.rolname = 'kb_apply' ) ) as execute_acl_exact from pg_catalog.pg_proc procedure join pg_catalog.pg_namespace namespace on namespace.oid = procedure.pronamespace join pg_catalog.pg_roles owner_role on owner_role.oid = procedure.proowner join pg_catalog.pg_language language on language.oid = procedure.prolang where namespace.nspname = 'kb_stage' and procedure.proname = {sql_literal(V3_CATALOG_LOCK_FUNCTION)} ), actual_v3_triggers as ( select trigger_row.*, table_namespace.nspname as schema_name, relation.relname as table_name, function_namespace.nspname as function_schema, procedure.proname as function_name, pg_catalog.pg_get_function_identity_arguments(procedure.oid) as function_identity_arguments, coalesce(( select pg_catalog.jsonb_agg(attribute.attname order by attribute.attname) from pg_catalog.unnest(trigger_row.tgattr::smallint[]) update_column(attnum) join pg_catalog.pg_attribute attribute on attribute.attrelid = trigger_row.tgrelid and attribute.attnum = update_column.attnum and not attribute.attisdropped ), '[]'::jsonb) as update_columns, pg_catalog.pg_get_expr(trigger_row.tgqual, trigger_row.tgrelid, true) as when_expression, pg_catalog.encode(trigger_row.tgargs, 'hex') as argument_hex from pg_catalog.pg_trigger trigger_row join pg_catalog.pg_class relation on relation.oid = trigger_row.tgrelid join pg_catalog.pg_namespace table_namespace on table_namespace.oid = relation.relnamespace join pg_catalog.pg_proc procedure on procedure.oid = trigger_row.tgfoid join pg_catalog.pg_namespace function_namespace on function_namespace.oid = procedure.pronamespace where pg_catalog.left(trigger_row.tgname, {len(V3_TRIGGER_PREFIX)}) = {sql_literal(V3_TRIGGER_PREFIX)} ), function_contract as ( select (select pg_catalog.count(*) from actual_v3_functions) = (select pg_catalog.count(*) from expected_functions) and not exists ( select 1 from expected_functions expected where not exists ( select 1 from actual_v3_functions actual where actual.function_schema = expected.function_schema and actual.function_name = expected.function_name and actual.function_identity_arguments = expected.function_identity_arguments and actual.function_owner = expected.function_owner and actual.function_security_definer = expected.function_security_definer and actual.function_config = expected.function_config and actual.function_source_sha256 = expected.function_source_sha256 and ( expected.function_owner_only_execute is null or actual.function_owner_only_execute = expected.function_owner_only_execute ) and actual.function_language = 'plpgsql' and actual.function_kind = 'f' and actual.function_argument_count = 0 and actual.prorettype = 'pg_catalog.trigger'::pg_catalog.regtype ) ) as valid ), catalog_lock_function_contract as ( select pg_catalog.count(*) = 1 and pg_catalog.bool_and( function_owner = {sql_literal(V3_GATE_OWNER)} and function_security_definer and function_config = '["search_path=pg_catalog, pg_temp"]'::jsonb and function_source_sha256 = {sql_literal(V3_CATALOG_LOCK_FUNCTION_SOURCE_SHA256)} and function_language = 'plpgsql' and function_kind = 'f' and function_argument_count = 0 and function_identity_arguments = '' and prorettype = 'pg_catalog.void'::pg_catalog.regtype and apply_execute and execute_acl_exact ) as valid from actual_v3_catalog_lock_functions ), trigger_contract as ( select (select pg_catalog.count(*) from actual_v3_triggers) = (select pg_catalog.count(*) from expected_triggers) and not exists ( select 1 from expected_triggers expected where not exists ( select 1 from actual_v3_triggers actual where actual.schema_name = expected.schema_name and actual.table_name = expected.table_name and actual.tgname = expected.trigger_name and not actual.tgisinternal and actual.tgenabled = 'O' and actual.tgtype = expected.trigger_type and actual.update_columns = expected.update_columns and actual.when_expression is not distinct from expected.when_expression and actual.tgnargs = expected.argument_count and actual.argument_hex = expected.argument_hex and actual.tgconstraint = 0::pg_catalog.oid and not actual.tgdeferrable and not actual.tginitdeferred and actual.tgconstrrelid = 0::pg_catalog.oid and actual.tgparentid = 0::pg_catalog.oid and actual.tgoldtable is null and actual.tgnewtable is null and actual.function_schema = expected.function_schema and actual.function_name = expected.function_name and actual.function_identity_arguments = expected.function_identity_arguments and exists ( select 1 from actual_v3_functions function_row where function_row.oid = actual.tgfoid and function_row.function_schema = expected.function_schema and function_row.function_name = expected.function_name and function_row.function_identity_arguments = expected.function_identity_arguments ) ) ) as valid ), catalog_state as ( select exists (select 1 from actual_v3_functions) or exists (select 1 from actual_v3_catalog_lock_functions) or exists (select 1 from actual_v3_triggers) as artifacts_present, (select valid from function_contract) as functions_valid, (select valid from catalog_lock_function_contract) as catalog_lock_function_valid, (select valid from trigger_contract) as triggers_valid, exists ( select 1 from pg_catalog.pg_roles owner_role where owner_role.rolname = {sql_literal(V3_GATE_OWNER)} and not owner_role.rolcanlogin and not owner_role.rolinherit and not owner_role.rolsuper and not owner_role.rolcreatedb and not owner_role.rolcreaterole and not owner_role.rolreplication and not owner_role.rolbypassrls ) as owner_role_valid, not exists ( select 1 from pg_catalog.pg_auth_members membership join pg_catalog.pg_roles member_role on member_role.oid = membership.member join pg_catalog.pg_roles granted_role on granted_role.oid = membership.roleid where member_role.rolname in ({protected_roles}) or granted_role.rolname in ({protected_roles}) ) as memberships_valid ) select case when not artifacts_present then 'V2' when functions_valid and catalog_lock_function_valid and triggers_valid and owner_role_valid and memberships_valid then 'V3' else 'INVALID' end{into_clause} from catalog_state""" def _v3_contract_lock_sql() -> str: statements: list[str] = [] for schema_name, table_name in V3_PROTECTED_RELATIONS: if (schema_name, table_name) == ("kb_stage", "kb_proposals"): statements.append(f"select kb_stage.{V3_CATALOG_LOCK_FUNCTION}();") else: statements.append(f"lock table {schema_name}.{table_name} in row exclusive mode;") return "\n".join(statements) def _v3_contract_transaction_guard_sql() -> str: state_query = build_v3_contract_state_query(into="v_contract_state") return f"""{_v3_contract_lock_sql()} do $v3_contract_guard$ declare v_contract_state text; begin {state_query}; if v_contract_state <> 'V3' then raise exception using errcode = '55000', message = pg_catalog.format( 'apply_proposal: V3 catalog contract is %s; refusing mutating transaction', v_contract_state ); end if; end $v3_contract_guard$;""" V3_CLAIM_TYPES = {"empirical", "predictive", "causal", "normative", "conceptual"} V3_ACCESS_SCOPES = {"agent_private", "collective_internal", "public"} V3_SOURCE_ORIGINS = {"operator_upload", "agent_fetch", "contributor", "system_import", "unknown"} V3_PROVENANCE_STATES = {"verified", "unverified", "disputed"} V3_SOURCE_CLASSES = { "primary_record", "peer_reviewed", "institutional", "journalistic", "self_published", "unknown", } V3_AUTO_PROMOTION_POLICIES = {"eligible", "human_required", "blocked"} V3_EVIDENCE_POLARITIES = {"supports", "challenges", "illustrates"} V3_RELATION_TYPES = {"supports", "challenges", "depends_on", "contradicts"} V3_SUPPORT_TIERS = {"none", "weak", "moderate", "strong"} V3_ASSESSMENT_STATES = {"insufficient", "support_dominant", "challenge_dominant", "mixed"} # --------------------------------------------------------------------------- # # SQL helpers # # --------------------------------------------------------------------------- # def sql_literal(value: Any) -> str: """Render a Python value as an explicit PostgreSQL escape-string literal.""" if value is None: return "null" if isinstance(value, bool): return "true" if value else "false" if isinstance(value, (int, float)): return repr(value) escaped = str(value).replace("\\", "\\\\").replace("'", "''") return "E'" + escaped + "'" def _jsonb(value: Any) -> str: return sql_literal(json.dumps(value, sort_keys=True)) + "::jsonb" def deterministic_row_uuid(proposal_id: str, row_kind: str, *semantic_key: Any) -> str: """Derive a stable persisted row id from the proposal and semantic key. Evidence and edge ids are not part of the reviewed v2 payload. Deriving them here makes clone/production receipts and a pre-authorized rollback packet bind the same exact ids instead of relying on database randomness. """ material = json.dumps( ["livingip", "kb-apply", str(proposal_id), row_kind, *semantic_key], ensure_ascii=True, separators=(",", ":"), ) return str(uuid.uuid5(uuid.NAMESPACE_URL, material)) def _text_array(values: list[str]) -> str: if not values: return "'{}'::text[]" return "array[" + ", ".join(sql_literal(value) for value in values) + "]::text[]" def _uuid(value: Any, path: str, *, nullable: bool = False) -> str | None: if value is None and nullable: return None try: return str(uuid.UUID(str(value))) except (TypeError, ValueError, AttributeError) as exc: raise ValueError(f"{path} must be a UUID{' or null' if nullable else ''}") from exc def _v3_uuid(value: Any, path: str, *, nullable: bool = False) -> str | None: """Require the reviewed V3 spelling to equal PostgreSQL's UUID text form.""" canonical = _uuid(value, path, nullable=nullable) if canonical is None: return None if not isinstance(value, str) or value != canonical: raise ValueError(f"{path} must be a canonical lowercase hyphenated UUID") return canonical def _weight(value: Any, path: str) -> Any: if value is None: return None if isinstance(value, bool) or not isinstance(value, (int, float)): raise ValueError(f"{path} must be a number from 0 to 1 or null") if not 0 <= value <= 1: raise ValueError(f"{path} must be between 0 and 1") return value def _required_text(value: Any, path: str) -> str: if not isinstance(value, str) or not value.strip(): raise ValueError(f"{path} must be a non-empty string") return value def _optional_text(value: Any, path: str) -> str | None: if value is None: return None if not isinstance(value, str): raise ValueError(f"{path} must be a string or null") return value def _sha256(value: Any, path: str) -> str: text = _required_text(value, path) if len(text) != 64 or any(character not in "0123456789abcdef" for character in text): raise ValueError(f"{path} must be a lowercase SHA-256 hex digest") return text def _utc_timestamp(value: Any, path: str) -> str: try: return replay_receipt.canonical_utc_timestamp(value, path=path) except ValueError as exc: raise ValueError(f"{path} must be a timezone-aware ISO-8601 timestamp") from exc def _json_object(value: Any, path: str, *, nonempty: bool = False) -> dict[str, Any]: if not isinstance(value, dict) or (nonempty and not value): qualifier = "non-empty " if nonempty else "" raise ValueError(f"{path} must be a {qualifier}object") return value def _rows(payload: dict[str, Any], key: str, *, required: bool = False) -> list[dict[str, Any]]: value = payload.get(key) if value is None: value = [] if not isinstance(value, list) or any(not isinstance(row, dict) for row in value): raise ValueError(f"approve_claim apply_payload.{key} must be a list of objects") if required and not value: raise ValueError(f"approve_claim apply_payload requires non-empty '{key}'") if len(value) > MAX_BUNDLE_ROWS[key]: raise ValueError(f"approve_claim apply_payload.{key} exceeds {MAX_BUNDLE_ROWS[key]} rows") return value def _dedupe(values: list[Any], path: str) -> None: seen = set() for value in values: marker = json.dumps(value, sort_keys=True) if marker in seen: raise ValueError(f"{path} contains a duplicate: {value!r}") seen.add(marker) def _reject_unknown(mapping: dict[str, Any], allowed: set[str], path: str) -> None: unknown = sorted(set(mapping) - allowed) if unknown: raise ValueError(f"{path} contains unknown fields: {unknown}") def _v3_evidence_set_hash(claim_id: str, rows: list[dict[str, Any]]) -> str: evidence = [ { "source_id": row["source_id"], "source_content_hash": row["source_content_hash"], "polarity": row["polarity"], "locator": row["locator_json"], } for row in rows if row["claim_id"] == claim_id ] evidence.sort(key=lambda row: json.dumps(row, separators=(",", ":"), sort_keys=True)) payload = json.dumps( {"claim_id": claim_id, "evidence": evidence}, separators=(",", ":"), sort_keys=True, ) return hashlib.sha256(payload.encode("utf-8")).hexdigest() def _normalize_v3_approve_claim(apply_payload: dict[str, Any]) -> dict[str, Any]: _reject_unknown( apply_payload, { "contract_version", "source_proposal_id", "agent_id", "claims", "sources", "evidence", "edges", "assessments", "reasoning_tools", "normalization_manifest", }, "approve_claim apply_payload", ) if apply_payload.get("contract_version") != 3: raise ValueError("V3 approve_claim apply_payload.contract_version must equal 3") for key in ("claims", "sources", "evidence", "edges", "assessments", "reasoning_tools"): if key not in apply_payload: raise ValueError(f"V3 approve_claim apply_payload requires explicit '{key}' collection") if apply_payload.get("reasoning_tools") != []: raise ValueError("V3 approve_claim does not admit reasoning_tools; use a separately reviewed capability") if apply_payload.get("source_proposal_id") is not None: _v3_uuid(apply_payload["source_proposal_id"], "approve_claim.source_proposal_id") manifest = apply_payload.get("normalization_manifest") if manifest is not None: _json_object(manifest, "approve_claim.normalization_manifest") agent_id = _v3_uuid(apply_payload.get("agent_id"), "approve_claim.agent_id", nullable=True) raw_claims = _rows(apply_payload, "claims", required=True) raw_sources = _rows(apply_payload, "sources", required=True) raw_evidence = _rows(apply_payload, "evidence", required=True) raw_edges = _rows(apply_payload, "edges") raw_assessments = _rows(apply_payload, "assessments", required=True) claims: list[dict[str, Any]] = [] for index, row in enumerate(raw_claims): path = f"approve_claim.claims[{index}]" _reject_unknown( row, { "id", "owner_agent_id", "type", "proposition", "body_markdown", "scope", "access_scope", "status", "revision_criteria", "revision_kind", "supersedes_id", "tags", "created_by", }, path, ) claim_type = row.get("type") if claim_type not in V3_CLAIM_TYPES: raise ValueError(f"{path}.type must be one of {sorted(V3_CLAIM_TYPES)}") status = row.get("status", "active") if status not in {"active", "contested"}: raise ValueError(f"{path}.status must be active or contested for an original admission") revision_kind = row.get("revision_kind", "original") if revision_kind != "original" or row.get("supersedes_id") is not None: raise ValueError("V3 minimum writer admits original claims only; revision transition is not yet proven") access_scope = row.get("access_scope") if access_scope not in V3_ACCESS_SCOPES: raise ValueError(f"{path}.access_scope must be one of {sorted(V3_ACCESS_SCOPES)}") owner_agent_id = _v3_uuid(row.get("owner_agent_id"), f"{path}.owner_agent_id", nullable=True) if access_scope == "agent_private" and owner_agent_id is None: raise ValueError(f"{path}.owner_agent_id is required for agent_private claims") created_by = _v3_uuid(row.get("created_by", agent_id), f"{path}.created_by", nullable=True) if created_by is None: raise ValueError(f"{path}.created_by or apply_payload.agent_id is required") tags = row.get("tags") or [] if not isinstance(tags, list) or any(not isinstance(tag, str) or not tag.strip() for tag in tags): raise ValueError(f"{path}.tags must be a list of non-empty strings") claims.append( { "id": _v3_uuid(row.get("id"), f"{path}.id"), "owner_agent_id": owner_agent_id, "type": claim_type, "proposition": _required_text(row.get("proposition"), f"{path}.proposition"), "body_markdown": _required_text(row.get("body_markdown"), f"{path}.body_markdown"), "scope": _required_text(row.get("scope"), f"{path}.scope"), "access_scope": access_scope, "status": status, "revision_criteria": _required_text(row.get("revision_criteria"), f"{path}.revision_criteria"), "revision_kind": revision_kind, "supersedes_id": None, "tags": tags, "created_by": created_by, } ) sources: list[dict[str, Any]] = [] for index, row in enumerate(raw_sources): path = f"approve_claim.sources[{index}]" _reject_unknown( row, { "id", "source_type", "canonical_url", "storage_uri", "excerpt", "content_hash", "access_scope", "ingestion_origin", "provenance_status", "source_class", "auto_promotion_policy", "captured_at", "created_by", }, path, ) source_type = row.get("source_type") if source_type not in SOURCE_TYPES: raise ValueError(f"{path}.source_type must be one of {sorted(SOURCE_TYPES)}") canonical_url = _optional_text(row.get("canonical_url"), f"{path}.canonical_url") storage_uri = _optional_text(row.get("storage_uri"), f"{path}.storage_uri") if not (canonical_url and canonical_url.strip()) and not (storage_uri and storage_uri.strip()): raise ValueError(f"{path} requires canonical_url or storage_uri") access_scope = row.get("access_scope") if access_scope not in V3_ACCESS_SCOPES: raise ValueError(f"{path}.access_scope must be one of {sorted(V3_ACCESS_SCOPES)}") ingestion_origin = row.get("ingestion_origin") if ingestion_origin not in V3_SOURCE_ORIGINS: raise ValueError(f"{path}.ingestion_origin must be one of {sorted(V3_SOURCE_ORIGINS)}") provenance_status = row.get("provenance_status") if provenance_status not in V3_PROVENANCE_STATES: raise ValueError(f"{path}.provenance_status must be one of {sorted(V3_PROVENANCE_STATES)}") source_class = row.get("source_class") if source_class not in V3_SOURCE_CLASSES: raise ValueError(f"{path}.source_class must be one of {sorted(V3_SOURCE_CLASSES)}") auto_promotion_policy = row.get("auto_promotion_policy") if auto_promotion_policy not in V3_AUTO_PROMOTION_POLICIES: raise ValueError(f"{path}.auto_promotion_policy must be one of {sorted(V3_AUTO_PROMOTION_POLICIES)}") created_by = _v3_uuid(row.get("created_by", agent_id), f"{path}.created_by", nullable=True) if created_by is None: raise ValueError(f"{path}.created_by or apply_payload.agent_id is required") sources.append( { "id": _v3_uuid(row.get("id"), f"{path}.id"), "source_type": source_type, "canonical_url": canonical_url, "storage_uri": storage_uri, "excerpt": _optional_text(row.get("excerpt"), f"{path}.excerpt"), "content_hash": _sha256(row.get("content_hash"), f"{path}.content_hash"), "access_scope": access_scope, "ingestion_origin": ingestion_origin, "provenance_status": provenance_status, "source_class": source_class, "auto_promotion_policy": auto_promotion_policy, "captured_at": _utc_timestamp(row.get("captured_at"), f"{path}.captured_at"), "created_by": created_by, } ) claim_ids = {row["id"] for row in claims} source_by_id = {row["id"]: row for row in sources} evidence: list[dict[str, Any]] = [] polarity_to_role = {"supports": "grounds", "challenges": "contradicts", "illustrates": "illustrates"} for index, row in enumerate(raw_evidence): path = f"approve_claim.evidence[{index}]" _reject_unknown( row, { "id", "claim_id", "source_id", "polarity", "excerpt", "locator_json", "source_content_hash", "rationale", "created_by", }, path, ) claim_id = _v3_uuid(row.get("claim_id"), f"{path}.claim_id") source_id = _v3_uuid(row.get("source_id"), f"{path}.source_id") if claim_id not in claim_ids or source_id not in source_by_id: raise ValueError(f"{path} must link payload-owned claim and source rows") polarity = row.get("polarity") if polarity not in V3_EVIDENCE_POLARITIES: raise ValueError(f"{path}.polarity must be one of {sorted(V3_EVIDENCE_POLARITIES)}") source_content_hash = _sha256(row.get("source_content_hash"), f"{path}.source_content_hash") if source_content_hash != source_by_id[source_id]["content_hash"]: raise ValueError(f"{path}.source_content_hash does not match the payload source") created_by = _v3_uuid(row.get("created_by", agent_id), f"{path}.created_by", nullable=True) if created_by is None: raise ValueError(f"{path}.created_by or apply_payload.agent_id is required") evidence.append( { "id": _v3_uuid(row.get("id"), f"{path}.id"), "claim_id": claim_id, "source_id": source_id, "role": polarity_to_role[polarity], "polarity": polarity, "excerpt": _optional_text(row.get("excerpt"), f"{path}.excerpt"), "locator_json": _json_object(row.get("locator_json"), f"{path}.locator_json", nonempty=True), "source_content_hash": source_content_hash, "rationale": _required_text(row.get("rationale"), f"{path}.rationale"), "created_by": created_by, } ) edges: list[dict[str, Any]] = [] for index, row in enumerate(raw_edges): path = f"approve_claim.edges[{index}]" _reject_unknown( row, { "id", "from_claim", "to_claim", "relation_type", "rationale", "scope_or_conditions", "created_by", }, path, ) from_claim = _v3_uuid(row.get("from_claim"), f"{path}.from_claim") to_claim = _v3_uuid(row.get("to_claim"), f"{path}.to_claim") relation_type = row.get("relation_type") if relation_type not in V3_RELATION_TYPES: raise ValueError(f"{path}.relation_type must be one of {sorted(V3_RELATION_TYPES)}") if from_claim == to_claim: raise ValueError(f"{path} cannot be a self-edge") if not ({from_claim, to_claim} & claim_ids): raise ValueError(f"{path} must involve at least one payload-owned claim") if relation_type == "contradicts" and from_claim > to_claim: raise ValueError(f"{path} contradicts endpoints must use canonical UUID order") created_by = _v3_uuid(row.get("created_by", agent_id), f"{path}.created_by", nullable=True) if created_by is None: raise ValueError(f"{path}.created_by or apply_payload.agent_id is required") edges.append( { "id": _v3_uuid(row.get("id"), f"{path}.id"), "from_claim": from_claim, "to_claim": to_claim, "relation_type": relation_type, "edge_type": "requires" if relation_type == "depends_on" else relation_type, "rationale": _required_text(row.get("rationale"), f"{path}.rationale"), "scope_or_conditions": _optional_text(row.get("scope_or_conditions"), f"{path}.scope_or_conditions"), "created_by": created_by, } ) assessments: list[dict[str, Any]] = [] claim_type_by_id = {row["id"]: row["type"] for row in claims} for index, row in enumerate(raw_assessments): path = f"approve_claim.assessments[{index}]" _reject_unknown( row, { "id", "claim_id", "support_tier", "challenge_tier", "overall_state", "rationale", "evidence_set_hash", "supersedes_assessment_id", }, path, ) claim_id = _v3_uuid(row.get("claim_id"), f"{path}.claim_id") if claim_id not in claim_ids: raise ValueError(f"{path}.claim_id must identify a payload-owned claim") support_tier = row.get("support_tier") challenge_tier = row.get("challenge_tier") if support_tier is not None and support_tier not in V3_SUPPORT_TIERS: raise ValueError(f"{path}.support_tier must be null or one of {sorted(V3_SUPPORT_TIERS)}") if challenge_tier is not None and challenge_tier not in V3_SUPPORT_TIERS: raise ValueError(f"{path}.challenge_tier must be null or one of {sorted(V3_SUPPORT_TIERS)}") if claim_type_by_id[claim_id] in {"empirical", "predictive", "causal"} and ( support_tier is None or challenge_tier is None ): raise ValueError(f"{path} requires support_tier and challenge_tier for a truth-apt claim") overall_state = row.get("overall_state") if overall_state not in V3_ASSESSMENT_STATES: raise ValueError(f"{path}.overall_state must be one of {sorted(V3_ASSESSMENT_STATES)}") evidence_set_hash = _sha256(row.get("evidence_set_hash"), f"{path}.evidence_set_hash") if evidence_set_hash != _v3_evidence_set_hash(claim_id, evidence): raise ValueError(f"{path}.evidence_set_hash does not match the accepted evidence set") supersedes_assessment_id = _v3_uuid( row.get("supersedes_assessment_id"), f"{path}.supersedes_assessment_id", nullable=True ) if supersedes_assessment_id is not None: raise ValueError("V3 minimum writer admits first assessments only; assessment supersession is not proven") assessments.append( { "id": _v3_uuid(row.get("id"), f"{path}.id"), "claim_id": claim_id, "support_tier": support_tier, "challenge_tier": challenge_tier, "overall_state": overall_state, "rationale": _required_text(row.get("rationale"), f"{path}.rationale"), "evidence_set_hash": evidence_set_hash, "supersedes_assessment_id": None, } ) _dedupe([row["id"] for row in claims], "V3 approve_claim claims ids") _dedupe([row["id"] for row in sources], "V3 approve_claim sources ids") _dedupe([row["content_hash"] for row in sources], "V3 approve_claim source hashes") _dedupe([row["id"] for row in evidence], "V3 approve_claim evidence ids") _dedupe( [[row["claim_id"], row["source_id"], row["polarity"]] for row in evidence], "V3 approve_claim evidence keys", ) _dedupe([row["id"] for row in edges], "V3 approve_claim edge ids") _dedupe( [[row["from_claim"], row["to_claim"], row["relation_type"]] for row in edges], "V3 approve_claim edge keys", ) _dedupe([row["id"] for row in assessments], "V3 approve_claim assessment ids") _dedupe([row["claim_id"] for row in assessments], "V3 approve_claim assessment claim ids") assessed_claims = {row["claim_id"] for row in assessments} if assessed_claims != claim_ids: raise ValueError("V3 approve_claim requires exactly one evidence assessment for every admitted claim") return { "agent_id": agent_id, "claims": claims, "sources": sources, "evidence": evidence, "edges": edges, "assessments": assessments, } def _validate_approval_meta(approval: dict[str, Any], proposal_type: str, apply_payload: dict[str, Any]) -> None: if not isinstance(approval, dict): raise ValueError("approved proposal metadata is required") if approval.get("proposal_type") != proposal_type: raise ValueError("approved proposal_type does not match the apply builder") payload = approval.get("payload") if not isinstance(payload, dict) or payload.get("apply_payload") != apply_payload: raise ValueError("approved proposal payload does not match the strict apply payload") if not str(approval.get("reviewed_by_handle") or "").strip(): raise ValueError("approved proposal requires reviewed_by_handle") if not str(approval.get("reviewed_at") or "").strip(): raise ValueError("approved proposal requires reviewed_at") _utc_timestamp(approval["reviewed_at"], "approved proposal reviewed_at") if not str(approval.get("review_note") or "").strip(): raise ValueError("approved proposal requires review_note") _uuid( approval.get("reviewed_by_agent_id"), "approved proposal reviewed_by_agent_id", nullable=True, ) def _approval_guard_sql(proposal_id: str, approval: dict[str, Any]) -> str: reviewed_at = _utc_timestamp(approval["reviewed_at"], "approved proposal reviewed_at") return f"""select kb_stage.assert_approved_proposal( {sql_literal(proposal_id)}::uuid, {sql_literal(approval["proposal_type"])}, {_jsonb(approval["payload"])}, {sql_literal(approval["reviewed_by_handle"])}, {sql_literal(approval.get("reviewed_by_agent_id"))}::uuid, {sql_literal(reviewed_at)}::timestamptz, {sql_literal(approval["review_note"])} );""" def _ledger_and_verify( proposal_id: str, applied_by: str, extra_checks: str, approval: dict[str, Any], ) -> str: """Verify canonical rows, then finish the locked, payload-bound apply.""" reviewed_at = _utc_timestamp(approval["reviewed_at"], "approved proposal reviewed_at") checks_sql = f"""do $verify$ begin {extra_checks} end $verify$;""" finish_sql = f"""select kb_stage.finish_approved_proposal( {sql_literal(proposal_id)}::uuid, {sql_literal(approval["proposal_type"])}, {_jsonb(approval["payload"])}, {sql_literal(approval["reviewed_by_handle"])}, {sql_literal(approval.get("reviewed_by_agent_id"))}::uuid, {sql_literal(reviewed_at)}::timestamptz, {sql_literal(approval["review_note"])}, {sql_literal(applied_by)} );""" return checks_sql + "\n" + finish_sql # --------------------------------------------------------------------------- # # Per-type SQL builders (pure; unit-tested without a DB) # # --------------------------------------------------------------------------- # def build_revise_strategy_sql( apply_payload: dict[str, Any], proposal_id: str, applied_by: str | None, approval: dict[str, Any], ) -> str: _validate_approval_meta(approval, "revise_strategy", apply_payload) agent_id = apply_payload.get("agent_id") strategy = apply_payload.get("strategy") or {} nodes: list[dict[str, Any]] = apply_payload.get("strategy_nodes") or [] if not agent_id: raise ValueError("revise_strategy apply_payload requires 'agent_id'") for key in ("diagnosis", "guiding_policy", "proximate_objectives"): if strategy.get(key) is None: raise ValueError(f"revise_strategy apply_payload.strategy requires '{key}'") if not nodes: raise ValueError("revise_strategy apply_payload requires non-empty 'strategy_nodes'") node_values = [] for n in nodes: nt = n.get("node_type") if nt not in ("diagnosis", "policy", "objective"): raise ValueError(f"invalid strategy node_type: {nt!r}") if not n.get("title") or n.get("body") is None: raise ValueError("each strategy_node requires 'title' and 'body'") node_values.append( f"({sql_literal(agent_id)}::uuid, {sql_literal(nt)}, " f"{sql_literal(n['title'])}, {sql_literal(n['body'])}, " f"{int(n.get('rank', 1))})" ) node_values_sql = ",\n ".join(node_values) canonical = f""" -- deactivate the current active strategy for this agent (one_active invariant) update public.strategies set active = false where agent_id = {sql_literal(agent_id)}::uuid and active; -- insert the new strategy as the next version, active insert into public.strategies (agent_id, diagnosis, guiding_policy, proximate_objectives, version, active) select {sql_literal(agent_id)}::uuid, {sql_literal(strategy["diagnosis"])}, {sql_literal(strategy["guiding_policy"])}, {_jsonb(strategy["proximate_objectives"])}, coalesce(max(version), 0) + 1, true from public.strategies where agent_id = {sql_literal(agent_id)}::uuid; -- retire the agent's current strategy nodes (shared NULL-agent nodes untouched) update public.strategy_nodes set status = 'retired', updated_at = now() where agent_id = {sql_literal(agent_id)}::uuid and status <> 'retired'; -- insert the new node set insert into public.strategy_nodes (agent_id, node_type, title, body, rank) values {node_values_sql}; """.rstrip() checks = f""" if (select count(*) from public.strategies where agent_id = {sql_literal(agent_id)}::uuid and active) <> 1 then raise exception 'apply_proposal: expected exactly one active strategy for agent %', {sql_literal(agent_id)}; end if;""" ledger = _ledger_and_verify(proposal_id, applied_by or SERVICE_AGENT_HANDLE, checks, approval) return _wrap_txn(canonical, ledger, _approval_guard_sql(proposal_id, approval)) def build_add_edge_sql( apply_payload: dict[str, Any], proposal_id: str, applied_by: str | None, approval: dict[str, Any], ) -> str: _validate_approval_meta(approval, "add_edge", apply_payload) _reject_unknown(apply_payload, {"from_claim", "to_claim", "edge_type", "weight"}, "add_edge apply_payload") f = _uuid(apply_payload.get("from_claim"), "add_edge.from_claim") t = _uuid(apply_payload.get("to_claim"), "add_edge.to_claim") et = apply_payload.get("edge_type") if et not in EDGE_TYPES: raise ValueError(f"add_edge.edge_type must be one of {sorted(EDGE_TYPES)}") w = _weight(apply_payload.get("weight"), "add_edge.weight") if f == t: raise ValueError("add_edge from_claim and to_claim must differ") row_id = deterministic_row_uuid(proposal_id, "claim_edge", f, t, et) edge_lock_key = f"claim_edge:{f}:{t}:{et}" canonical = f""" -- serialize semantic edge creation because the historical table has no -- (from_claim,to_claim,edge_type) uniqueness constraint. select pg_advisory_xact_lock(hashtextextended({sql_literal(edge_lock_key)}, 0)); -- insert the edge unless an identical (from,to,type) edge already exists insert into public.claim_edges (id, from_claim, to_claim, edge_type, weight) select {sql_literal(row_id)}::uuid, {sql_literal(f)}::uuid, {sql_literal(t)}::uuid, {sql_literal(et)}::edge_type, {sql_literal(w)} where not exists ( select 1 from public.claim_edges where from_claim = {sql_literal(f)}::uuid and to_claim = {sql_literal(t)}::uuid and edge_type = {sql_literal(et)}::edge_type); """.rstrip() checks = f""" -- Historical replay receipts can carry a pre-deterministic row id. -- Require one exact semantic row; fresh applies still insert row_id above. if (select count(*) from public.claim_edges where from_claim = {sql_literal(f)}::uuid and to_claim = {sql_literal(t)}::uuid and edge_type = {sql_literal(et)}::edge_type) <> 1 then raise exception 'apply_proposal: claim_edge row does not match strict apply payload'; end if; if exists (select 1 from public.claim_edges where from_claim = {sql_literal(f)}::uuid and to_claim = {sql_literal(t)}::uuid and edge_type = {sql_literal(et)}::edge_type and weight is distinct from {sql_literal(w)}) then raise exception 'apply_proposal: claim_edge row does not match strict apply payload'; end if; if not exists (select 1 from public.claim_edges where from_claim = {sql_literal(f)}::uuid and to_claim = {sql_literal(t)}::uuid and edge_type = {sql_literal(et)}::edge_type and weight is not distinct from {sql_literal(w)}) then raise exception 'apply_proposal: claim_edge row does not match strict apply payload'; end if;""" ledger = _ledger_and_verify(proposal_id, applied_by or SERVICE_AGENT_HANDLE, checks, approval) return _wrap_txn(canonical, ledger, _approval_guard_sql(proposal_id, approval)) def build_attach_evidence_sql( apply_payload: dict[str, Any], proposal_id: str, applied_by: str | None, approval: dict[str, Any], ) -> str: _validate_approval_meta(approval, "attach_evidence", apply_payload) _reject_unknown(apply_payload, {"evidence"}, "attach_evidence apply_payload") evidence: list[dict[str, Any]] = apply_payload.get("evidence") or [] if not evidence: raise ValueError("attach_evidence apply_payload requires non-empty 'evidence'") statements = [] checks = [] for i, ev in enumerate(evidence): _reject_unknown( ev, {"claim_id", "source_id", "role", "weight"}, f"attach_evidence.evidence[{i}]", ) claim_id = _uuid(ev.get("claim_id"), f"attach_evidence.evidence[{i}].claim_id") source_id = _uuid(ev.get("source_id"), f"attach_evidence.evidence[{i}].source_id") role = ev.get("role") or "grounds" if role not in EVIDENCE_ROLES: raise ValueError(f"attach_evidence.evidence[{i}].role must be one of {sorted(EVIDENCE_ROLES)}") weight = _weight(ev.get("weight"), f"attach_evidence.evidence[{i}].weight") statements.append( f"""insert into public.claim_evidence (claim_id, source_id, role, weight) select {sql_literal(claim_id)}::uuid, {sql_literal(source_id)}::uuid, {sql_literal(role)}::evidence_role, {sql_literal(weight)} on conflict (claim_id, source_id, role) do nothing;""" ) checks.append( f""" -- Accept a legacy receipt id only when exactly one semantic row matches. if (select count(*) from public.claim_evidence where claim_id = {sql_literal(claim_id)}::uuid and source_id = {sql_literal(source_id)}::uuid and role = {sql_literal(role)}::evidence_role) <> 1 then raise exception 'apply_proposal: evidence row % does not match strict apply payload', {i}; end if; if exists (select 1 from public.claim_evidence where claim_id = {sql_literal(claim_id)}::uuid and source_id = {sql_literal(source_id)}::uuid and role = {sql_literal(role)}::evidence_role and weight is distinct from {sql_literal(weight)}) then raise exception 'apply_proposal: evidence row % does not match strict apply payload', {i}; end if; if not exists (select 1 from public.claim_evidence where claim_id = {sql_literal(claim_id)}::uuid and source_id = {sql_literal(source_id)}::uuid and role = {sql_literal(role)}::evidence_role and weight is not distinct from {sql_literal(weight)}) then raise exception 'apply_proposal: evidence row % does not match strict apply payload', {i}; end if;""" ) canonical = "\n".join(statements) ledger = _ledger_and_verify(proposal_id, applied_by or SERVICE_AGENT_HANDLE, "\n".join(checks), approval) return _wrap_txn(canonical, ledger, _approval_guard_sql(proposal_id, approval)) def build_approve_claim_v3_sql( apply_payload: dict[str, Any], proposal_id: str, applied_by: str | None, approval: dict[str, Any], *, fresh_owned: bool = False, fresh_preflight_sha256: str | None = None, ) -> str: """Build an exact-row idempotent V3 claim/source/evidence transaction.""" _validate_approval_meta(approval, "approve_claim", apply_payload) bundle = _normalize_v3_approve_claim(apply_payload) if fresh_owned: _sha256(fresh_preflight_sha256, "fresh-owned approve_claim preflight") elif fresh_preflight_sha256 is not None: raise ValueError("fresh preflight SHA-256 is only valid for fresh-owned approve_claim") statements: list[str] = [] checks: list[str] = [] if fresh_owned: statements.extend( [ f"-- fresh-owned preflight SHA-256: {fresh_preflight_sha256}", ( "select pg_advisory_xact_lock(hashtextextended(" f"'proposal-fresh-apply:' || {sql_literal(proposal_id)}, 0));" ), ] ) for row in bundle["sources"]: statements.append( f"""do $source_conflict$ begin if exists (select 1 from public.sources where hash = {sql_literal(row["content_hash"])} and id <> {sql_literal(row["id"])}::uuid) then raise exception 'V3 approve_claim: source hash collision for source %', {sql_literal(row["id"])}; end if; end $source_conflict$; insert into public.sources ( id, source_type, url, storage_path, excerpt, hash, created_by, captured_at, canonical_url, storage_uri, content_hash, access_scope, ingestion_origin, provenance_status, source_class, auto_promotion_policy, accepted_by_proposal_id ) values ( {sql_literal(row["id"])}::uuid, {sql_literal(row["source_type"])}, {sql_literal(row["canonical_url"])}, {sql_literal(row["storage_uri"])}, {sql_literal(row["excerpt"])}, {sql_literal(row["content_hash"])}, {sql_literal(row["created_by"])}::uuid, {sql_literal(row["captured_at"])}::timestamptz, {sql_literal(row["canonical_url"])}, {sql_literal(row["storage_uri"])}, {sql_literal(row["content_hash"])}, {sql_literal(row["access_scope"])}, {sql_literal(row["ingestion_origin"])}, {sql_literal(row["provenance_status"])}, {sql_literal(row["source_class"])}, {sql_literal(row["auto_promotion_policy"])}, {sql_literal(proposal_id)}::uuid ) on conflict do nothing;""" ) checks.append( f""" if not exists (select 1 from public.sources where id = {sql_literal(row["id"])}::uuid and source_type = {sql_literal(row["source_type"])} and url is not distinct from {sql_literal(row["canonical_url"])} and storage_path is not distinct from {sql_literal(row["storage_uri"])} and excerpt is not distinct from {sql_literal(row["excerpt"])} and hash = {sql_literal(row["content_hash"])} and created_by is not distinct from {sql_literal(row["created_by"])}::uuid and captured_at = {sql_literal(row["captured_at"])}::timestamptz and canonical_url is not distinct from {sql_literal(row["canonical_url"])} and storage_uri is not distinct from {sql_literal(row["storage_uri"])} and content_hash = {sql_literal(row["content_hash"])} and access_scope = {sql_literal(row["access_scope"])} and ingestion_origin = {sql_literal(row["ingestion_origin"])} and provenance_status = {sql_literal(row["provenance_status"])} and source_class = {sql_literal(row["source_class"])} and auto_promotion_policy = {sql_literal(row["auto_promotion_policy"])} and accepted_by_proposal_id = {sql_literal(proposal_id)}::uuid) then raise exception 'V3 approve_claim: source row does not match strict payload: %', {sql_literal(row["id"])}; end if;""" ) for row in bundle["claims"]: tags = _text_array(row["tags"]) statements.append( f"""insert into public.claims ( id, owner_agent_id, type, text, proposition, body_markdown, scope, access_scope, status, admission_state, revision_criteria, revision_kind, supersedes_id, confidence, tags, created_by, superseded_by, accepted_by_proposal_id ) values ( {sql_literal(row["id"])}::uuid, {sql_literal(row["owner_agent_id"])}::uuid, {sql_literal(row["type"])}, {sql_literal(row["proposition"])}, {sql_literal(row["proposition"])}, {sql_literal(row["body_markdown"])}, {sql_literal(row["scope"])}, {sql_literal(row["access_scope"])}, {sql_literal(row["status"])}, 'admitted', {sql_literal(row["revision_criteria"])}, {sql_literal(row["revision_kind"])}, null, null, {tags}, {sql_literal(row["created_by"])}::uuid, null, {sql_literal(proposal_id)}::uuid ) on conflict do nothing;""" ) checks.append( f""" if not exists (select 1 from public.claims where id = {sql_literal(row["id"])}::uuid and owner_agent_id is not distinct from {sql_literal(row["owner_agent_id"])}::uuid and type = {sql_literal(row["type"])} and text = {sql_literal(row["proposition"])} and proposition = {sql_literal(row["proposition"])} and body_markdown = {sql_literal(row["body_markdown"])} and scope = {sql_literal(row["scope"])} and access_scope = {sql_literal(row["access_scope"])} and status = {sql_literal(row["status"])} and admission_state = 'admitted' and revision_criteria = {sql_literal(row["revision_criteria"])} and revision_kind = 'original' and supersedes_id is null and confidence is null and tags = {tags} and created_by is not distinct from {sql_literal(row["created_by"])}::uuid and superseded_by is null and accepted_by_proposal_id = {sql_literal(proposal_id)}::uuid) then raise exception 'V3 approve_claim: claim row does not match strict payload: %', {sql_literal(row["id"])}; end if;""" ) for row in bundle["evidence"]: statements.append( f"""insert into public.claim_evidence ( id, claim_id, source_id, role, weight, created_by, excerpt, locator_json, source_content_hash, rationale, polarity, accepted_from_proposal_id ) values ( {sql_literal(row["id"])}::uuid, {sql_literal(row["claim_id"])}::uuid, {sql_literal(row["source_id"])}::uuid, {sql_literal(row["role"])}::evidence_role, null, {sql_literal(row["created_by"])}::uuid, {sql_literal(row["excerpt"])}, {_jsonb(row["locator_json"])}, {sql_literal(row["source_content_hash"])}, {sql_literal(row["rationale"])}, {sql_literal(row["polarity"])}, {sql_literal(proposal_id)}::uuid ) on conflict do nothing;""" ) checks.append( f""" if not exists (select 1 from public.claim_evidence where id = {sql_literal(row["id"])}::uuid and claim_id = {sql_literal(row["claim_id"])}::uuid and source_id = {sql_literal(row["source_id"])}::uuid and role = {sql_literal(row["role"])}::evidence_role and weight is null and created_by is not distinct from {sql_literal(row["created_by"])}::uuid and excerpt is not distinct from {sql_literal(row["excerpt"])} and locator_json = {_jsonb(row["locator_json"])} and source_content_hash = {sql_literal(row["source_content_hash"])} and rationale = {sql_literal(row["rationale"])} and polarity = {sql_literal(row["polarity"])} and accepted_from_proposal_id = {sql_literal(proposal_id)}::uuid) then raise exception 'V3 approve_claim: evidence row does not match strict payload: %', {sql_literal(row["id"])}; end if;""" ) for row in bundle["edges"]: statements.append( f"""insert into public.claim_edges ( id, from_claim, to_claim, edge_type, weight, created_by, relation_type, rationale, scope_or_conditions, accepted_by_proposal_id ) values ( {sql_literal(row["id"])}::uuid, {sql_literal(row["from_claim"])}::uuid, {sql_literal(row["to_claim"])}::uuid, {sql_literal(row["edge_type"])}::edge_type, null, {sql_literal(row["created_by"])}::uuid, {sql_literal(row["relation_type"])}, {sql_literal(row["rationale"])}, {sql_literal(row["scope_or_conditions"])}, {sql_literal(proposal_id)}::uuid ) on conflict do nothing;""" ) checks.append( f""" if not exists (select 1 from public.claim_edges where id = {sql_literal(row["id"])}::uuid and from_claim = {sql_literal(row["from_claim"])}::uuid and to_claim = {sql_literal(row["to_claim"])}::uuid and edge_type = {sql_literal(row["edge_type"])}::edge_type and weight is null and created_by is not distinct from {sql_literal(row["created_by"])}::uuid and relation_type = {sql_literal(row["relation_type"])} and rationale = {sql_literal(row["rationale"])} and scope_or_conditions is not distinct from {sql_literal(row["scope_or_conditions"])} and accepted_by_proposal_id = {sql_literal(proposal_id)}::uuid) then raise exception 'V3 approve_claim: edge row does not match strict payload: %', {sql_literal(row["id"])}; end if;""" ) for row in bundle["assessments"]: statements.append( f"""insert into public.claim_evidence_assessments ( id, claim_id, support_tier, challenge_tier, overall_state, rationale, evidence_set_hash, accepted_by_proposal_id, supersedes_assessment_id ) values ( {sql_literal(row["id"])}::uuid, {sql_literal(row["claim_id"])}::uuid, {sql_literal(row["support_tier"])}, {sql_literal(row["challenge_tier"])}, {sql_literal(row["overall_state"])}, {sql_literal(row["rationale"])}, {sql_literal(row["evidence_set_hash"])}, {sql_literal(proposal_id)}::uuid, {sql_literal(row["supersedes_assessment_id"])}::uuid ) on conflict do nothing;""" ) checks.append( f""" if not exists (select 1 from public.claim_evidence_assessments where id = {sql_literal(row["id"])}::uuid and claim_id = {sql_literal(row["claim_id"])}::uuid and support_tier is not distinct from {sql_literal(row["support_tier"])} and challenge_tier is not distinct from {sql_literal(row["challenge_tier"])} and overall_state = {sql_literal(row["overall_state"])} and rationale = {sql_literal(row["rationale"])} and evidence_set_hash = {sql_literal(row["evidence_set_hash"])} and accepted_by_proposal_id = {sql_literal(proposal_id)}::uuid and supersedes_assessment_id is not distinct from {sql_literal(row["supersedes_assessment_id"])}::uuid) then raise exception 'V3 approve_claim: assessment row does not match strict payload: %', {sql_literal(row["id"])}; end if;""" ) canonical = "\n\n".join(statements) ledger = _ledger_and_verify(proposal_id, applied_by or SERVICE_AGENT_HANDLE, "\n".join(checks), approval) return _wrap_txn( canonical, ledger, _approval_guard_sql(proposal_id, approval), serializable=True, contract_guard_sql=_v3_contract_transaction_guard_sql(), session_advisory_lock_key=V3_CATALOG_DDL_ADVISORY_LOCK_KEY, ) def build_approve_claim_sql( apply_payload: dict[str, Any], proposal_id: str, applied_by: str | None, approval: dict[str, Any], *, fresh_owned: bool = False, fresh_preflight_sha256: str | None = None, ) -> str: """Build one conflict-safe transaction for a reviewed canonical graph bundle.""" _validate_approval_meta(approval, "approve_claim", apply_payload) if apply_payload.get("contract_version") == 3: return build_approve_claim_v3_sql( apply_payload, proposal_id, applied_by, approval, fresh_owned=fresh_owned, fresh_preflight_sha256=fresh_preflight_sha256, ) _reject_unknown( apply_payload, { "contract_version", "source_proposal_id", "agent_id", "claims", "sources", "evidence", "edges", "reasoning_tools", "normalization_manifest", }, "approve_claim apply_payload", ) if apply_payload.get("contract_version") != 2: raise ValueError("approve_claim apply_payload.contract_version must equal 2") for key in ("claims", "sources", "evidence", "edges", "reasoning_tools"): if key not in apply_payload: raise ValueError(f"approve_claim apply_payload requires explicit '{key}' collection") if apply_payload.get("source_proposal_id") is not None: _uuid(apply_payload["source_proposal_id"], "approve_claim.source_proposal_id") if apply_payload.get("normalization_manifest") is not None and not isinstance( apply_payload["normalization_manifest"], dict ): raise ValueError("approve_claim.normalization_manifest must be an object") agent_id = _uuid(apply_payload.get("agent_id"), "approve_claim.agent_id", nullable=True) raw_claims = _rows(apply_payload, "claims", required=True) raw_sources = _rows(apply_payload, "sources") raw_evidence = _rows(apply_payload, "evidence") raw_edges = _rows(apply_payload, "edges") raw_tools = _rows(apply_payload, "reasoning_tools") claims: list[dict[str, Any]] = [] for index, row in enumerate(raw_claims): path = f"approve_claim.claims[{index}]" _reject_unknown( row, {"id", "type", "text", "status", "confidence", "tags", "created_by", "superseded_by"}, path, ) claim_type = row.get("type") if claim_type not in CLAIM_TYPES: raise ValueError(f"{path}.type must be one of {sorted(CLAIM_TYPES)}") text = row.get("text") if not isinstance(text, str) or not text.strip(): raise ValueError(f"{path}.text must be a non-empty string") status = row.get("status", "open") if status != "open": raise ValueError(f"{path}.status must be 'open' for an insert-only approval") tags = row.get("tags") or [] if not isinstance(tags, list) or any(not isinstance(tag, str) for tag in tags): raise ValueError(f"{path}.tags must be a list of strings") claims.append( { "id": _uuid(row.get("id"), f"{path}.id"), "type": claim_type, "text": text, "status": status, "confidence": _weight(row.get("confidence"), f"{path}.confidence"), "tags": tags, "created_by": _uuid(row.get("created_by", agent_id), f"{path}.created_by", nullable=True), "superseded_by": _uuid(row.get("superseded_by"), f"{path}.superseded_by", nullable=True), } ) sources: list[dict[str, Any]] = [] for index, row in enumerate(raw_sources): path = f"approve_claim.sources[{index}]" _reject_unknown( row, {"id", "source_type", "url", "storage_path", "excerpt", "hash", "created_by"}, path, ) source_type = row.get("source_type") if source_type not in SOURCE_TYPES: raise ValueError(f"{path}.source_type must be one of {sorted(SOURCE_TYPES)}") source_hash = row.get("hash") if not isinstance(source_hash, str) or not source_hash.strip(): raise ValueError(f"{path}.hash must be a non-empty string") for key in ("url", "storage_path", "excerpt"): if row.get(key) is not None and not isinstance(row.get(key), str): raise ValueError(f"{path}.{key} must be a string or null") sources.append( { "id": _uuid(row.get("id"), f"{path}.id"), "source_type": source_type, "url": row.get("url"), "storage_path": row.get("storage_path"), "excerpt": row.get("excerpt"), "hash": source_hash, "created_by": _uuid(row.get("created_by", agent_id), f"{path}.created_by", nullable=True), } ) evidence: list[dict[str, Any]] = [] for index, row in enumerate(raw_evidence): path = f"approve_claim.evidence[{index}]" _reject_unknown( row, {"claim_id", "source_id", "role", "weight", "created_by"}, path, ) role = row.get("role", "grounds") if role not in EVIDENCE_ROLES: raise ValueError(f"{path}.role must be one of {sorted(EVIDENCE_ROLES)}") evidence.append( { "claim_id": _uuid(row.get("claim_id"), f"{path}.claim_id"), "source_id": _uuid(row.get("source_id"), f"{path}.source_id"), "role": role, "weight": _weight(row.get("weight"), f"{path}.weight"), "created_by": _uuid(row.get("created_by", agent_id), f"{path}.created_by", nullable=True), } ) edges: list[dict[str, Any]] = [] for index, row in enumerate(raw_edges): path = f"approve_claim.edges[{index}]" _reject_unknown( row, {"from_claim", "to_claim", "edge_type", "weight", "created_by"}, path, ) edge_type = row.get("edge_type") if edge_type not in EDGE_TYPES: raise ValueError(f"{path}.edge_type must be one of {sorted(EDGE_TYPES)}") from_claim = _uuid(row.get("from_claim"), f"{path}.from_claim") to_claim = _uuid(row.get("to_claim"), f"{path}.to_claim") if from_claim == to_claim: raise ValueError(f"{path} cannot be a self-edge") edges.append( { "id": deterministic_row_uuid(proposal_id, "claim_edge", from_claim, to_claim, edge_type), "from_claim": from_claim, "to_claim": to_claim, "edge_type": edge_type, "weight": _weight(row.get("weight"), f"{path}.weight"), "created_by": _uuid(row.get("created_by", agent_id), f"{path}.created_by", nullable=True), } ) reasoning_tools: list[dict[str, Any]] = [] for index, row in enumerate(raw_tools): path = f"approve_claim.reasoning_tools[{index}]" _reject_unknown( row, {"id", "agent_id", "name", "description", "category"}, path, ) name = row.get("name") description = row.get("description") category = row.get("category") if not isinstance(name, str) or not name.strip(): raise ValueError(f"{path}.name must be a non-empty string") if not isinstance(description, str) or not description.strip(): raise ValueError(f"{path}.description must be a non-empty string") if category is not None and not isinstance(category, str): raise ValueError(f"{path}.category must be a string or null") reasoning_tools.append( { "id": _uuid(row.get("id"), f"{path}.id"), "agent_id": _uuid(row.get("agent_id", agent_id), f"{path}.agent_id", nullable=True), "name": name, "description": description, "category": category, } ) _dedupe([row["id"] for row in claims], "approve_claim.claims ids") _dedupe([row["id"] for row in sources], "approve_claim.sources ids") _dedupe([row["hash"] for row in sources], "approve_claim.sources hashes") _dedupe( [[row["claim_id"], row["source_id"], row["role"]] for row in evidence], "approve_claim.evidence keys", ) _dedupe( [[row["from_claim"], row["to_claim"], row["edge_type"]] for row in edges], "approve_claim.edges keys", ) _dedupe([row["id"] for row in reasoning_tools], "approve_claim.reasoning_tools ids") if fresh_owned: if ( not isinstance(fresh_preflight_sha256, str) or len(fresh_preflight_sha256) != 64 or any(character not in "0123456789abcdef" for character in fresh_preflight_sha256) ): raise ValueError("fresh-owned approve_claim requires an exact preflight SHA-256") elif fresh_preflight_sha256 is not None: raise ValueError("fresh preflight SHA-256 is only valid for fresh-owned approve_claim") statements: list[str] = [] checks: list[str] = [] if fresh_owned: statements.extend( [ f"-- fresh-owned preflight SHA-256: {fresh_preflight_sha256}", ( "select pg_advisory_xact_lock(hashtextextended(" f"'proposal-fresh-apply:' || {sql_literal(proposal_id)}, 0));" ), ] ) if sources: collision_checks = [] for row in sources: collision_checks.append( f""" if exists (select 1 from public.sources where hash = {sql_literal(row["hash"])} and id <> {sql_literal(row["id"])}::uuid) then raise exception 'approve_claim: source hash collision for source %', {sql_literal(row["id"])}; end if;""" ) statements.append("do $source_conflicts$\nbegin\n" + "\n".join(collision_checks) + "\nend\n$source_conflicts$;") for row in claims: tags = _text_array(row["tags"]) conflict_clause = ";" if fresh_owned else "\non conflict (id) do nothing;" statements.append( f"""insert into public.claims (id, type, text, status, confidence, tags, created_by, superseded_by) select {sql_literal(row["id"])}::uuid, {sql_literal(row["type"])}, {sql_literal(row["text"])}, {sql_literal(row["status"])}, {sql_literal(row["confidence"])}, {tags}, {sql_literal(row["created_by"])}::uuid, {sql_literal(row["superseded_by"])}::uuid{conflict_clause}""" ) checks.append( f""" if not exists (select 1 from public.claims where id = {sql_literal(row["id"])}::uuid and type = {sql_literal(row["type"])} and text = {sql_literal(row["text"])} and status = {sql_literal(row["status"])} and confidence is not distinct from {sql_literal(row["confidence"])} and tags is not distinct from {tags} and created_by is not distinct from {sql_literal(row["created_by"])}::uuid and superseded_by is not distinct from {sql_literal(row["superseded_by"])}::uuid) then raise exception 'approve_claim: claim row does not match strict apply payload: %', {sql_literal(row["id"])}; end if;""" ) for row in sources: conflict_clause = ";" if fresh_owned else "\non conflict do nothing;" statements.append( f"""insert into public.sources (id, source_type, url, storage_path, excerpt, hash, created_by) select {sql_literal(row["id"])}::uuid, {sql_literal(row["source_type"])}, {sql_literal(row["url"])}, {sql_literal(row["storage_path"])}, {sql_literal(row["excerpt"])}, {sql_literal(row["hash"])}, {sql_literal(row["created_by"])}::uuid{conflict_clause}""" ) checks.append( f""" if exists (select 1 from public.sources where hash = {sql_literal(row["hash"])} and id <> {sql_literal(row["id"])}::uuid) then raise exception 'approve_claim: source hash collision for source %', {sql_literal(row["id"])}; end if; if not exists (select 1 from public.sources where id = {sql_literal(row["id"])}::uuid and source_type = {sql_literal(row["source_type"])} and url is not distinct from {sql_literal(row["url"])} and storage_path is not distinct from {sql_literal(row["storage_path"])} and excerpt is not distinct from {sql_literal(row["excerpt"])} and hash = {sql_literal(row["hash"])} and created_by is not distinct from {sql_literal(row["created_by"])}::uuid) then raise exception 'approve_claim: source row does not match strict apply payload: %', {sql_literal(row["id"])}; end if;""" ) for row in reasoning_tools: conflict_clause = ";" if fresh_owned else "\non conflict (id) do nothing;" statements.append( f"""insert into public.reasoning_tools (id, agent_id, name, description, category) select {sql_literal(row["id"])}::uuid, {sql_literal(row["agent_id"])}::uuid, {sql_literal(row["name"])}, {sql_literal(row["description"])}, {sql_literal(row["category"])}{conflict_clause}""" ) checks.append( f""" if not exists (select 1 from public.reasoning_tools where id = {sql_literal(row["id"])}::uuid and agent_id is not distinct from {sql_literal(row["agent_id"])}::uuid and name = {sql_literal(row["name"])} and description = {sql_literal(row["description"])} and category is not distinct from {sql_literal(row["category"])}) then raise exception 'approve_claim: reasoning tool row does not match strict apply payload: %', {sql_literal(row["id"])}; end if;""" ) for row in evidence: conflict_clause = ";" if fresh_owned else "\non conflict (claim_id, source_id, role) do nothing;" statements.append( f"""insert into public.claim_evidence (claim_id, source_id, role, weight, created_by) select {sql_literal(row["claim_id"])}::uuid, {sql_literal(row["source_id"])}::uuid, {sql_literal(row["role"])}::evidence_role, {sql_literal(row["weight"])}, {sql_literal(row["created_by"])}::uuid{conflict_clause}""" ) checks.append( f""" -- Accept a legacy receipt id only when exactly one semantic row matches. if (select count(*) from public.claim_evidence where claim_id = {sql_literal(row["claim_id"])}::uuid and source_id = {sql_literal(row["source_id"])}::uuid and role = {sql_literal(row["role"])}::evidence_role) <> 1 then raise exception 'approve_claim: evidence row does not match strict apply payload'; end if; if exists (select 1 from public.claim_evidence where claim_id = {sql_literal(row["claim_id"])}::uuid and source_id = {sql_literal(row["source_id"])}::uuid and role = {sql_literal(row["role"])}::evidence_role and (weight is distinct from {sql_literal(row["weight"])} or created_by is distinct from {sql_literal(row["created_by"])}::uuid)) then raise exception 'approve_claim: evidence row does not match strict apply payload'; end if; if not exists (select 1 from public.claim_evidence where claim_id = {sql_literal(row["claim_id"])}::uuid and source_id = {sql_literal(row["source_id"])}::uuid and role = {sql_literal(row["role"])}::evidence_role and weight is not distinct from {sql_literal(row["weight"])} and created_by is not distinct from {sql_literal(row["created_by"])}::uuid) then raise exception 'approve_claim: evidence row does not match strict apply payload'; end if;""" ) for row in edges: edge_lock_key = f"claim_edge:{row['from_claim']}:{row['to_claim']}:{row['edge_type']}" edge_insert_guard = ( ";" if fresh_owned else f"""\n where not exists ( select 1 from public.claim_edges where from_claim = {sql_literal(row["from_claim"])}::uuid and to_claim = {sql_literal(row["to_claim"])}::uuid and edge_type = {sql_literal(row["edge_type"])}::edge_type);""" ) statements.append( f"""select pg_advisory_xact_lock(hashtextextended({sql_literal(edge_lock_key)}, 0)); insert into public.claim_edges (id, from_claim, to_claim, edge_type, weight, created_by) select {sql_literal(row["id"])}::uuid, {sql_literal(row["from_claim"])}::uuid, {sql_literal(row["to_claim"])}::uuid, {sql_literal(row["edge_type"])}::edge_type, {sql_literal(row["weight"])}, {sql_literal(row["created_by"])}::uuid{edge_insert_guard}""" ) checks.append( f""" -- Accept a legacy receipt id only when exactly one semantic row matches. if (select count(*) from public.claim_edges where from_claim = {sql_literal(row["from_claim"])}::uuid and to_claim = {sql_literal(row["to_claim"])}::uuid and edge_type = {sql_literal(row["edge_type"])}::edge_type) <> 1 then raise exception 'approve_claim: edge row does not match strict apply payload'; end if; if exists (select 1 from public.claim_edges where from_claim = {sql_literal(row["from_claim"])}::uuid and to_claim = {sql_literal(row["to_claim"])}::uuid and edge_type = {sql_literal(row["edge_type"])}::edge_type and (weight is distinct from {sql_literal(row["weight"])} or created_by is distinct from {sql_literal(row["created_by"])}::uuid)) then raise exception 'approve_claim: edge row does not match strict apply payload'; end if; if not exists (select 1 from public.claim_edges where from_claim = {sql_literal(row["from_claim"])}::uuid and to_claim = {sql_literal(row["to_claim"])}::uuid and edge_type = {sql_literal(row["edge_type"])}::edge_type and weight is not distinct from {sql_literal(row["weight"])} and created_by is not distinct from {sql_literal(row["created_by"])}::uuid) then raise exception 'approve_claim: edge row does not match strict apply payload'; end if;""" ) canonical = "\n\n".join(statements) ledger = _ledger_and_verify(proposal_id, applied_by or SERVICE_AGENT_HANDLE, "\n".join(checks), approval) return _wrap_txn( canonical, ledger, _approval_guard_sql(proposal_id, approval), serializable=fresh_owned, ) def _wrap_txn( canonical_sql: str, ledger_sql: str, approval_guard_sql: str, *, serializable: bool = False, contract_guard_sql: str = "", session_advisory_lock_key: int | None = None, ) -> str: session_lock_sql = "" session_unlock_sql = "" if session_advisory_lock_key is not None: session_lock_sql = f"select pg_catalog.pg_advisory_lock_shared({session_advisory_lock_key});\n" session_unlock_sql = f"select pg_catalog.pg_advisory_unlock_shared({session_advisory_lock_key});\n" return ( session_lock_sql + "begin;\n" + ("set transaction isolation level serializable;\n" if serializable else "") + "set local timezone = 'UTC';\n" + "set local standard_conforming_strings = on;\n" + (f"{contract_guard_sql}\n" if contract_guard_sql else "") + f"{approval_guard_sql}\n" + f"{canonical_sql}\n" + f"{ledger_sql}\n" + "commit;\n" + session_unlock_sql ) BUILDERS = { "revise_strategy": build_revise_strategy_sql, "add_edge": build_add_edge_sql, "attach_evidence": build_attach_evidence_sql, "approve_claim": build_approve_claim_sql, } def proposal_contract_version(proposal: dict[str, Any]) -> int: """Return the exact typed apply contract or reject malformed/hybrid shapes.""" proposal_type = proposal.get("proposal_type") payload = proposal.get("payload") if not isinstance(payload, dict): raise ValueError("proposal payload must be a JSON object") apply_payload = payload.get("apply_payload") if not isinstance(apply_payload, dict): raise ValueError("proposal payload.apply_payload must be a JSON object") raw_version = apply_payload.get("contract_version") if proposal_type == "approve_claim": if type(raw_version) is not int or raw_version not in {2, 3}: raise ValueError("approve_claim apply_payload.contract_version must be integer 2 or 3") return raw_version if "contract_version" in apply_payload: raise ValueError(f"proposal_type {proposal_type!r} is a V2 legacy contract and must omit contract_version") return 2 def assert_expected_contract_version(proposal: dict[str, Any], expected: int | None) -> int: actual = proposal_contract_version(proposal) if expected is not None and actual != expected: raise ValueError(f"proposal contract version {actual} does not match expected version {expected}") if actual == 3 and proposal.get("proposal_type") != "approve_claim": raise ValueError("V3 apply contract supports only proposal_type 'approve_claim'") return actual def build_apply_sql( proposal: dict[str, Any], applied_by: str | None, *, fresh_owned: bool = False, fresh_preflight_sha256: str | None = None, ) -> str: assert_expected_contract_version(proposal, None) ptype = proposal["proposal_type"] if ptype not in BUILDERS: raise ValueError(f"apply_proposal cannot apply proposal_type {ptype!r}") payload = proposal["payload"] apply_payload = payload.get("apply_payload") if apply_payload is None: raise ValueError( "proposal payload has no 'apply_payload' strict contract. " "Run the normalizer to produce apply_payload before applying." ) if fresh_owned and ptype != "approve_claim": raise ValueError("fresh-owned apply is supported only for approve_claim") if ptype == "approve_claim": return build_approve_claim_sql( apply_payload, proposal["id"], applied_by or SERVICE_AGENT_HANDLE, proposal, fresh_owned=fresh_owned, fresh_preflight_sha256=fresh_preflight_sha256, ) return BUILDERS[ptype]( apply_payload, proposal["id"], applied_by or SERVICE_AGENT_HANDLE, proposal, ) def assert_applyable(proposal: dict[str, Any]) -> None: status = proposal.get("status") if status == "applied": raise SystemExit(f"proposal {proposal['id']} is already applied (idempotent no-op)") if status != "approved": raise SystemExit(f"proposal {proposal['id']} has status {status!r}; only 'approved' proposals apply") def assert_receiptable(proposal: dict[str, Any]) -> None: if proposal.get("status") != "applied": raise SystemExit( f"proposal {proposal['id']} has status {proposal.get('status')!r}; " "--receipt-only requires an applied proposal" ) replay_receipt.expected_row_counts(proposal) # --------------------------------------------------------------------------- # # Execution (docker exec as the kb_apply role) # # --------------------------------------------------------------------------- # def load_password(secrets_file: str) -> str: path = Path(secrets_file) if not path.is_file(): raise SystemExit(f"secrets file not found: {secrets_file}") for line in path.read_text(encoding="utf-8").splitlines(): line = line.strip() if not line or line.startswith("#") or "=" not in line: continue key, _, val = line.partition("=") if key.strip() in ("KB_APPLY_PASSWORD", "KB_APPLY_DB_PASSWORD", "PGPASSWORD"): return val.strip().strip('"').strip("'") raise SystemExit(f"no KB_APPLY_PASSWORD/KB_APPLY_DB_PASSWORD/PGPASSWORD entry in {secrets_file}") def run_psql( args: argparse.Namespace, sql: str, password: str, *, redact_output_on_error: bool = False, ) -> str: docker_binary = shutil.which("docker") or "docker" command = [ docker_binary, "exec", "-e", "PGPASSWORD", "-i", args.container, "psql", "-U", args.role, "-h", args.host, "-d", args.db, "-v", "ON_ERROR_STOP=1", "-At", "-q", ] session_sql = "set timezone = 'UTC';\n" + sql result = subprocess.run( command, input=session_sql, text=True, capture_output=True, env={"PGPASSWORD": password, "PATH": "/usr/bin:/bin:/usr/local/bin"}, check=False, ) if result.returncode != 0: if redact_output_on_error: raise SystemExit(f"psql failed ({result.returncode}); private postflight output redacted") raise SystemExit(f"psql failed ({result.returncode})\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}") return result.stdout def load_proposal(args: argparse.Namespace, password: str) -> dict[str, Any]: sql = f"""select jsonb_build_object( 'id', id::text, 'proposal_type', proposal_type, 'status', status, 'payload', payload, 'reviewed_by_handle', reviewed_by_handle, 'reviewed_by_agent_id', reviewed_by_agent_id::text, 'reviewed_at', case when reviewed_at is null then null else to_char(reviewed_at at time zone 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"') end, 'review_note', review_note, 'applied_by_handle', applied_by_handle, 'applied_by_agent_id', applied_by_agent_id::text, 'applied_at', case when applied_at is null then null else to_char(applied_at at time zone 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"') end)::text from kb_stage.kb_proposals where id = {sql_literal(args.proposal_id)}::uuid;""" out = run_psql(args, sql, password).strip() if not out: raise SystemExit(f"proposal not found: {args.proposal_id}") return json.loads(out) def capture_replay_receipt( args: argparse.Namespace, proposal: dict[str, Any], password: str, *, apply_sql: str | None = None, ) -> tuple[Path, dict[str, Any]]: """Read exact postflight rows and atomically write a private replay receipt.""" postflight_sql = replay_receipt.build_postflight_sql(proposal) raw = run_psql( args, postflight_sql, password, redact_output_on_error=True, ).strip() if not raw: raise RuntimeError("postflight query returned no row") canonical_rows = json.loads(raw) if not isinstance(canonical_rows, dict): raise RuntimeError("postflight query did not return a JSON object") apply_sql_source = "exact_executed_sql" if apply_sql is None: apply_sql = build_apply_sql_for_args(proposal, args, proposal.get("applied_by_handle")) apply_sql_source = "reconstructed_current_engine" receipt = replay_receipt.build_replay_receipt( proposal, canonical_rows, apply_sql_sha256=replay_receipt.sha256_text(apply_sql), apply_sql_source=apply_sql_source, ) path = replay_receipt.resolve_receipt_path( str(proposal["id"]), receipt_out=args.receipt_out, receipt_dir=args.receipt_dir, ) replay_receipt.write_private_receipt(path, receipt) return path, receipt def parse_args(argv: list[str]) -> argparse.Namespace: p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) p.add_argument("proposal_id", help="UUID of the approved proposal to apply") p.add_argument( "--applied-by", default=SERVICE_AGENT_HANDLE, help="agent handle recorded as applied_by_handle and resolved to the " "applied_by_agent_id FK (default: the kb-apply service agent)", ) p.add_argument("--dry-run", action="store_true", help="print the SQL, do not execute") p.add_argument("--secrets-file", default=DEFAULT_SECRETS_FILE) p.add_argument("--container", default=DEFAULT_CONTAINER) p.add_argument("--db", default=DEFAULT_DB) p.add_argument("--host", default=DEFAULT_HOST) p.add_argument("--role", default=DEFAULT_ROLE) p.add_argument( "--expected-contract-version", type=int, choices=(2, 3), default=None, help="reassert one exact proposal contract before any mutating SQL or receipt recovery", ) p.add_argument( "--fresh-preflight", type=Path, help=( "exact proposal_apply_fresh_preflight JSON; makes approve_claim use strict fresh-owned inserts " "bound to the timestamped preflight" ), ) p.add_argument( "--receipt-only", action="store_true", help="recover the private row-level replay receipt for an already applied proposal; no writes", ) p.add_argument( "--receipt-dir", default=None, help=f"private receipt directory (default: KB_APPLY_RECEIPT_DIR or {replay_receipt.DEFAULT_RECEIPT_DIR})", ) p.add_argument( "--receipt-out", default=None, help="exact private receipt path; overrides --receipt-dir", ) args = p.parse_args(argv) if args.receipt_only and args.dry_run: p.error("--receipt-only and --dry-run are mutually exclusive") return args def build_apply_sql_for_args( proposal: dict[str, Any], args: argparse.Namespace, applied_by: str | None, ) -> str: fresh_preflight_path = getattr(args, "fresh_preflight", None) if fresh_preflight_path is None: return build_apply_sql(proposal, applied_by) if not fresh_preflight_path.is_file(): raise SystemExit(f"fresh preflight file not found: {fresh_preflight_path}") try: preflight = json.loads(fresh_preflight_path.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError) as exc: raise SystemExit(f"fresh preflight is unreadable: {fresh_preflight_path}") from exc import proposal_apply_lifecycle as lifecycle try: return lifecycle.build_fresh_owned_apply_sql(proposal, preflight, applied_by) except ValueError as exc: raise SystemExit(f"fresh preflight refused: {exc}") from exc def main(argv: list[str] | None = None) -> int: args = parse_args(sys.argv[1:] if argv is None else argv) if args.receipt_only: password = load_password(args.secrets_file) proposal = load_proposal(args, password) assert_expected_contract_version(proposal, args.expected_contract_version) assert_receiptable(proposal) path, receipt = capture_replay_receipt(args, proposal, password) print( json.dumps( { "proposal_id": proposal["id"], "receipt_path": str(path), "replay_material_sha256": receipt["hashes"]["replay_material_sha256"], "replay_ready": receipt["replay_ready"], }, sort_keys=True, ) ) return 0 if args.dry_run: # Dry-run still needs the proposal to build SQL; read it as kb_apply. password = load_password(args.secrets_file) proposal = load_proposal(args, password) assert_expected_contract_version(proposal, args.expected_contract_version) assert_applyable(proposal) print(build_apply_sql_for_args(proposal, args, args.applied_by)) return 0 password = load_password(args.secrets_file) proposal = load_proposal(args, password) assert_expected_contract_version(proposal, args.expected_contract_version) assert_applyable(proposal) sql = build_apply_sql_for_args(proposal, args, args.applied_by) run_psql(args, sql, password) applied_proposal = load_proposal(args, password) try: path, receipt = capture_replay_receipt( args, applied_proposal, password, apply_sql=sql, ) except Exception as exc: raise SystemExit( f"proposal {proposal['id']} committed, but private replay receipt capture failed: {exc}. " f"Recover without reapplying via: {Path(__file__).name} {proposal['id']} --receipt-only" ) from exc print( json.dumps( { "applied": True, "proposal_id": proposal["id"], "proposal_type": proposal["proposal_type"], "receipt_path": str(path), "replay_material_sha256": receipt["hashes"]["replay_material_sha256"], "replay_ready": receipt["replay_ready"], }, sort_keys=True, ) ) return 0 if __name__ == "__main__": raise SystemExit(main())