-- Minimum additive LivingIP V3 epistemic contract for the canonical KB tables. -- -- This is a compatibility migration, not a production rollout. Existing rows -- remain in the visible legacy tier. Only rows explicitly linked to an accepted -- proposal are required to satisfy the modern provenance contract. -- -- CUTOVER ORDER: deploy the cutover-aware writer, stop the default V2 worker, -- ALTER ROLE kb_apply NOLOGIN, and end every kb_apply session before running this -- migration. Approved historical V2 proposals remain review-visible, but the V3 -- contract blocks their execution until each packet is migrated or rejected. \set ON_ERROR_STOP on set timezone = 'UTC'; begin; -- Stable bigint key: controlled V3 function/trigger DDL is exclusive while -- authorized applies hold the shared transaction form through commit/rollback. select pg_catalog.pg_advisory_xact_lock(6072343533146485505); -- The worker mirrors this declaration, and tests require byte-equivalent -- catalog predicates in preflight/postflight. SHA-256 pins every guard body. do $trigger_contract_declaration$ begin perform pg_catalog.set_config( 'teleo.v3_expected_trigger_contract', $trigger_contract$ [ { "argument_count": 0, "argument_hex": "", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_enforce_apply_contract_cutover", "function_owner": "kb_gate_owner", "function_owner_only_execute": true, "function_schema": "kb_stage", "function_security_definer": true, "function_source_sha256": "3ab2d5e1d5d1d18b13579fe2c0cae487036c7b147cf7f1b6b9a3633274756c18", "schema_name": "kb_stage", "table_name": "kb_proposals", "trigger_name": "teleo_v3_00_enforce_apply_contract_cutover", "trigger_type": 23, "update_columns": [ "payload", "proposal_type", "status" ], "when_expression": null }, { "argument_count": 2, "argument_hex": "61636365707465645f62795f70726f706f73616c5f69640061646d697373696f6e5f737461746500", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_require_linked_insert", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "d8c190bd2fc90159b9a4824015c10a73a701a263bba90b67f8ccce75e454e6f8", "schema_name": "public", "table_name": "claims", "trigger_name": "teleo_v3_00_claim_require_linked_insert", "trigger_type": 7, "update_columns": [], "when_expression": null }, { "argument_count": 1, "argument_hex": "61636365707465645f62795f70726f706f73616c5f696400", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_require_accepted_proposal", "function_owner": "kb_gate_owner", "function_owner_only_execute": true, "function_schema": "kb_stage", "function_security_definer": true, "function_source_sha256": "ca398ef2f185488c0712bd5ebc22402ab9e41e5dfd2f5823b367e6a382e8a64f", "schema_name": "public", "table_name": "claims", "trigger_name": "teleo_v3_claim_require_accepted_proposal", "trigger_type": 23, "update_columns": [ "accepted_by_proposal_id" ], "when_expression": null }, { "argument_count": 1, "argument_hex": "61636365707465645f62795f70726f706f73616c5f696400", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_require_linked_insert", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "d8c190bd2fc90159b9a4824015c10a73a701a263bba90b67f8ccce75e454e6f8", "schema_name": "public", "table_name": "sources", "trigger_name": "teleo_v3_00_source_require_linked_insert", "trigger_type": 7, "update_columns": [], "when_expression": null }, { "argument_count": 20, "argument_hex": "6964006f776e65725f6167656e745f6964007479706500746578740070726f706f736974696f6e00626f64795f6d61726b646f776e0073636f7065006163636573735f73636f7065007374617475730061646d697373696f6e5f7374617465007265766973696f6e5f6372697465726961007265766973696f6e5f6b696e6400737570657273656465735f696400737570657273656465645f627900636f6e666964656e6365007461677300637265617465645f627900637265617465645f617400757064617465645f61740061636365707465645f62795f70726f706f73616c5f696400", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_guard_immutable_fields", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "d243741e552fe8c232440ffa5f853cf261f4b86b6f32d76f375734a2235c2d13", "schema_name": "public", "table_name": "claims", "trigger_name": "teleo_v3_claim_immutable", "trigger_type": 19, "update_columns": [], "when_expression": null }, { "argument_count": 0, "argument_hex": "", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_reject_delete", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "46190723ab4520954858dbd4bb6c9230e68e41c68b5aee3b8c2719e1416de43c", "schema_name": "public", "table_name": "claims", "trigger_name": "teleo_v3_claim_reject_delete", "trigger_type": 11, "update_columns": [], "when_expression": null }, { "argument_count": 1, "argument_hex": "61636365707465645f66726f6d5f70726f706f73616c5f696400", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_require_linked_insert", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "d8c190bd2fc90159b9a4824015c10a73a701a263bba90b67f8ccce75e454e6f8", "schema_name": "public", "table_name": "claim_evidence", "trigger_name": "teleo_v3_00_evidence_require_linked_insert", "trigger_type": 7, "update_columns": [], "when_expression": null }, { "argument_count": 1, "argument_hex": "61636365707465645f62795f70726f706f73616c5f696400", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_require_accepted_proposal", "function_owner": "kb_gate_owner", "function_owner_only_execute": true, "function_schema": "kb_stage", "function_security_definer": true, "function_source_sha256": "ca398ef2f185488c0712bd5ebc22402ab9e41e5dfd2f5823b367e6a382e8a64f", "schema_name": "public", "table_name": "sources", "trigger_name": "teleo_v3_source_require_accepted_proposal", "trigger_type": 23, "update_columns": [ "accepted_by_proposal_id" ], "when_expression": null }, { "argument_count": 1, "argument_hex": "61636365707465645f62795f70726f706f73616c5f696400", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_require_linked_insert", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "d8c190bd2fc90159b9a4824015c10a73a701a263bba90b67f8ccce75e454e6f8", "schema_name": "public", "table_name": "claim_edges", "trigger_name": "teleo_v3_00_edge_require_linked_insert", "trigger_type": 7, "update_columns": [], "when_expression": null }, { "argument_count": 18, "argument_hex": "696400736f757263655f747970650075726c0073746f726167655f70617468006578636572707400686173680063616e6f6e6963616c5f75726c0073746f726167655f75726900636f6e74656e745f68617368006163636573735f73636f706500696e67657374696f6e5f6f726967696e0070726f76656e616e63655f73746174757300736f757263655f636c617373006175746f5f70726f6d6f74696f6e5f706f6c69637900637265617465645f62790063617074757265645f617400637265617465645f61740061636365707465645f62795f70726f706f73616c5f696400", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_guard_immutable_fields", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "d243741e552fe8c232440ffa5f853cf261f4b86b6f32d76f375734a2235c2d13", "schema_name": "public", "table_name": "sources", "trigger_name": "teleo_v3_source_immutable", "trigger_type": 19, "update_columns": [], "when_expression": null }, { "argument_count": 0, "argument_hex": "", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_reject_delete", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "46190723ab4520954858dbd4bb6c9230e68e41c68b5aee3b8c2719e1416de43c", "schema_name": "public", "table_name": "sources", "trigger_name": "teleo_v3_source_reject_delete", "trigger_type": 11, "update_columns": [], "when_expression": null }, { "argument_count": 1, "argument_hex": "61636365707465645f66726f6d5f70726f706f73616c5f696400", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_require_accepted_proposal", "function_owner": "kb_gate_owner", "function_owner_only_execute": true, "function_schema": "kb_stage", "function_security_definer": true, "function_source_sha256": "ca398ef2f185488c0712bd5ebc22402ab9e41e5dfd2f5823b367e6a382e8a64f", "schema_name": "public", "table_name": "claim_evidence", "trigger_name": "teleo_v3_evidence_require_accepted_proposal", "trigger_type": 23, "update_columns": [ "accepted_from_proposal_id" ], "when_expression": null }, { "argument_count": 0, "argument_hex": "", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_validate_claim_evidence", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "4c697c497699760214aca99f255c215e393319fd4482d4b36efac839280c3733", "schema_name": "public", "table_name": "claim_evidence", "trigger_name": "teleo_v3_evidence_validate_provenance", "trigger_type": 23, "update_columns": [], "when_expression": null }, { "argument_count": 13, "argument_hex": "696400636c61696d5f696400736f757263655f696400726f6c65007765696768740065786365727074006c6f6361746f725f6a736f6e00736f757263655f636f6e74656e745f6861736800726174696f6e616c6500706f6c617269747900637265617465645f627900637265617465645f61740061636365707465645f66726f6d5f70726f706f73616c5f696400", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_guard_immutable_fields", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "d243741e552fe8c232440ffa5f853cf261f4b86b6f32d76f375734a2235c2d13", "schema_name": "public", "table_name": "claim_evidence", "trigger_name": "teleo_v3_evidence_immutable", "trigger_type": 19, "update_columns": [], "when_expression": null }, { "argument_count": 0, "argument_hex": "", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_reject_delete", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "46190723ab4520954858dbd4bb6c9230e68e41c68b5aee3b8c2719e1416de43c", "schema_name": "public", "table_name": "claim_evidence", "trigger_name": "teleo_v3_evidence_reject_delete", "trigger_type": 11, "update_columns": [], "when_expression": null }, { "argument_count": 1, "argument_hex": "61636365707465645f62795f70726f706f73616c5f696400", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_require_accepted_proposal", "function_owner": "kb_gate_owner", "function_owner_only_execute": true, "function_schema": "kb_stage", "function_security_definer": true, "function_source_sha256": "ca398ef2f185488c0712bd5ebc22402ab9e41e5dfd2f5823b367e6a382e8a64f", "schema_name": "public", "table_name": "claim_edges", "trigger_name": "teleo_v3_edge_require_accepted_proposal", "trigger_type": 23, "update_columns": [ "accepted_by_proposal_id" ], "when_expression": null }, { "argument_count": 0, "argument_hex": "", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_validate_claim_edge", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "6a7a3e9d1d5ac6e9490abc9b7b1f321bb2e0948aea2671415aad28101f4ee936", "schema_name": "public", "table_name": "claim_edges", "trigger_name": "teleo_v3_edge_validate_endpoints", "trigger_type": 23, "update_columns": [], "when_expression": null }, { "argument_count": 11, "argument_hex": "69640066726f6d5f636c61696d00746f5f636c61696d00656467655f74797065007765696768740072656c6174696f6e5f7479706500726174696f6e616c650073636f70655f6f725f636f6e646974696f6e7300637265617465645f627900637265617465645f61740061636365707465645f62795f70726f706f73616c5f696400", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_guard_immutable_fields", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "d243741e552fe8c232440ffa5f853cf261f4b86b6f32d76f375734a2235c2d13", "schema_name": "public", "table_name": "claim_edges", "trigger_name": "teleo_v3_edge_immutable", "trigger_type": 19, "update_columns": [], "when_expression": null }, { "argument_count": 0, "argument_hex": "", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_reject_delete", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "46190723ab4520954858dbd4bb6c9230e68e41c68b5aee3b8c2719e1416de43c", "schema_name": "public", "table_name": "claim_edges", "trigger_name": "teleo_v3_edge_reject_delete", "trigger_type": 11, "update_columns": [], "when_expression": null }, { "argument_count": 1, "argument_hex": "61636365707465645f62795f70726f706f73616c5f696400", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_require_accepted_proposal", "function_owner": "kb_gate_owner", "function_owner_only_execute": true, "function_schema": "kb_stage", "function_security_definer": true, "function_source_sha256": "ca398ef2f185488c0712bd5ebc22402ab9e41e5dfd2f5823b367e6a382e8a64f", "schema_name": "public", "table_name": "claim_evidence_assessments", "trigger_name": "teleo_v3_assessment_require_accepted_proposal", "trigger_type": 23, "update_columns": [ "accepted_by_proposal_id" ], "when_expression": null }, { "argument_count": 0, "argument_hex": "", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_validate_evidence_assessment", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "860f939b4878abe8e2e604e03be9eeb6c23b4878af4b19f9ec42de4b821fac61", "schema_name": "public", "table_name": "claim_evidence_assessments", "trigger_name": "teleo_v3_assessment_validate", "trigger_type": 23, "update_columns": [], "when_expression": null }, { "argument_count": 10, "argument_hex": "696400636c61696d5f696400737570706f72745f74696572006368616c6c656e67655f74696572006f766572616c6c5f737461746500726174696f6e616c650065766964656e63655f7365745f686173680061636365707465645f62795f70726f706f73616c5f696400737570657273656465735f6173736573736d656e745f696400637265617465645f617400", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_guard_immutable_fields", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "d243741e552fe8c232440ffa5f853cf261f4b86b6f32d76f375734a2235c2d13", "schema_name": "public", "table_name": "claim_evidence_assessments", "trigger_name": "teleo_v3_assessment_immutable", "trigger_type": 19, "update_columns": [], "when_expression": null }, { "argument_count": 0, "argument_hex": "", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_reject_delete", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "46190723ab4520954858dbd4bb6c9230e68e41c68b5aee3b8c2719e1416de43c", "schema_name": "public", "table_name": "claim_evidence_assessments", "trigger_name": "teleo_v3_assessment_reject_delete", "trigger_type": 11, "update_columns": [], "when_expression": null }, { "argument_count": 0, "argument_hex": "", "function_config": [ "search_path=pg_catalog, pg_temp" ], "function_identity_arguments": "", "function_name": "teleo_v3_guard_linked_proposal_decision", "function_owner": "kb_gate_owner", "function_owner_only_execute": null, "function_schema": "kb_stage", "function_security_definer": false, "function_source_sha256": "602ee8f81f4515b0237f09a503ecbfe4fce2dc4a799e13f833554103b0e83e32", "schema_name": "kb_stage", "table_name": "kb_proposals", "trigger_name": "teleo_v3_guard_linked_proposal_decision", "trigger_type": 19, "update_columns": [ "applied_at", "applied_by_agent_id", "applied_by_handle", "payload", "proposal_type", "rationale", "review_note", "reviewed_at", "reviewed_by_agent_id", "reviewed_by_handle", "status" ], "when_expression": null } ] $trigger_contract$, true ); end $trigger_contract_declaration$; do $preflight$ declare v_contract_state text; v_apply_can_login boolean; v_legacy_queue_count bigint; v_preexisting_objects text; v_ownership_valid boolean; begin if to_regclass('public.agents') is null then raise exception 'teleo_v3_epistemic_contract: missing public.agents'; end if; if to_regclass('public.claims') is null then raise exception 'teleo_v3_epistemic_contract: missing public.claims'; end if; if to_regclass('public.sources') is null then raise exception 'teleo_v3_epistemic_contract: missing public.sources'; end if; if to_regclass('public.claim_evidence') is null then raise exception 'teleo_v3_epistemic_contract: missing public.claim_evidence'; end if; if to_regclass('public.claim_edges') is null then raise exception 'teleo_v3_epistemic_contract: missing public.claim_edges'; end if; if to_regclass('kb_stage.kb_proposals') is null then raise exception 'teleo_v3_epistemic_contract: missing kb_stage.kb_proposals'; end if; if not exists ( select 1 from pg_catalog.pg_roles role where role.rolname = 'kb_gate_owner' and not role.rolcanlogin and not role.rolinherit and not role.rolsuper and not role.rolcreatedb and not role.rolcreaterole and not role.rolreplication and not role.rolbypassrls ) then raise exception using errcode = '55000', message = 'teleo_v3_epistemic_contract: kb_gate_owner must be NOLOGIN NOINHERIT and unprivileged'; end if; if not exists (select 1 from pg_catalog.pg_roles where rolname = 'kb_apply') then raise exception 'teleo_v3_epistemic_contract: missing runtime role kb_apply'; end if; if not exists (select 1 from pg_catalog.pg_roles where rolname = 'kb_review') then raise exception 'teleo_v3_epistemic_contract: missing runtime role kb_review'; end if; if 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 ('kb_gate_owner', 'kb_review', 'kb_apply') or granted_role.rolname in ('kb_gate_owner', 'kb_review', 'kb_apply') ) then raise exception using errcode = '55000', message = 'teleo_v3_epistemic_contract: protected role membership drift must be removed'; end if; if not pg_catalog.has_schema_privilege('kb_gate_owner', 'kb_stage', 'USAGE') or not pg_catalog.has_table_privilege('kb_gate_owner', 'kb_stage.kb_proposals', 'SELECT') or not pg_catalog.has_table_privilege('kb_gate_owner', 'kb_stage.kb_proposals', 'UPDATE') then raise exception 'teleo_v3_epistemic_contract: kb_gate_owner lacks narrow proposal-ledger lock access'; end if; -- TELEO_V3_CONTRACT_STATE_QUERY_BEGIN with expected_triggers as ( select expected.* from pg_catalog.jsonb_to_recordset(pg_catalog.current_setting('teleo.v3_expected_trigger_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, 9) = E'teleo_v3_' and procedure.proname <> E'teleo_v3_acquire_protected_relation_locks' ), 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 = E'teleo_v3_acquire_protected_relation_locks' ), 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, 9) = E'teleo_v3_' ), 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 = E'kb_gate_owner' and function_security_definer and function_config = '["search_path=pg_catalog, pg_temp"]'::jsonb and function_source_sha256 = E'897345944ba0f1b2a5c01b098fb17de6f881a122e906277086fa7889e76219e8' 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 = E'kb_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 (E'kb_gate_owner', E'kb_review', E'kb_apply') or granted_role.rolname in (E'kb_gate_owner', E'kb_review', E'kb_apply') ) 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 v_contract_state from catalog_state; -- TELEO_V3_CONTRACT_STATE_QUERY_END if v_contract_state <> 'V3' then select role.rolcanlogin into v_apply_can_login from pg_catalog.pg_roles role where role.rolname = 'kb_apply'; if v_apply_can_login then raise exception using errcode = '55000', message = 'teleo_v3 cutover refused before DDL: stop the V2 worker and ALTER ROLE kb_apply NOLOGIN'; end if; if exists ( select 1 from pg_catalog.pg_stat_activity activity where activity.datid = (select oid from pg_catalog.pg_database where datname = current_database()) and activity.usename = 'kb_apply' and activity.pid <> pg_catalog.pg_backend_pid() ) then raise exception using errcode = '55000', message = 'teleo_v3 cutover refused before DDL: active kb_apply sessions must exit'; end if; end if; select pg_catalog.count(*) into v_legacy_queue_count from kb_stage.kb_proposals proposal where proposal.status::text in ('pending_review', 'approved') and proposal.proposal_type in ('revise_strategy', 'add_edge', 'attach_evidence', 'approve_claim') and not ( proposal.proposal_type = 'approve_claim' and pg_catalog.jsonb_typeof(proposal.payload) = 'object' and pg_catalog.jsonb_typeof(proposal.payload->'apply_payload') = 'object' and pg_catalog.jsonb_typeof(proposal.payload->'apply_payload'->'contract_version') = 'number' and proposal.payload->'apply_payload'->>'contract_version' = '3' ); if v_legacy_queue_count > 0 then raise notice 'teleo_v3 cutover retains % pending/approved V2 proposal(s) for explicit migration or rejection', v_legacy_queue_count; end if; if not exists ( select 1 from information_schema.columns where table_schema = 'public' and table_name = 'claims' and column_name = 'text' ) then raise exception 'teleo_v3_epistemic_contract: public.claims.text compatibility column is required'; end if; if not exists ( select 1 from information_schema.columns where table_schema = 'public' and table_name = 'sources' and column_name = 'hash' ) then raise exception 'teleo_v3_epistemic_contract: public.sources.hash compatibility column is required'; end if; if not exists ( select 1 from information_schema.columns where table_schema = 'public' and table_name = 'claim_edges' and column_name = 'edge_type' ) then raise exception 'teleo_v3_epistemic_contract: public.claim_edges.edge_type compatibility column is required'; end if; if v_contract_state = 'V2' then with expected_columns(table_name, column_name) as ( values ('claims', 'owner_agent_id'), ('claims', 'proposition'), ('claims', 'body_markdown'), ('claims', 'scope'), ('claims', 'access_scope'), ('claims', 'admission_state'), ('claims', 'revision_criteria'), ('claims', 'revision_kind'), ('claims', 'supersedes_id'), ('claims', 'accepted_by_proposal_id'), ('sources', 'canonical_url'), ('sources', 'storage_uri'), ('sources', 'content_hash'), ('sources', 'access_scope'), ('sources', 'ingestion_origin'), ('sources', 'provenance_status'), ('sources', 'source_class'), ('sources', 'auto_promotion_policy'), ('sources', 'accepted_by_proposal_id'), ('claim_evidence', 'excerpt'), ('claim_evidence', 'locator_json'), ('claim_evidence', 'source_content_hash'), ('claim_evidence', 'rationale'), ('claim_evidence', 'polarity'), ('claim_evidence', 'accepted_from_proposal_id'), ('claim_edges', 'relation_type'), ('claim_edges', 'rationale'), ('claim_edges', 'scope_or_conditions'), ('claim_edges', 'accepted_by_proposal_id') ), present_objects(object_name) as ( select pg_catalog.format('%I.%I', columns.table_name, columns.column_name) from information_schema.columns columns join expected_columns expected on expected.table_name = columns.table_name and expected.column_name = columns.column_name where columns.table_schema = 'public' union all select 'public.claim_evidence_assessments' where pg_catalog.to_regclass('public.claim_evidence_assessments') is not null union all select 'kb_stage.teleo_v3_installation_ownership' where pg_catalog.to_regclass('kb_stage.teleo_v3_installation_ownership') is not null ) select pg_catalog.string_agg(object_name, ', ' order by object_name) into v_preexisting_objects from present_objects; if v_preexisting_objects is not null then raise exception using errcode = '55000', message = pg_catalog.format( 'teleo_v3_epistemic_contract: preexisting V3 schema objects lack installation ownership: %s', v_preexisting_objects ); end if; elsif v_contract_state in ('V3', 'INVALID') then if pg_catalog.to_regclass('kb_stage.teleo_v3_installation_ownership') is null then if v_contract_state = 'INVALID' then raise exception using errcode = '55000', message = 'teleo_v3_epistemic_contract: partial or altered V3 contract lacks exact installation ownership'; end if; raise exception using errcode = '55000', message = 'teleo_v3_epistemic_contract: existing V3 contract lacks exact installation ownership'; end if; select owner_role.rolname = 'kb_gate_owner' into v_ownership_valid from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace join pg_catalog.pg_roles owner_role on owner_role.oid = relation.relowner where namespace.nspname = 'kb_stage' and relation.relname = 'teleo_v3_installation_ownership' and relation.relkind in ('r', 'p'); if not coalesce(v_ownership_valid, false) then raise exception using errcode = '55000', message = 'teleo_v3_epistemic_contract: installation ownership ledger owner is invalid'; end if; execute $ownership_query$ select count(*) = 1 and bool_and( contract_name = 'livingip.teleo-v3-epistemic-contract' and contract_version = 1 and installation_token = 'collision-free-install-v1' ) from kb_stage.teleo_v3_installation_ownership $ownership_query$ into v_ownership_valid; if not coalesce(v_ownership_valid, false) then raise exception using errcode = '55000', message = 'teleo_v3_epistemic_contract: installation ownership ledger is not exact'; end if; end if; end $preflight$; create table if not exists kb_stage.teleo_v3_installation_ownership ( contract_name text primary key, contract_version integer not null, installation_token text not null, constraint teleo_v3_installation_contract_check check ( contract_name = 'livingip.teleo-v3-epistemic-contract' and contract_version = 1 ), constraint teleo_v3_installation_token_check check ( installation_token = 'collision-free-install-v1' ) ); alter table kb_stage.teleo_v3_installation_ownership owner to kb_gate_owner; revoke all privileges on table kb_stage.teleo_v3_installation_ownership from public, kb_apply, kb_review; insert into kb_stage.teleo_v3_installation_ownership ( contract_name, contract_version, installation_token ) values ( 'livingip.teleo-v3-epistemic-contract', 1, 'collision-free-install-v1' ) on conflict (contract_name) do nothing; alter table public.claims add column if not exists owner_agent_id uuid, add column if not exists proposition text, add column if not exists body_markdown text, add column if not exists scope text, add column if not exists access_scope text not null default 'collective_internal', add column if not exists admission_state text not null default 'legacy', add column if not exists revision_criteria text, add column if not exists revision_kind text, add column if not exists supersedes_id uuid, add column if not exists accepted_by_proposal_id uuid; alter table public.sources add column if not exists canonical_url text, add column if not exists storage_uri text, add column if not exists content_hash text, add column if not exists access_scope text, add column if not exists ingestion_origin text, add column if not exists provenance_status text, add column if not exists source_class text, add column if not exists auto_promotion_policy text, add column if not exists accepted_by_proposal_id uuid; alter table public.claim_evidence add column if not exists excerpt text, add column if not exists locator_json jsonb, add column if not exists source_content_hash text, add column if not exists rationale text, add column if not exists polarity text, add column if not exists accepted_from_proposal_id uuid; alter table public.claim_edges add column if not exists relation_type text, add column if not exists rationale text, add column if not exists scope_or_conditions text, add column if not exists accepted_by_proposal_id uuid; create table if not exists public.claim_evidence_assessments ( id uuid primary key default gen_random_uuid(), claim_id uuid not null references public.claims(id), support_tier text, challenge_tier text, overall_state text not null, rationale text not null, evidence_set_hash text not null, accepted_by_proposal_id uuid not null references kb_stage.kb_proposals(id), supersedes_assessment_id uuid references public.claim_evidence_assessments(id), created_at timestamptz not null default now(), constraint teleo_v3_assessment_support_tier_check check ( support_tier is null or support_tier in ('none', 'weak', 'moderate', 'strong') ), constraint teleo_v3_assessment_challenge_tier_check check ( challenge_tier is null or challenge_tier in ('none', 'weak', 'moderate', 'strong') ), constraint teleo_v3_assessment_overall_state_check check ( overall_state in ('insufficient', 'support_dominant', 'challenge_dominant', 'mixed') ), constraint teleo_v3_assessment_rationale_check check (btrim(rationale) <> ''), constraint teleo_v3_assessment_evidence_set_hash_check check ( evidence_set_hash ~ '^[0-9a-f]{64}$' ), constraint teleo_v3_assessment_not_self_superseding_check check ( supersedes_assessment_id is null or supersedes_assessment_id <> id ) ); do $constraints$ begin if not exists ( select 1 from pg_constraint where conrelid = 'public.claims'::regclass and conname = 'teleo_v3_claim_owner_agent_fkey' ) then alter table public.claims add constraint teleo_v3_claim_owner_agent_fkey foreign key (owner_agent_id) references public.agents(id); end if; if not exists ( select 1 from pg_constraint where conrelid = 'public.claims'::regclass and conname = 'teleo_v3_claim_supersedes_fkey' ) then alter table public.claims add constraint teleo_v3_claim_supersedes_fkey foreign key (supersedes_id) references public.claims(id); end if; if not exists ( select 1 from pg_constraint where conrelid = 'public.claims'::regclass and conname = 'teleo_v3_claim_accepted_proposal_fkey' ) then alter table public.claims add constraint teleo_v3_claim_accepted_proposal_fkey foreign key (accepted_by_proposal_id) references kb_stage.kb_proposals(id); end if; if not exists ( select 1 from pg_constraint where conrelid = 'public.claims'::regclass and conname = 'teleo_v3_claim_admission_check' ) then alter table public.claims add constraint teleo_v3_claim_admission_check check ( admission_state = 'legacy' or ( admission_state = 'admitted' and type in ('empirical', 'predictive', 'causal', 'normative', 'conceptual') and proposition is not null and btrim(proposition) <> '' and text = proposition and body_markdown is not null and btrim(body_markdown) <> '' and scope is not null and btrim(scope) <> '' and access_scope in ('agent_private', 'collective_internal', 'public') and (access_scope <> 'agent_private' or owner_agent_id is not null) and revision_criteria is not null and btrim(revision_criteria) <> '' and revision_kind is not null and revision_kind in ('original', 'editorial', 'semantic', 'metadata_correction') and status in ('active', 'contested', 'retracted', 'superseded') and accepted_by_proposal_id is not null and ( (revision_kind = 'original' and supersedes_id is null) or (revision_kind <> 'original' and supersedes_id is not null) ) ) ); end if; if not exists ( select 1 from pg_constraint where conrelid = 'public.sources'::regclass and conname = 'teleo_v3_source_accepted_proposal_fkey' ) then alter table public.sources add constraint teleo_v3_source_accepted_proposal_fkey foreign key (accepted_by_proposal_id) references kb_stage.kb_proposals(id); end if; if not exists ( select 1 from pg_constraint where conrelid = 'public.sources'::regclass and conname = 'teleo_v3_source_contract_check' ) then alter table public.sources add constraint teleo_v3_source_contract_check check ( accepted_by_proposal_id is null or ( content_hash is not null and content_hash ~ '^[0-9a-f]{64}$' and hash = content_hash and ( (canonical_url is not null and btrim(canonical_url) <> '') or (storage_uri is not null and btrim(storage_uri) <> '') ) and access_scope is not null and access_scope in ('agent_private', 'collective_internal', 'public') and ingestion_origin is not null and ingestion_origin in ('operator_upload', 'agent_fetch', 'contributor', 'system_import', 'unknown') and provenance_status is not null and provenance_status in ('verified', 'unverified', 'disputed') and source_class is not null and source_class in ( 'primary_record', 'peer_reviewed', 'institutional', 'journalistic', 'self_published', 'unknown' ) and auto_promotion_policy is not null and auto_promotion_policy in ('eligible', 'human_required', 'blocked') ) ); end if; if not exists ( select 1 from pg_constraint where conrelid = 'public.claim_evidence'::regclass and conname = 'teleo_v3_evidence_accepted_proposal_fkey' ) then alter table public.claim_evidence add constraint teleo_v3_evidence_accepted_proposal_fkey foreign key (accepted_from_proposal_id) references kb_stage.kb_proposals(id); end if; if not exists ( select 1 from pg_constraint where conrelid = 'public.claim_evidence'::regclass and conname = 'teleo_v3_evidence_contract_check' ) then alter table public.claim_evidence add constraint teleo_v3_evidence_contract_check check ( accepted_from_proposal_id is null or ( polarity is not null and polarity in ('supports', 'challenges', 'illustrates') and ( (polarity = 'supports' and role::text = 'grounds') or (polarity = 'challenges' and role::text = 'contradicts') or (polarity = 'illustrates' and role::text = 'illustrates') ) and locator_json is not null and jsonb_typeof(locator_json) = 'object' and locator_json <> '{}'::jsonb and source_content_hash is not null and source_content_hash ~ '^[0-9a-f]{64}$' and rationale is not null and btrim(rationale) <> '' and weight is null ) ); end if; if not exists ( select 1 from pg_constraint where conrelid = 'public.claim_edges'::regclass and conname = 'teleo_v3_edge_accepted_proposal_fkey' ) then alter table public.claim_edges add constraint teleo_v3_edge_accepted_proposal_fkey foreign key (accepted_by_proposal_id) references kb_stage.kb_proposals(id); end if; if not exists ( select 1 from pg_constraint where conrelid = 'public.claim_edges'::regclass and conname = 'teleo_v3_edge_contract_check' ) then alter table public.claim_edges add constraint teleo_v3_edge_contract_check check ( accepted_by_proposal_id is null or ( relation_type is not null and relation_type in ('supports', 'challenges', 'depends_on', 'contradicts') and ( relation_type = edge_type::text or (relation_type = 'depends_on' and edge_type::text = 'requires') ) and rationale is not null and btrim(rationale) <> '' and from_claim <> to_claim and weight is null ) ); end if; if not exists ( select 1 from pg_constraint where conrelid = 'kb_stage.kb_proposals'::regclass and conname = 'teleo_v3_proposal_apply_receipt_check' ) then alter table kb_stage.kb_proposals add constraint teleo_v3_proposal_apply_receipt_check check ( ( status::text = 'applied' and applied_at is not null and ( applied_by_agent_id is not null or nullif(btrim(applied_by_handle), '') is not null ) ) or ( status::text <> 'applied' and applied_at is null and applied_by_agent_id is null and nullif(btrim(applied_by_handle), '') is null ) ) not valid; end if; end $constraints$; create unique index if not exists teleo_v3_claim_one_admitted_successor_idx on public.claims (supersedes_id) where admission_state = 'admitted' and supersedes_id is not null; create unique index if not exists teleo_v3_assessment_one_successor_idx on public.claim_evidence_assessments (supersedes_assessment_id) where supersedes_assessment_id is not null; create index if not exists teleo_v3_evidence_claim_polarity_idx on public.claim_evidence (claim_id, polarity) where accepted_from_proposal_id is not null; create index if not exists teleo_v3_edge_typed_adjacency_idx on public.claim_edges (from_claim, relation_type, to_claim) where accepted_by_proposal_id is not null; -- kb_apply intentionally has SELECT-only access to the proposal ledger, so a -- fixed SECURITY DEFINER helper takes only that table's transaction-held lock. -- The apply takes the remaining relation locks directly under its narrow DML -- grants. The exact helper body, owner, search_path, return type, and ACL are -- part of the fail-closed catalog predicate above and below. create or replace function kb_stage.teleo_v3_acquire_protected_relation_locks() returns void language plpgsql security definer set search_path = pg_catalog, pg_temp as $function$ begin lock table kb_stage.kb_proposals in row exclusive mode; end $function$; alter function kb_stage.teleo_v3_acquire_protected_relation_locks() owner to kb_gate_owner; revoke all on function kb_stage.teleo_v3_acquire_protected_relation_locks() from public, kb_apply, kb_review; grant execute on function kb_stage.teleo_v3_acquire_protected_relation_locks() to kb_apply; create or replace function kb_stage.teleo_v3_enforce_apply_contract_cutover() returns trigger language plpgsql security definer set search_path = pg_catalog, pg_temp as $function$ declare v_exact_v3 boolean; begin v_exact_v3 := new.proposal_type = 'approve_claim' and pg_catalog.jsonb_typeof(new.payload) = 'object' and pg_catalog.jsonb_typeof(new.payload->'apply_payload') = 'object' and pg_catalog.jsonb_typeof(new.payload->'apply_payload'->'contract_version') = 'number' and new.payload->'apply_payload'->>'contract_version' = '3'; if v_exact_v3 then return new; end if; if tg_op = 'UPDATE' and old.status::text in ('pending_review', 'approved') and new.status::text in ('rejected', 'canceled') and new.proposal_type is not distinct from old.proposal_type and new.payload is not distinct from old.payload then return new; end if; if new.status::text in ('pending_review', 'approved', 'applied') and new.proposal_type in ('revise_strategy', 'add_edge', 'attach_evidence', 'approve_claim') then raise exception using errcode = '55000', message = pg_catalog.format( 'teleo_v3 cutover: proposal %s uses a disabled V2 or malformed apply contract; migrate or reject it', new.id ); end if; return new; end $function$; alter function kb_stage.teleo_v3_enforce_apply_contract_cutover() owner to kb_gate_owner; revoke all on function kb_stage.teleo_v3_enforce_apply_contract_cutover() from public, kb_apply, kb_review; create or replace function kb_stage.teleo_v3_require_accepted_proposal() returns trigger language plpgsql security definer set search_path = pg_catalog, pg_temp as $function$ declare proposal_id uuid; proposal_status text; proposal_reviewed_by_handle text; proposal_reviewed_by_agent_id uuid; proposal_reviewed_at timestamptz; proposal_review_note text; proposal_applied_by_handle text; proposal_applied_by_agent_id uuid; proposal_applied_at timestamptz; begin proposal_id := nullif(pg_catalog.to_jsonb(new) ->> tg_argv[0], '')::uuid; if proposal_id is null then return new; end if; select status::text, reviewed_by_handle, reviewed_by_agent_id, reviewed_at, review_note, applied_by_handle, applied_by_agent_id, applied_at into proposal_status, proposal_reviewed_by_handle, proposal_reviewed_by_agent_id, proposal_reviewed_at, proposal_review_note, proposal_applied_by_handle, proposal_applied_by_agent_id, proposal_applied_at from kb_stage.kb_proposals where id = proposal_id for share; if not found then raise exception using errcode = '23503', message = pg_catalog.format('teleo_v3: proposal %s does not exist', proposal_id); end if; if proposal_status not in ('approved', 'accepted', 'applied') or proposal_reviewed_at is null or (proposal_reviewed_by_agent_id is null and nullif(pg_catalog.btrim(proposal_reviewed_by_handle), '') is null) or nullif(pg_catalog.btrim(proposal_review_note), '') is null then raise exception using errcode = '23514', message = pg_catalog.format('teleo_v3: proposal %s is not an accepted reviewed decision', proposal_id); end if; if proposal_status = 'applied' and ( proposal_applied_at is null or ( proposal_applied_by_agent_id is null and nullif(pg_catalog.btrim(proposal_applied_by_handle), '') is null ) ) then raise exception using errcode = '23514', message = pg_catalog.format('teleo_v3: applied proposal %s lacks immutable apply metadata', proposal_id); end if; if proposal_status <> 'applied' and ( proposal_applied_at is not null or proposal_applied_by_agent_id is not null or nullif(pg_catalog.btrim(proposal_applied_by_handle), '') is not null ) then raise exception using errcode = '23514', message = pg_catalog.format('teleo_v3: non-applied proposal %s carries invalid apply metadata', proposal_id); end if; return new; end $function$; -- The trigger takes FOR SHARE on the proposal ledger so a decision cannot be -- downgraded between validation and canonical insertion. Runtime writers do -- not receive direct UPDATE/row-lock privilege on that ledger; the fixed, -- trigger-only SECURITY DEFINER body is the sole narrow lock path. alter function kb_stage.teleo_v3_require_accepted_proposal() owner to kb_gate_owner; revoke all on function kb_stage.teleo_v3_require_accepted_proposal() from public, kb_apply, kb_review; do $security_definer_invariants$ declare v_drift text; begin select pg_catalog.format( 'owner=%I security_definer=%s config=%s acl=%s', owner_role.rolname, procedure.prosecdef, procedure.proconfig, procedure.proacl ) into v_drift 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 where namespace.nspname = 'kb_stage' and procedure.proname in ( 'teleo_v3_enforce_apply_contract_cutover', 'teleo_v3_require_accepted_proposal' ) and ( owner_role.rolname <> 'kb_gate_owner' or not procedure.prosecdef or pg_catalog.array_to_string(procedure.proconfig, ',') is distinct from 'search_path=pg_catalog, pg_temp' or 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 ) ); if v_drift is not null then raise exception 'teleo_v3_epistemic_contract: SECURITY DEFINER trigger invariants failed: %', v_drift; end if; end $security_definer_invariants$; create or replace function kb_stage.teleo_v3_require_linked_insert() returns trigger language plpgsql security invoker set search_path = pg_catalog, pg_temp as $function$ declare proposal_field text := tg_argv[0]; admission_field text := case when array_length(tg_argv, 1) > 1 then tg_argv[1] else null end; proposal_value text; admission_value text; begin proposal_value := pg_catalog.to_jsonb(new) ->> proposal_field; if nullif(pg_catalog.btrim(proposal_value), '') is null then raise exception using errcode = '23514', message = pg_catalog.format( 'teleo_v3: new %s.%s rows must be proposal-linked through %s', tg_table_schema, tg_table_name, proposal_field ); end if; if admission_field is not null then admission_value := pg_catalog.to_jsonb(new) ->> admission_field; if admission_value is distinct from 'admitted' then raise exception using errcode = '23514', message = pg_catalog.format( 'teleo_v3: new %s.%s rows must use %s=admitted; legacy is reserved for rows present at migration', tg_table_schema, tg_table_name, admission_field ); end if; end if; return new; end $function$; create or replace function kb_stage.teleo_v3_guard_immutable_fields() returns trigger language plpgsql security invoker set search_path = pg_catalog, pg_temp as $function$ declare field_name text; begin if ( tg_table_schema = 'public' and tg_table_name = 'claims' and (pg_catalog.to_jsonb(old) ->> 'admission_state') is distinct from 'admitted' ) or ( tg_table_schema = 'public' and tg_table_name = 'sources' and (pg_catalog.to_jsonb(old) ->> 'accepted_by_proposal_id') is null ) or ( tg_table_schema = 'public' and tg_table_name = 'claim_evidence' and (pg_catalog.to_jsonb(old) ->> 'accepted_from_proposal_id') is null ) or ( tg_table_schema = 'public' and tg_table_name = 'claim_edges' and (pg_catalog.to_jsonb(old) ->> 'accepted_by_proposal_id') is null ) then return new; end if; foreach field_name in array tg_argv loop if (pg_catalog.to_jsonb(new) -> field_name) is distinct from (pg_catalog.to_jsonb(old) -> field_name) then raise exception using errcode = '55000', message = pg_catalog.format( 'teleo_v3: immutable %s.%s field %s requires a new version', tg_table_schema, tg_table_name, field_name ); end if; end loop; return new; end $function$; create or replace function kb_stage.teleo_v3_reject_delete() returns trigger language plpgsql security invoker set search_path = pg_catalog, pg_temp as $function$ begin if ( tg_table_schema = 'public' and tg_table_name = 'claims' and (pg_catalog.to_jsonb(old) ->> 'admission_state') is distinct from 'admitted' and (pg_catalog.to_jsonb(old) ->> 'accepted_by_proposal_id') is null ) or ( tg_table_schema = 'public' and tg_table_name = 'sources' and (pg_catalog.to_jsonb(old) ->> 'accepted_by_proposal_id') is null ) or ( tg_table_schema = 'public' and tg_table_name = 'claim_evidence' and (pg_catalog.to_jsonb(old) ->> 'accepted_from_proposal_id') is null ) or ( tg_table_schema = 'public' and tg_table_name = 'claim_edges' and (pg_catalog.to_jsonb(old) ->> 'accepted_by_proposal_id') is null ) then return old; end if; raise exception using errcode = '55000', message = pg_catalog.format( 'teleo_v3: proposal-linked %s.%s rows are append-only; supersede or retract through a new reviewed version', tg_table_schema, tg_table_name ); end $function$; create or replace function kb_stage.teleo_v3_validate_claim_evidence() returns trigger language plpgsql security invoker set search_path = pg_catalog, pg_temp as $function$ declare claim_admission_state text; source_hash text; source_proposal_id uuid; begin if new.accepted_from_proposal_id is null then return new; end if; select admission_state into claim_admission_state from public.claims where id = new.claim_id; select content_hash, accepted_by_proposal_id into source_hash, source_proposal_id from public.sources where id = new.source_id; if claim_admission_state is distinct from 'admitted' then raise exception using errcode = '23514', message = 'teleo_v3: accepted evidence must target an admitted claim version'; end if; if source_proposal_id is null then raise exception using errcode = '23514', message = 'teleo_v3: accepted evidence must reference a proposal-linked source version'; end if; if source_hash is distinct from new.source_content_hash then raise exception using errcode = '23514', message = 'teleo_v3: evidence source_content_hash does not match the immutable source version'; end if; return new; end $function$; create or replace function kb_stage.teleo_v3_validate_claim_edge() returns trigger language plpgsql security invoker set search_path = pg_catalog, pg_temp as $function$ declare from_admission_state text; to_admission_state text; begin if new.accepted_by_proposal_id is null then return new; end if; select admission_state into from_admission_state from public.claims where id = new.from_claim; select admission_state into to_admission_state from public.claims where id = new.to_claim; if from_admission_state is distinct from 'admitted' or to_admission_state is distinct from 'admitted' then raise exception using errcode = '23514', message = 'teleo_v3: accepted typed edges require two admitted claim versions'; end if; return new; end $function$; create or replace function kb_stage.teleo_v3_validate_evidence_assessment() returns trigger language plpgsql security invoker set search_path = pg_catalog, pg_temp as $function$ declare claim_type text; claim_admission_state text; begin select type, admission_state into claim_type, claim_admission_state from public.claims where id = new.claim_id; if claim_admission_state is distinct from 'admitted' then raise exception using errcode = '23514', message = 'teleo_v3: evidence assessments require an admitted claim version'; end if; if claim_type in ('empirical', 'predictive', 'causal') and (new.support_tier is null or new.challenge_tier is null) then raise exception using errcode = '23514', message = 'teleo_v3: truth-apt claim assessments require support and challenge tiers'; end if; if not exists ( select 1 from public.claim_evidence where claim_id = new.claim_id and accepted_from_proposal_id is not null ) then raise exception using errcode = '23514', message = 'teleo_v3: evidence assessments require accepted claim-specific evidence'; end if; return new; end $function$; create or replace function kb_stage.teleo_v3_guard_linked_proposal_decision() returns trigger language plpgsql security invoker set search_path = pg_catalog, pg_temp as $function$ declare proposal_is_linked boolean; valid_apply_transition boolean; begin select exists (select 1 from public.claims where accepted_by_proposal_id = old.id) or exists (select 1 from public.sources where accepted_by_proposal_id = old.id) or exists (select 1 from public.claim_evidence where accepted_from_proposal_id = old.id) or exists (select 1 from public.claim_edges where accepted_by_proposal_id = old.id) or exists (select 1 from public.claim_evidence_assessments where accepted_by_proposal_id = old.id) into proposal_is_linked; valid_apply_transition := old.status::text in ('approved', 'accepted') and new.status::text = 'applied' and old.applied_by_handle is null and old.applied_by_agent_id is null and old.applied_at is null and new.applied_at is not null and ( new.applied_by_agent_id is not null or nullif(pg_catalog.btrim(new.applied_by_handle), '') is not null ); if new.status::text = 'applied' and ( new.applied_at is null or ( new.applied_by_agent_id is null and nullif(pg_catalog.btrim(new.applied_by_handle), '') is null ) ) then raise exception using errcode = '55000', message = pg_catalog.format('teleo_v3: applied proposal %s requires immutable apply metadata', old.id); end if; if new.status::text <> 'applied' and ( new.applied_at is not null or new.applied_by_agent_id is not null or nullif(pg_catalog.btrim(new.applied_by_handle), '') is not null ) then raise exception using errcode = '55000', message = pg_catalog.format('teleo_v3: non-applied proposal %s cannot carry apply metadata', old.id); end if; if old.status::text = 'applied' and ( new.status::text <> 'applied' or new.proposal_type is distinct from old.proposal_type or new.payload is distinct from old.payload or new.rationale is distinct from old.rationale or new.reviewed_by_handle is distinct from old.reviewed_by_handle or new.reviewed_by_agent_id is distinct from old.reviewed_by_agent_id or new.reviewed_at is distinct from old.reviewed_at or new.review_note is distinct from old.review_note or new.applied_by_handle is distinct from old.applied_by_handle or new.applied_by_agent_id is distinct from old.applied_by_agent_id or new.applied_at is distinct from old.applied_at ) then raise exception using errcode = '55000', message = pg_catalog.format('teleo_v3: applied proposal %s is immutable', old.id); end if; if ( new.applied_by_handle is distinct from old.applied_by_handle or new.applied_by_agent_id is distinct from old.applied_by_agent_id or new.applied_at is distinct from old.applied_at ) and not valid_apply_transition then raise exception using errcode = '55000', message = pg_catalog.format('teleo_v3: proposal %s apply metadata may only be set during apply', old.id); end if; if proposal_is_linked and ( new.status::text not in ('approved', 'accepted', 'applied') or ( old.status::text in ('approved', 'accepted') and new.status::text not in (old.status::text, 'applied') ) or (old.status::text = 'applied' and new.status::text <> 'applied') or new.proposal_type is distinct from old.proposal_type or new.payload is distinct from old.payload or new.rationale is distinct from old.rationale or new.reviewed_by_handle is distinct from old.reviewed_by_handle or new.reviewed_by_agent_id is distinct from old.reviewed_by_agent_id or new.reviewed_at is distinct from old.reviewed_at or new.review_note is distinct from old.review_note or new.reviewed_at is null or (new.reviewed_by_agent_id is null and nullif(pg_catalog.btrim(new.reviewed_by_handle), '') is null) or nullif(pg_catalog.btrim(new.review_note), '') is null ) then raise exception using errcode = '55000', message = pg_catalog.format('teleo_v3: linked accepted proposal %s cannot lose its decision metadata', old.id); end if; return new; end $function$; do $trigger_function_owners$ declare v_function record; begin for v_function in select distinct expected.function_schema, expected.function_name, expected.function_identity_arguments from pg_catalog.jsonb_to_recordset( pg_catalog.current_setting('teleo.v3_expected_trigger_contract')::jsonb ) as expected( function_schema text, function_name text, function_identity_arguments text ) loop if v_function.function_identity_arguments <> '' then raise exception 'teleo_v3_epistemic_contract: trigger functions must have zero identity arguments'; end if; execute pg_catalog.format( 'alter function %I.%I() owner to kb_gate_owner', v_function.function_schema, v_function.function_name ); end loop; end $trigger_function_owners$; do $triggers$ declare v_trigger record; begin execute $ddl$ create or replace trigger teleo_v3_00_enforce_apply_contract_cutover before insert or update of proposal_type, payload, status on kb_stage.kb_proposals for each row execute function kb_stage.teleo_v3_enforce_apply_contract_cutover() $ddl$; execute $ddl$ create or replace trigger teleo_v3_00_claim_require_linked_insert before insert on public.claims for each row execute function kb_stage.teleo_v3_require_linked_insert( 'accepted_by_proposal_id', 'admission_state' ) $ddl$; execute $ddl$ create or replace trigger teleo_v3_claim_require_accepted_proposal before insert or update of accepted_by_proposal_id on public.claims for each row execute function kb_stage.teleo_v3_require_accepted_proposal('accepted_by_proposal_id') $ddl$; execute $ddl$ create or replace trigger teleo_v3_00_source_require_linked_insert before insert on public.sources for each row execute function kb_stage.teleo_v3_require_linked_insert('accepted_by_proposal_id') $ddl$; execute $ddl$ create or replace trigger teleo_v3_claim_immutable before update on public.claims for each row execute function kb_stage.teleo_v3_guard_immutable_fields( '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' ) $ddl$; execute $ddl$ create or replace trigger teleo_v3_claim_reject_delete before delete on public.claims for each row execute function kb_stage.teleo_v3_reject_delete() $ddl$; execute $ddl$ create or replace trigger teleo_v3_00_evidence_require_linked_insert before insert on public.claim_evidence for each row execute function kb_stage.teleo_v3_require_linked_insert('accepted_from_proposal_id') $ddl$; execute $ddl$ create or replace trigger teleo_v3_source_require_accepted_proposal before insert or update of accepted_by_proposal_id on public.sources for each row execute function kb_stage.teleo_v3_require_accepted_proposal('accepted_by_proposal_id') $ddl$; execute $ddl$ create or replace trigger teleo_v3_00_edge_require_linked_insert before insert on public.claim_edges for each row execute function kb_stage.teleo_v3_require_linked_insert('accepted_by_proposal_id') $ddl$; execute $ddl$ create or replace trigger teleo_v3_source_immutable before update on public.sources for each row execute function kb_stage.teleo_v3_guard_immutable_fields( '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' ) $ddl$; execute $ddl$ create or replace trigger teleo_v3_source_reject_delete before delete on public.sources for each row execute function kb_stage.teleo_v3_reject_delete() $ddl$; execute $ddl$ create or replace trigger teleo_v3_evidence_require_accepted_proposal before insert or update of accepted_from_proposal_id on public.claim_evidence for each row execute function kb_stage.teleo_v3_require_accepted_proposal('accepted_from_proposal_id') $ddl$; execute $ddl$ create or replace trigger teleo_v3_evidence_validate_provenance before insert or update on public.claim_evidence for each row execute function kb_stage.teleo_v3_validate_claim_evidence() $ddl$; execute $ddl$ create or replace trigger teleo_v3_evidence_immutable before update on public.claim_evidence for each row execute function kb_stage.teleo_v3_guard_immutable_fields( 'id', 'claim_id', 'source_id', 'role', 'weight', 'excerpt', 'locator_json', 'source_content_hash', 'rationale', 'polarity', 'created_by', 'created_at', 'accepted_from_proposal_id' ) $ddl$; execute $ddl$ create or replace trigger teleo_v3_evidence_reject_delete before delete on public.claim_evidence for each row execute function kb_stage.teleo_v3_reject_delete() $ddl$; execute $ddl$ create or replace trigger teleo_v3_edge_require_accepted_proposal before insert or update of accepted_by_proposal_id on public.claim_edges for each row execute function kb_stage.teleo_v3_require_accepted_proposal('accepted_by_proposal_id') $ddl$; execute $ddl$ create or replace trigger teleo_v3_edge_validate_endpoints before insert or update on public.claim_edges for each row execute function kb_stage.teleo_v3_validate_claim_edge() $ddl$; execute $ddl$ create or replace trigger teleo_v3_edge_immutable before update on public.claim_edges for each row execute function kb_stage.teleo_v3_guard_immutable_fields( 'id', 'from_claim', 'to_claim', 'edge_type', 'weight', 'relation_type', 'rationale', 'scope_or_conditions', 'created_by', 'created_at', 'accepted_by_proposal_id' ) $ddl$; execute $ddl$ create or replace trigger teleo_v3_edge_reject_delete before delete on public.claim_edges for each row execute function kb_stage.teleo_v3_reject_delete() $ddl$; execute $ddl$ create or replace trigger teleo_v3_assessment_require_accepted_proposal before insert or update of accepted_by_proposal_id on public.claim_evidence_assessments for each row execute function kb_stage.teleo_v3_require_accepted_proposal('accepted_by_proposal_id') $ddl$; execute $ddl$ create or replace trigger teleo_v3_assessment_validate before insert or update on public.claim_evidence_assessments for each row execute function kb_stage.teleo_v3_validate_evidence_assessment() $ddl$; execute $ddl$ create or replace trigger teleo_v3_assessment_immutable before update on public.claim_evidence_assessments for each row execute function kb_stage.teleo_v3_guard_immutable_fields( 'id', 'claim_id', 'support_tier', 'challenge_tier', 'overall_state', 'rationale', 'evidence_set_hash', 'accepted_by_proposal_id', 'supersedes_assessment_id', 'created_at' ) $ddl$; execute $ddl$ create or replace trigger teleo_v3_assessment_reject_delete before delete on public.claim_evidence_assessments for each row execute function kb_stage.teleo_v3_reject_delete() $ddl$; execute $ddl$ create or replace trigger teleo_v3_guard_linked_proposal_decision before update of 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 on kb_stage.kb_proposals for each row execute function kb_stage.teleo_v3_guard_linked_proposal_decision() $ddl$; for v_trigger in select expected.schema_name, expected.table_name, expected.trigger_name from pg_catalog.jsonb_to_recordset( pg_catalog.current_setting('teleo.v3_expected_trigger_contract')::jsonb ) as expected(schema_name text, table_name text, trigger_name text) loop execute pg_catalog.format( 'alter table %I.%I enable trigger %I', v_trigger.schema_name, v_trigger.table_name, v_trigger.trigger_name ); end loop; end $triggers$; do $contract_postflight$ declare v_contract_state text; begin -- TELEO_V3_CONTRACT_POSTFLIGHT_QUERY_BEGIN with expected_triggers as ( select expected.* from pg_catalog.jsonb_to_recordset(pg_catalog.current_setting('teleo.v3_expected_trigger_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, 9) = E'teleo_v3_' and procedure.proname <> E'teleo_v3_acquire_protected_relation_locks' ), 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 = E'teleo_v3_acquire_protected_relation_locks' ), 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, 9) = E'teleo_v3_' ), 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 = E'kb_gate_owner' and function_security_definer and function_config = '["search_path=pg_catalog, pg_temp"]'::jsonb and function_source_sha256 = E'897345944ba0f1b2a5c01b098fb17de6f881a122e906277086fa7889e76219e8' 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 = E'kb_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 (E'kb_gate_owner', E'kb_review', E'kb_apply') or granted_role.rolname in (E'kb_gate_owner', E'kb_review', E'kb_apply') ) 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 v_contract_state from catalog_state; -- TELEO_V3_CONTRACT_POSTFLIGHT_QUERY_END if v_contract_state <> 'V3' then raise exception using errcode = '55000', message = pg_catalog.format( 'teleo_v3_epistemic_contract: installed trigger catalog is %s', v_contract_state ); end if; end $contract_postflight$; commit; select jsonb_build_object( 'contract', 'livingip.teleo-v3-epistemic-contract', 'version', 1, 'migration', 'installed', 'legacy_claims', (select count(*) from public.claims where admission_state = 'legacy'), 'admitted_claims', (select count(*) from public.claims where admission_state = 'admitted') )::text;