teleo-infrastructure/scripts/kb_apply_prereqs.sql
twentyOne2x fed5cb0805 Add guarded canonical claim application
- Normalize rich proposals into atomic reviewed canonical graph bundles with exact row verification.
- Harden reviewer/apply roles, upgrade ACLs, and prove generic plus Helmer lifecycles in isolated PostgreSQL.
- Publish repo-native VPS/GCP/Cory skills and live-readonly GCP inventory without changing the live VPS runtime.

`.agents/skills/crabbox/SKILL.md`
`.agents/skills/teleo-gcp-parity-ops/SKILL.md`
`.agents/skills/teleo-kb-db-change-workflow/SKILL.md`
`.agents/skills/teleo-leo-onboarding/SKILL.md`
`.agents/skills/teleo-vps-runtime-ops/SKILL.md`
`.agents/skills/working-leo-cory-outcomes/SKILL.md`
`docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.json`
`docs/reports/leo-working-state-20260709/approve-claim-clone-canary-current.md`
`docs/reports/leo-working-state-20260709/current-truth-index.md`
`docs/reports/leo-working-state-20260709/gcp-cloud-sql-t3-live-readonly-current.json`
`docs/reports/leo-working-state-20260709/gcp-cloud-sql-t3-live-readonly-current.md`
`docs/reports/leo-working-state-20260709/gcp-cloudsql-studio-parity-gate-current.json`
`docs/reports/leo-working-state-20260709/gcp-cloudsql-studio-parity-gate-current.md`
`docs/reports/leo-working-state-20260709/gcp-parallel-delegation-current.json`
`docs/reports/leo-working-state-20260709/helmer-approve-claim-clone-canary-current.json`
`docs/reports/leo-working-state-20260709/helmer-approve-claim-normalization-current.json`
`docs/reports/leo-working-state-20260709/skill-pack-manifest.json`
`docs/reports/leo-working-state-20260709/skill-pack-readme.md`
`docs/reports/leo-working-state-20260709/working-leo-current-state-20260709.md`
`docs/reports/leo-working-state-20260709/working-leo-definition-20260709.md`
`docs/reports/leo-working-state-20260709/working-leo-execution-plan-current.md`
`scripts/apply_proposal.py`
`scripts/apply_worker.py`
`scripts/approve_proposal.py`
`scripts/kb_apply_prereqs.sql`
`scripts/kb_proposal_normalize.py`
`scripts/probe_gcp_db_parity.py`
`scripts/run_approve_claim_clone_canary.py`
`scripts/run_approve_claim_isolated_container_canary.sh`
`tests/test_apply_proposal.py`
`tests/test_apply_worker.py`
`tests/test_approve_proposal.py`
`tests/test_kb_apply_prereqs.py`
`tests/test_kb_proposal_normalize.py`
`tests/test_kb_proposal_routes.py`
`tests/test_probe_gcp_db_parity.py`
`tests/test_repo_skill_pack.py`
2026-07-10 17:49:37 +02:00

1073 lines
42 KiB
PL/PgSQL

-- Guarded KB review/apply prerequisites. Run as postgres at deploy time.
--
-- Authority is deliberately split:
-- * kb_review can read proposals and execute one constrained approval function;
-- * kb_apply can read approved proposals, perform narrow canonical writes, and
-- execute payload-bound assert/finish functions;
-- * kb_gate_owner is a NOLOGIN owner for gate tables/functions;
-- * neither login role can directly UPDATE kb_stage.kb_proposals or mutate the
-- immutable approval snapshot.
--
-- The deployer provisions passwords separately in kb-review.env/kb-apply.env.
-- This file contains no credentials and is safe to reuse in disposable clones.
begin;
do $deployer$
begin
if not exists (
select 1
from pg_catalog.pg_roles
where rolname = current_user
and rolsuper
) then
raise exception 'kb_apply_prereqs: current role % must be a superuser', current_user;
end if;
if not exists (select 1 from pg_catalog.pg_roles where rolname = 'kb_apply') then
raise exception 'kb_apply_prereqs: required runtime role kb_apply does not exist';
end if;
end
$deployer$;
do $roles$
begin
if not exists (select 1 from pg_catalog.pg_roles where rolname = 'kb_gate_owner') then
create role kb_gate_owner nologin;
end if;
if not exists (select 1 from pg_catalog.pg_roles where rolname = 'kb_review') then
create role kb_review login;
end if;
end
$roles$;
alter role kb_gate_owner nologin noinherit nosuperuser nocreatedb nocreaterole noreplication nobypassrls;
alter role kb_review login noinherit nosuperuser nocreatedb nocreaterole noreplication nobypassrls;
alter role kb_apply login noinherit nosuperuser nocreatedb nocreaterole noreplication nobypassrls;
-- Protected roles are standalone principals. Any membership involving one of
-- them creates an inherited or SET ROLE path that this migration cannot safely
-- classify, so fail instead of silently revoking an operator-managed grant.
do $memberships$
declare
v_memberships text;
begin
select pg_catalog.string_agg(
pg_catalog.format('%I -> %I', member_role.rolname, granted_role.rolname),
', ' order by member_role.rolname, granted_role.rolname
)
into v_memberships
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');
if v_memberships is not null then
raise exception 'kb_apply_prereqs: unexpected protected role membership: %', v_memberships;
end if;
end
$memberships$;
-- Service identity used for the applied_by_agent_id foreign key. kb_apply can
-- read this row but can never create or modify agent rows.
insert into public.agents (id, handle, kind)
values ('44444444-4444-4444-4444-444444444444', 'kb-apply', 'service')
on conflict (handle) do nothing;
-- DB-login to human-reviewer mapping. Only the gate owner/admin can change it;
-- the approval function verifies --reviewed-by against this mapping rather than
-- trusting caller-supplied identity. The current dedicated reviewer credential
-- maps to the existing canonical m3ta human agent when that row is present.
create table if not exists kb_stage.kb_review_principals (
db_role name primary key,
reviewed_by_handle text not null,
reviewed_by_agent_id uuid not null references public.agents(id),
active boolean not null default true,
created_at timestamptz not null default now()
);
-- One immutable snapshot per reviewed proposal. Apply is bound to this row as
-- well as the current proposal ledger, so an approved payload cannot drift.
create table if not exists kb_stage.kb_proposal_approvals (
proposal_id uuid primary key references kb_stage.kb_proposals(id),
proposal_type text not null,
payload jsonb not null,
reviewed_by_handle text not null,
reviewed_by_agent_id uuid not null references public.agents(id),
reviewed_by_db_role name not null,
reviewed_at timestamptz not null,
review_note text not null
);
-- Existing canonical/proposal tables must remain owned by the deployer. Gate
-- tables may be owned by the deployer during a legacy upgrade or kb_gate_owner
-- after an earlier guarded run. Anything else is ownership drift.
do $gate_preflight$
declare
v_drift text;
begin
select pg_catalog.string_agg(
pg_catalog.format('%I.%I owned by %I', namespace.nspname, relation.relname, owner_role.rolname),
', ' order by namespace.nspname, relation.relname
)
into v_drift
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, relation.relname) in (
('public', 'agents'),
('public', 'strategies'),
('public', 'strategy_nodes'),
('public', 'claim_evidence'),
('public', 'claim_edges'),
('public', 'claims'),
('public', 'sources'),
('public', 'reasoning_tools'),
('kb_stage', 'kb_proposals'),
('kb_stage', 'kb_review_principals'),
('kb_stage', 'kb_proposal_approvals')
)
and relation.relkind in ('r', 'p')
and not (
(namespace.nspname = 'kb_stage'
and relation.relname in ('kb_review_principals', 'kb_proposal_approvals')
and owner_role.rolname in (current_user, 'kb_gate_owner'))
or (not (
namespace.nspname = 'kb_stage'
and relation.relname in ('kb_review_principals', 'kb_proposal_approvals')
)
and owner_role.rolname = current_user)
);
if v_drift is not null then
raise exception 'kb_apply_prereqs: unexpected protected table owner: %', v_drift;
end if;
select pg_catalog.string_agg(
pg_catalog.format(
'%I.%I(%s) owned by %I',
namespace.nspname,
procedure.proname,
pg_catalog.oidvectortypes(procedure.proargtypes),
owner_role.rolname
),
', ' order by procedure.proname, pg_catalog.oidvectortypes(procedure.proargtypes)
)
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 (
'approve_strict_proposal',
'assert_approved_proposal',
'finish_approved_proposal'
)
and owner_role.rolname not in (current_user, 'kb_gate_owner');
if v_drift is not null then
raise exception 'kb_apply_prereqs: unexpected protected function owner: %', v_drift;
end if;
select pg_catalog.string_agg(
pg_catalog.format(
'%I.%I(%s)',
namespace.nspname,
procedure.proname,
pg_catalog.oidvectortypes(procedure.proargtypes)
),
', ' order by procedure.proname, pg_catalog.oidvectortypes(procedure.proargtypes)
)
into v_drift
from pg_catalog.pg_proc procedure
join pg_catalog.pg_namespace namespace on namespace.oid = procedure.pronamespace
where namespace.nspname = 'kb_stage'
and procedure.proname in (
'approve_strict_proposal',
'assert_approved_proposal',
'finish_approved_proposal'
)
and not (
(procedure.proname = 'approve_strict_proposal'
and pg_catalog.oidvectortypes(procedure.proargtypes) in (
'uuid, text, text',
'uuid, text, jsonb, text, text'
))
or (procedure.proname = 'assert_approved_proposal'
and pg_catalog.oidvectortypes(procedure.proargtypes) =
'uuid, text, jsonb, text, uuid, timestamp with time zone, text')
or (procedure.proname = 'finish_approved_proposal'
and pg_catalog.oidvectortypes(procedure.proargtypes) =
'uuid, text, jsonb, text, uuid, timestamp with time zone, text, text')
);
if v_drift is not null then
raise exception 'kb_apply_prereqs: unexpected protected function overload: %', v_drift;
end if;
select pg_catalog.string_agg(
pg_catalog.format(
'%I.%I -> %s',
namespace.nspname,
relation.relname,
case
when acl.grantee = 0 then 'PUBLIC'
else pg_catalog.pg_get_userbyid(acl.grantee)
end
),
', ' order by namespace.nspname, relation.relname, acl.grantee
)
into v_drift
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
cross join lateral pg_catalog.aclexplode(
coalesce(relation.relacl, pg_catalog.acldefault('r', relation.relowner))
) acl
where (namespace.nspname, relation.relname) in (
('kb_stage', 'kb_review_principals'),
('kb_stage', 'kb_proposal_approvals')
)
and acl.grantee <> 0
and acl.grantee <> relation.relowner
and pg_catalog.pg_get_userbyid(acl.grantee) not in (
'kb_gate_owner', 'kb_review', 'kb_apply'
);
if v_drift is not null then
raise exception 'kb_apply_prereqs: unexpected protected table grantee: %', v_drift;
end if;
select pg_catalog.string_agg(
pg_catalog.format(
'%I.%I.%I -> %s',
namespace.nspname,
relation.relname,
attribute.attname,
case
when acl.grantee = 0 then 'PUBLIC'
else pg_catalog.pg_get_userbyid(acl.grantee)
end
),
', ' order by namespace.nspname, relation.relname, attribute.attnum, acl.grantee
)
into v_drift
from pg_catalog.pg_attribute attribute
join pg_catalog.pg_class relation on relation.oid = attribute.attrelid
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
cross join lateral pg_catalog.aclexplode(attribute.attacl) acl
where (namespace.nspname, relation.relname) in (
('kb_stage', 'kb_review_principals'),
('kb_stage', 'kb_proposal_approvals')
)
and not attribute.attisdropped
and acl.grantee <> 0
and acl.grantee <> relation.relowner
and pg_catalog.pg_get_userbyid(acl.grantee) not in (
'kb_gate_owner', 'kb_review', 'kb_apply'
);
if v_drift is not null then
raise exception 'kb_apply_prereqs: unexpected protected table grantee: %', v_drift;
end if;
select pg_catalog.string_agg(
pg_catalog.format(
'%I.%I(%s) -> %s',
namespace.nspname,
procedure.proname,
pg_catalog.oidvectortypes(procedure.proargtypes),
case
when acl.grantee = 0 then 'PUBLIC'
else pg_catalog.pg_get_userbyid(acl.grantee)
end
),
', ' order by procedure.proname, pg_catalog.oidvectortypes(procedure.proargtypes), acl.grantee
)
into v_drift
from pg_catalog.pg_proc procedure
join pg_catalog.pg_namespace namespace on namespace.oid = procedure.pronamespace
cross join lateral pg_catalog.aclexplode(
coalesce(procedure.proacl, pg_catalog.acldefault('f', procedure.proowner))
) acl
where namespace.nspname = 'kb_stage'
and procedure.proname in (
'approve_strict_proposal',
'assert_approved_proposal',
'finish_approved_proposal'
)
and acl.grantee <> 0
and acl.grantee <> procedure.proowner
and pg_catalog.pg_get_userbyid(acl.grantee) not in (
'kb_gate_owner', 'kb_review', 'kb_apply'
);
if v_drift is not null then
raise exception 'kb_apply_prereqs: unexpected protected function grantee: %', v_drift;
end if;
end
$gate_preflight$;
-- This legacy three-argument SECURITY DEFINER path trusted caller-supplied
-- reviewer text. Its exact signature is the only obsolete overload auto-removed.
drop function if exists kb_stage.approve_strict_proposal(uuid, text, text);
alter table kb_stage.kb_review_principals owner to kb_gate_owner;
alter table kb_stage.kb_proposal_approvals owner to kb_gate_owner;
insert into kb_stage.kb_review_principals
(db_role, reviewed_by_handle, reviewed_by_agent_id, active)
select 'kb_review'::name, a.handle, a.id, true
from public.agents a
where a.handle = 'm3ta'
on conflict (db_role) do nothing;
-- Reset every explicit privilege held by the protected roles before granting
-- the documented matrix. This removes stale DELETE/TRUNCATE/REFERENCES/TRIGGER
-- rights from upgrades instead of layering narrow grants on top of them.
revoke all privileges on schema public, kb_stage
from kb_gate_owner, kb_apply, kb_review;
grant usage on schema public, kb_stage to kb_gate_owner;
grant usage on schema public, kb_stage to kb_apply;
grant usage on schema public, kb_stage to kb_review;
revoke all privileges on table
public.agents,
public.strategies,
public.strategy_nodes,
public.claim_evidence,
public.claim_edges,
public.claims,
public.sources,
public.reasoning_tools,
kb_stage.kb_proposals,
kb_stage.kb_review_principals,
kb_stage.kb_proposal_approvals
from kb_gate_owner, kb_apply, kb_review;
revoke all privileges on table
kb_stage.kb_proposals,
kb_stage.kb_review_principals,
kb_stage.kb_proposal_approvals
from public;
-- Column grants are stored separately from table ACLs and survive a table-level
-- REVOKE. Clear them explicitly so stale per-column writes cannot bypass the
-- normalized table matrix.
do $column_acl_reset$
declare
protected_table record;
begin
for protected_table in
select namespace.nspname as schema_name,
relation.relname as table_name,
pg_catalog.string_agg(
pg_catalog.format('%I', attribute.attname),
', ' order by attribute.attnum
) as column_list
from pg_catalog.pg_class relation
join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid
where (namespace.nspname, relation.relname) in (
('public', 'agents'),
('public', 'strategies'),
('public', 'strategy_nodes'),
('public', 'claim_evidence'),
('public', 'claim_edges'),
('public', 'claims'),
('public', 'sources'),
('public', 'reasoning_tools'),
('kb_stage', 'kb_proposals'),
('kb_stage', 'kb_review_principals'),
('kb_stage', 'kb_proposal_approvals')
)
and attribute.attnum > 0
and not attribute.attisdropped
group by namespace.nspname, relation.relname
loop
execute pg_catalog.format(
'revoke all privileges (%s) on table %I.%I from kb_gate_owner, kb_apply, kb_review',
protected_table.column_list,
protected_table.schema_name,
protected_table.table_name
);
if protected_table.schema_name = 'kb_stage'
and protected_table.table_name in (
'kb_proposals', 'kb_review_principals', 'kb_proposal_approvals'
) then
execute pg_catalog.format(
'revoke all privileges (%s) on table %I.%I from public',
protected_table.column_list,
protected_table.schema_name,
protected_table.table_name
);
end if;
end loop;
end
$column_acl_reset$;
grant select on public.agents to kb_gate_owner;
grant select on public.agents to kb_apply;
grant select on public.agents to kb_review;
grant select, insert, update on public.strategies to kb_apply;
grant select, insert, update on public.strategy_nodes to kb_apply;
grant select, insert on public.claim_evidence to kb_apply;
grant select, insert on public.claim_edges to kb_apply;
grant select, insert on public.claims to kb_apply;
grant select, insert on public.sources to kb_apply;
grant select, insert on public.reasoning_tools to kb_apply;
-- Keep the primary ledger denial explicit in addition to the full reset above.
revoke update on kb_stage.kb_proposals from kb_apply;
grant select on kb_stage.kb_proposals to kb_apply;
grant select on kb_stage.kb_proposals to kb_review;
grant select, update on kb_stage.kb_proposals to kb_gate_owner;
grant select on kb_stage.kb_review_principals to kb_gate_owner;
grant select, insert on kb_stage.kb_proposal_approvals to kb_gate_owner;
-- Required conflict arbiters. Existing production constraints satisfy these
-- checks; fresh clones receive named indexes only when no equivalent unique
-- index exists. Migration failure on pre-existing duplicates is intentional.
do $indexes$
begin
if not exists (
select 1
from pg_catalog.pg_index i
join pg_catalog.pg_class t on t.oid = i.indrelid
join pg_catalog.pg_namespace n on n.oid = t.relnamespace
where n.nspname = 'public'
and t.relname = 'sources'
and i.indisunique
and i.indpred is null
and replace(pg_catalog.pg_get_indexdef(i.indexrelid), ' ', '') like '%(hash)%'
) then
execute 'create unique index kb_apply_sources_hash_unique on public.sources (hash)';
end if;
if not exists (
select 1
from pg_catalog.pg_index i
join pg_catalog.pg_class t on t.oid = i.indrelid
join pg_catalog.pg_namespace n on n.oid = t.relnamespace
where n.nspname = 'public'
and t.relname = 'claim_evidence'
and i.indisunique
and i.indpred is null
and replace(pg_catalog.pg_get_indexdef(i.indexrelid), ' ', '')
like '%(claim_id,source_id,role)%'
) then
execute 'create unique index kb_apply_claim_evidence_semantic_unique '
'on public.claim_evidence (claim_id, source_id, role)';
end if;
end
$indexes$;
create unique index if not exists one_active_strategy_per_agent
on public.strategies (agent_id) where active;
-- Reviewer-only state transition. The expected type/payload are the exact
-- Python-validated snapshot; a concurrent pending-row mutation makes the
-- locked WHERE clause fail. Reviewer identity comes from session_user mapping.
create or replace function kb_stage.approve_strict_proposal(
p_proposal_id uuid,
p_expected_proposal_type text,
p_expected_payload jsonb,
p_expected_reviewed_by_handle text,
p_review_note text
) returns jsonb
language plpgsql
security definer
set search_path = pg_catalog, pg_temp
as $function$
declare
v_row kb_stage.kb_proposals%rowtype;
v_principal kb_stage.kb_review_principals%rowtype;
v_reviewed_at timestamptz;
begin
if nullif(pg_catalog.btrim(p_expected_reviewed_by_handle), '') is null then
raise exception 'approve_strict_proposal: expected reviewer handle is required';
end if;
if nullif(pg_catalog.btrim(p_review_note), '') is null then
raise exception 'approve_strict_proposal: review note is required';
end if;
select * into v_principal
from kb_stage.kb_review_principals
where db_role = session_user::name
and active;
if not found then
raise exception 'approve_strict_proposal: session role % has no active reviewer principal',
session_user;
end if;
if v_principal.reviewed_by_handle <> pg_catalog.btrim(p_expected_reviewed_by_handle) then
raise exception 'approve_strict_proposal: session role % is mapped to reviewer %, not %',
session_user, v_principal.reviewed_by_handle, p_expected_reviewed_by_handle;
end if;
select * into v_row
from kb_stage.kb_proposals
where id = p_proposal_id
and status = 'pending_review'
and proposal_type = p_expected_proposal_type
and payload = p_expected_payload
for update;
if not found then
raise exception 'approve_strict_proposal: proposal % is not the exact pending snapshot',
p_proposal_id;
end if;
if v_row.proposal_type not in ('revise_strategy', 'add_edge', 'attach_evidence', 'approve_claim') then
raise exception 'approve_strict_proposal: unsupported proposal_type %', v_row.proposal_type;
end if;
if pg_catalog.jsonb_typeof(v_row.payload) <> 'object'
or not (v_row.payload ? 'apply_payload')
or pg_catalog.jsonb_typeof(v_row.payload->'apply_payload') <> 'object' then
raise exception 'approve_strict_proposal: proposal % lacks object payload.apply_payload',
p_proposal_id;
end if;
v_reviewed_at := pg_catalog.clock_timestamp();
insert into kb_stage.kb_proposal_approvals
(proposal_id, proposal_type, payload, reviewed_by_handle,
reviewed_by_agent_id, reviewed_by_db_role, reviewed_at, review_note)
values
(v_row.id, v_row.proposal_type, v_row.payload, v_principal.reviewed_by_handle,
v_principal.reviewed_by_agent_id, session_user::name, v_reviewed_at,
pg_catalog.btrim(p_review_note));
update kb_stage.kb_proposals
set status = 'approved',
reviewed_by_handle = v_principal.reviewed_by_handle,
reviewed_by_agent_id = v_principal.reviewed_by_agent_id,
reviewed_at = v_reviewed_at,
review_note = pg_catalog.btrim(p_review_note),
updated_at = pg_catalog.clock_timestamp()
where id = p_proposal_id
and status = 'pending_review'
and proposal_type = p_expected_proposal_type
and payload = p_expected_payload
returning * into v_row;
if not found then
raise exception 'approve_strict_proposal: proposal % changed while approval was recorded',
p_proposal_id;
end if;
return pg_catalog.jsonb_build_object(
'id', v_row.id::text,
'proposal_type', v_row.proposal_type,
'status', v_row.status,
'reviewed_by_handle', v_row.reviewed_by_handle,
'reviewed_by_agent_id', v_row.reviewed_by_agent_id::text,
'reviewed_by_db_role', session_user::text,
'reviewed_at', v_row.reviewed_at::text,
'review_note', v_row.review_note
);
end
$function$;
-- Apply-side precondition. It locks the proposal for the surrounding transaction
-- and compares both the mutable ledger and immutable approval snapshot.
create or replace function kb_stage.assert_approved_proposal(
p_proposal_id uuid,
p_proposal_type text,
p_payload jsonb,
p_reviewed_by_handle text,
p_reviewed_by_agent_id uuid,
p_reviewed_at timestamptz,
p_review_note text
) returns void
language plpgsql
security definer
set search_path = pg_catalog, pg_temp
as $function$
begin
perform 1
from kb_stage.kb_proposals p
join kb_stage.kb_proposal_approvals a on a.proposal_id = p.id
where p.id = p_proposal_id
and p.status = 'approved'
and p.proposal_type = p_proposal_type
and p.payload = p_payload
and p.reviewed_by_handle = p_reviewed_by_handle
and p.reviewed_by_agent_id is not distinct from p_reviewed_by_agent_id
and p.reviewed_at = p_reviewed_at
and p.review_note = p_review_note
and a.proposal_type = p_proposal_type
and a.payload = p_payload
and a.reviewed_by_handle = p_reviewed_by_handle
and a.reviewed_by_agent_id is not distinct from p_reviewed_by_agent_id
and a.reviewed_at = p_reviewed_at
and a.review_note = p_review_note
for update of p;
if not found then
raise exception 'assert_approved_proposal: proposal % is not the exact immutable approved snapshot',
p_proposal_id;
end if;
end
$function$;
-- Apply-side terminal transition. It repeats every snapshot comparison while
-- the proposal row remains locked, resolves a real service-agent FK, and changes
-- ledger fields only. kb_apply remains a trusted narrow canonical writer; the
-- Python transaction performs and independently verifies typed writes first.
create or replace function kb_stage.finish_approved_proposal(
p_proposal_id uuid,
p_proposal_type text,
p_payload jsonb,
p_reviewed_by_handle text,
p_reviewed_by_agent_id uuid,
p_reviewed_at timestamptz,
p_review_note text,
p_applied_by_handle text
) returns jsonb
language plpgsql
security definer
set search_path = pg_catalog, pg_temp
as $function$
declare
v_row kb_stage.kb_proposals%rowtype;
v_applied_by_agent_id uuid;
begin
if nullif(pg_catalog.btrim(p_applied_by_handle), '') is null then
raise exception 'finish_approved_proposal: applied-by handle is required';
end if;
perform 1
from kb_stage.kb_proposals p
join kb_stage.kb_proposal_approvals a on a.proposal_id = p.id
where p.id = p_proposal_id
and p.status = 'approved'
and p.proposal_type = p_proposal_type
and p.payload = p_payload
and p.reviewed_by_handle = p_reviewed_by_handle
and p.reviewed_by_agent_id is not distinct from p_reviewed_by_agent_id
and p.reviewed_at = p_reviewed_at
and p.review_note = p_review_note
and a.proposal_type = p_proposal_type
and a.payload = p_payload
and a.reviewed_by_handle = p_reviewed_by_handle
and a.reviewed_by_agent_id is not distinct from p_reviewed_by_agent_id
and a.reviewed_at = p_reviewed_at
and a.review_note = p_review_note
for update of p;
if not found then
raise exception 'finish_approved_proposal: proposal % is not the locked immutable approved snapshot',
p_proposal_id;
end if;
select id into v_applied_by_agent_id
from public.agents
where handle = pg_catalog.btrim(p_applied_by_handle)
limit 1;
if v_applied_by_agent_id is null then
raise exception 'finish_approved_proposal: applied-by handle % has no public.agents row',
p_applied_by_handle;
end if;
update kb_stage.kb_proposals
set status = 'applied',
applied_by_handle = pg_catalog.btrim(p_applied_by_handle),
applied_by_agent_id = v_applied_by_agent_id,
applied_at = pg_catalog.clock_timestamp(),
updated_at = pg_catalog.clock_timestamp()
where id = p_proposal_id
and status = 'approved'
and proposal_type = p_proposal_type
and payload = p_payload
and reviewed_by_handle = p_reviewed_by_handle
and reviewed_by_agent_id is not distinct from p_reviewed_by_agent_id
and reviewed_at = p_reviewed_at
and review_note = p_review_note
returning * into v_row;
if not found then
raise exception 'finish_approved_proposal: proposal % changed during apply', p_proposal_id;
end if;
return pg_catalog.jsonb_build_object(
'id', v_row.id::text,
'proposal_type', v_row.proposal_type,
'status', v_row.status,
'applied_by_handle', v_row.applied_by_handle,
'applied_by_agent_id', v_row.applied_by_agent_id::text,
'applied_at', v_row.applied_at::text
);
end
$function$;
alter function kb_stage.approve_strict_proposal(uuid, text, jsonb, text, text)
owner to kb_gate_owner;
alter function kb_stage.assert_approved_proposal(uuid, text, jsonb, text, uuid, timestamptz, text)
owner to kb_gate_owner;
alter function kb_stage.finish_approved_proposal(uuid, text, jsonb, text, uuid, timestamptz, text, text)
owner to kb_gate_owner;
revoke all on function kb_stage.approve_strict_proposal(uuid, text, jsonb, text, text)
from public, kb_gate_owner, kb_apply, kb_review;
revoke all on function kb_stage.assert_approved_proposal(uuid, text, jsonb, text, uuid, timestamptz, text)
from public, kb_gate_owner, kb_apply, kb_review;
revoke all on function kb_stage.finish_approved_proposal(uuid, text, jsonb, text, uuid, timestamptz, text, text)
from public, kb_gate_owner, kb_apply, kb_review;
grant execute on function kb_stage.approve_strict_proposal(uuid, text, jsonb, text, text)
to kb_review;
grant execute on function kb_stage.assert_approved_proposal(uuid, text, jsonb, text, uuid, timestamptz, text)
to kb_apply;
grant execute on function kb_stage.finish_approved_proposal(uuid, text, jsonb, text, uuid, timestamptz, text, text)
to kb_apply;
-- Assert the complete protected catalog contract before commit. These checks
-- make reruns an upgrade proof rather than a best-effort sequence of grants.
do $final_invariants$
declare
v_drift text;
begin
select pg_catalog.string_agg(role.rolname, ', ' order by role.rolname)
into v_drift
from pg_catalog.pg_roles role
where role.rolname in ('kb_gate_owner', 'kb_review', 'kb_apply')
and (
role.rolsuper
or role.rolinherit
or role.rolcreatedb
or role.rolcreaterole
or role.rolreplication
or role.rolbypassrls
or (role.rolname = 'kb_gate_owner' and role.rolcanlogin)
or (role.rolname in ('kb_review', 'kb_apply') and not role.rolcanlogin)
);
if v_drift is not null then
raise exception 'kb_apply_prereqs: protected role attributes did not normalize: %', v_drift;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I -> %I', member_role.rolname, granted_role.rolname),
', ' order by member_role.rolname, granted_role.rolname
)
into v_drift
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');
if v_drift is not null then
raise exception 'kb_apply_prereqs: protected role membership appeared during migration: %', v_drift;
end if;
select pg_catalog.string_agg(
pg_catalog.format(
'%I on %I usage=%s create=%s',
protected_role.rolname,
protected_schema.nspname,
pg_catalog.has_schema_privilege(protected_role.rolname, protected_schema.nspname, 'USAGE'),
pg_catalog.has_schema_privilege(protected_role.rolname, protected_schema.nspname, 'CREATE')
),
', ' order by protected_role.rolname, protected_schema.nspname
)
into v_drift
from (values ('kb_gate_owner'), ('kb_review'), ('kb_apply')) protected_role(rolname)
cross join (values ('public'), ('kb_stage')) protected_schema(nspname)
where not pg_catalog.has_schema_privilege(
protected_role.rolname, protected_schema.nspname, 'USAGE'
)
or pg_catalog.has_schema_privilege(
protected_role.rolname, protected_schema.nspname, 'CREATE'
);
if v_drift is not null then
raise exception 'kb_apply_prereqs: protected schema ACL mismatch: %', v_drift;
end if;
select pg_catalog.string_agg(
pg_catalog.format('%I.%I owned by %I', namespace.nspname, relation.relname, owner_role.rolname),
', ' order by namespace.nspname, relation.relname
)
into v_drift
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, relation.relname) in (
('public', 'agents'),
('public', 'strategies'),
('public', 'strategy_nodes'),
('public', 'claim_evidence'),
('public', 'claim_edges'),
('public', 'claims'),
('public', 'sources'),
('public', 'reasoning_tools'),
('kb_stage', 'kb_proposals'),
('kb_stage', 'kb_review_principals'),
('kb_stage', 'kb_proposal_approvals')
)
and not (
(namespace.nspname = 'kb_stage'
and relation.relname in ('kb_review_principals', 'kb_proposal_approvals')
and owner_role.rolname = 'kb_gate_owner')
or (not (
namespace.nspname = 'kb_stage'
and relation.relname in ('kb_review_principals', 'kb_proposal_approvals')
)
and owner_role.rolname = current_user)
);
if v_drift is not null then
raise exception 'kb_apply_prereqs: protected table ownership mismatch: %', v_drift;
end if;
with expected(signature) as (
values
('approve_strict_proposal(uuid, text, jsonb, text, text)'),
('assert_approved_proposal(uuid, text, jsonb, text, uuid, timestamp with time zone, text)'),
('finish_approved_proposal(uuid, text, jsonb, text, uuid, timestamp with time zone, text, text)')
), actual(signature) as (
select procedure.proname || '(' || pg_catalog.oidvectortypes(procedure.proargtypes) || ')'
from pg_catalog.pg_proc procedure
join pg_catalog.pg_namespace namespace on namespace.oid = procedure.pronamespace
where namespace.nspname = 'kb_stage'
and procedure.proname in (
'approve_strict_proposal',
'assert_approved_proposal',
'finish_approved_proposal'
)
), delta(kind, signature) as (
select 'unexpected', actual.signature
from actual
where not exists (select 1 from expected where expected.signature = actual.signature)
union all
select 'missing', expected.signature
from expected
where not exists (select 1 from actual where actual.signature = expected.signature)
)
select pg_catalog.string_agg(kind || ' ' || signature, ', ' order by kind, signature)
into v_drift
from delta;
if v_drift is not null then
raise exception 'kb_apply_prereqs: protected function signature mismatch: %', v_drift;
end if;
select pg_catalog.string_agg(
pg_catalog.format(
'%I.%I(%s) owner=%I security_definer=%s config=%s',
namespace.nspname,
procedure.proname,
pg_catalog.oidvectortypes(procedure.proargtypes),
owner_role.rolname,
procedure.prosecdef,
procedure.proconfig
),
', ' order by procedure.proname
)
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 (
'approve_strict_proposal',
'assert_approved_proposal',
'finish_approved_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'
);
if v_drift is not null then
raise exception 'kb_apply_prereqs: protected function attribute mismatch: %', v_drift;
end if;
with tracked(schema_name, object_name) as (
values
('public', 'agents'),
('public', 'strategies'),
('public', 'strategy_nodes'),
('public', 'claim_evidence'),
('public', 'claim_edges'),
('public', 'claims'),
('public', 'sources'),
('public', 'reasoning_tools'),
('kb_stage', 'kb_proposals'),
('kb_stage', 'kb_review_principals'),
('kb_stage', 'kb_proposal_approvals')
)
select pg_catalog.string_agg(
pg_catalog.format(
'%I.%I.%I -> %s %s grantable=%s',
namespace.nspname,
relation.relname,
attribute.attname,
case
when acl.grantee = 0 then 'PUBLIC'
else pg_catalog.pg_get_userbyid(acl.grantee)
end,
pg_catalog.upper(acl.privilege_type),
acl.is_grantable
),
', ' order by namespace.nspname, relation.relname, attribute.attnum, acl.grantee
)
into v_drift
from tracked
join pg_catalog.pg_namespace namespace on namespace.nspname = tracked.schema_name
join pg_catalog.pg_class relation
on relation.relnamespace = namespace.oid
and relation.relname = tracked.object_name
join pg_catalog.pg_attribute attribute on attribute.attrelid = relation.oid
cross join lateral pg_catalog.aclexplode(attribute.attacl) acl
left join pg_catalog.pg_roles grantee_role on grantee_role.oid = acl.grantee
where attribute.attnum > 0
and not attribute.attisdropped
and (acl.grantee = 0
or grantee_role.rolname in ('kb_gate_owner', 'kb_review', 'kb_apply'));
if v_drift is not null then
raise exception 'kb_apply_prereqs: protected column ACL mismatch: %', v_drift;
end if;
with expected(schema_name, object_name, grantee, privilege_type, is_grantable) as (
values
('public', 'agents', 'kb_gate_owner', 'SELECT', false),
('public', 'agents', 'kb_apply', 'SELECT', false),
('public', 'agents', 'kb_review', 'SELECT', false),
('public', 'strategies', 'kb_apply', 'SELECT', false),
('public', 'strategies', 'kb_apply', 'INSERT', false),
('public', 'strategies', 'kb_apply', 'UPDATE', false),
('public', 'strategy_nodes', 'kb_apply', 'SELECT', false),
('public', 'strategy_nodes', 'kb_apply', 'INSERT', false),
('public', 'strategy_nodes', 'kb_apply', 'UPDATE', false),
('public', 'claim_evidence', 'kb_apply', 'SELECT', false),
('public', 'claim_evidence', 'kb_apply', 'INSERT', false),
('public', 'claim_edges', 'kb_apply', 'SELECT', false),
('public', 'claim_edges', 'kb_apply', 'INSERT', false),
('public', 'claims', 'kb_apply', 'SELECT', false),
('public', 'claims', 'kb_apply', 'INSERT', false),
('public', 'sources', 'kb_apply', 'SELECT', false),
('public', 'sources', 'kb_apply', 'INSERT', false),
('public', 'reasoning_tools', 'kb_apply', 'SELECT', false),
('public', 'reasoning_tools', 'kb_apply', 'INSERT', false),
('kb_stage', 'kb_proposals', 'kb_gate_owner', 'SELECT', false),
('kb_stage', 'kb_proposals', 'kb_gate_owner', 'UPDATE', false),
('kb_stage', 'kb_proposals', 'kb_apply', 'SELECT', false),
('kb_stage', 'kb_proposals', 'kb_review', 'SELECT', false)
), tracked(schema_name, object_name) as (
values
('public', 'agents'),
('public', 'strategies'),
('public', 'strategy_nodes'),
('public', 'claim_evidence'),
('public', 'claim_edges'),
('public', 'claims'),
('public', 'sources'),
('public', 'reasoning_tools'),
('kb_stage', 'kb_proposals'),
('kb_stage', 'kb_review_principals'),
('kb_stage', 'kb_proposal_approvals')
), actual(schema_name, object_name, grantee, privilege_type, is_grantable) as (
select distinct
namespace.nspname::text,
relation.relname::text,
(case
when acl.grantee = 0 then 'PUBLIC'
else grantee_role.rolname
end)::text,
pg_catalog.upper(acl.privilege_type),
acl.is_grantable
from tracked
join pg_catalog.pg_namespace namespace on namespace.nspname = tracked.schema_name
join pg_catalog.pg_class relation
on relation.relnamespace = namespace.oid
and relation.relname = tracked.object_name
cross join lateral pg_catalog.aclexplode(
coalesce(relation.relacl, pg_catalog.acldefault('r', relation.relowner))
) acl
left join pg_catalog.pg_roles grantee_role on grantee_role.oid = acl.grantee
where acl.grantee <> relation.relowner
and (acl.grantee = 0
or grantee_role.rolname in ('kb_gate_owner', 'kb_review', 'kb_apply'))
), delta(kind, schema_name, object_name, grantee, privilege_type, is_grantable) as (
select 'unexpected', actual.*
from actual
where not exists (
select 1 from expected where pg_catalog.to_jsonb(expected) = pg_catalog.to_jsonb(actual)
)
union all
select 'missing', expected.*
from expected
where not exists (
select 1 from actual where pg_catalog.to_jsonb(actual) = pg_catalog.to_jsonb(expected)
)
)
select pg_catalog.string_agg(
pg_catalog.format(
'%s %I.%I -> %I %s grantable=%s',
kind, schema_name, object_name, grantee, privilege_type, is_grantable
),
', ' order by kind, schema_name, object_name, grantee, privilege_type
)
into v_drift
from delta;
if v_drift is not null then
raise exception 'kb_apply_prereqs: protected table ACL mismatch: %', v_drift;
end if;
with expected(signature, grantee, privilege_type, is_grantable) as (
values
('approve_strict_proposal(uuid, text, jsonb, text, text)', 'kb_review', 'EXECUTE', false),
('assert_approved_proposal(uuid, text, jsonb, text, uuid, timestamp with time zone, text)',
'kb_apply', 'EXECUTE', false),
('finish_approved_proposal(uuid, text, jsonb, text, uuid, timestamp with time zone, text, text)',
'kb_apply', 'EXECUTE', false)
), actual(signature, grantee, privilege_type, is_grantable) as (
select distinct
procedure.proname || '(' || pg_catalog.oidvectortypes(procedure.proargtypes) || ')',
(case
when acl.grantee = 0 then 'PUBLIC'
else pg_catalog.pg_get_userbyid(acl.grantee)
end)::text,
pg_catalog.upper(acl.privilege_type),
acl.is_grantable
from pg_catalog.pg_proc procedure
join pg_catalog.pg_namespace namespace on namespace.oid = procedure.pronamespace
cross join lateral pg_catalog.aclexplode(
coalesce(procedure.proacl, pg_catalog.acldefault('f', procedure.proowner))
) acl
where namespace.nspname = 'kb_stage'
and procedure.proname in (
'approve_strict_proposal',
'assert_approved_proposal',
'finish_approved_proposal'
)
and acl.grantee <> procedure.proowner
), delta(kind, signature, grantee, privilege_type, is_grantable) as (
select 'unexpected', actual.*
from actual
where not exists (
select 1 from expected where pg_catalog.to_jsonb(expected) = pg_catalog.to_jsonb(actual)
)
union all
select 'missing', expected.*
from expected
where not exists (
select 1 from actual where pg_catalog.to_jsonb(actual) = pg_catalog.to_jsonb(expected)
)
)
select pg_catalog.string_agg(
pg_catalog.format(
'%s %s -> %I %s grantable=%s',
kind, signature, grantee, privilege_type, is_grantable
),
', ' order by kind, signature, grantee, privilege_type
)
into v_drift
from delta;
if v_drift is not null then
raise exception 'kb_apply_prereqs: protected function ACL mismatch: %', v_drift;
end if;
end
$final_invariants$;
commit;