teleo-infrastructure/ops/teleo_v3_epistemic_contract.sql

2293 lines
89 KiB
PL/PgSQL

-- 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;