\set ON_ERROR_STOP on

-- One-time GCP staging provisioning for Leo's Cloud SQL identity. Run this
-- only through ops/provision_gcp_leoclean_runtime_role.sh; psql's \password
-- command encrypts the supplied value client-side, so cleartext is never SQL.
--
-- The login can read the canonical allowlist and can stage a pending proposal
-- through one narrowly-scoped SECURITY DEFINER function. It cannot inherit or
-- SET ROLE into another principal, forge the proposer identity, write directly
-- to canonical/staging tables, or inspect the legacy teleo_restore audit schema.
-- Keep the runtime unable to authenticate until both database phases have
-- completed and verified. Any failure before the final transaction therefore
-- leaves a disabled role instead of a partially provisioned login.
select 'create role leoclean_kb_runtime nologin nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit 8'
where not exists (select 1 from pg_catalog.pg_roles where rolname = 'leoclean_kb_runtime')
\gexec

select 'create role leoclean_kb_stage_owner nologin nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit -1'
where not exists (select 1 from pg_catalog.pg_roles where rolname = 'leoclean_kb_stage_owner')
\gexec

-- Cloud SQL's postgres administrator has CREATEROLE but is deliberately not a
-- true PostgreSQL superuser. It may not spell SUPERUSER, REPLICATION, or
-- BYPASSRLS attributes in ALTER ROLE, even when setting their safe false form.
-- CREATE ROLE above establishes those attributes as false; the verification
-- blocks below fail closed if an existing role ever carries any of them.
alter role leoclean_kb_runtime
  with nologin nocreatedb nocreaterole noinherit connection limit 8;
alter role leoclean_kb_stage_owner
  with nologin nocreatedb nocreaterole noinherit connection limit -1
  password null;

-- Snapshot every non-superuser principal connected to either scoped role before
-- removing the direct membership edges. Existing sessions can retain an
-- already-selected SET ROLE identity after NOLOGIN/revocation, so all other
-- connected sessions must be terminated before password or privilege work.
create temporary table leoclean_fenced_roles (
  role_oid oid primary key,
  role_name name unique not null
) on commit preserve rows;

with recursive connected(role_oid) as (
  values
    ('leoclean_kb_runtime'::pg_catalog.regrole::oid),
    ('leoclean_kb_stage_owner'::pg_catalog.regrole::oid)
  union
  select membership.member
    from connected
    join pg_catalog.pg_auth_members membership
      on membership.roleid = connected.role_oid
)
insert into pg_temp.leoclean_fenced_roles (role_oid, role_name)
select role_row.oid, role_row.rolname
  from connected
  join pg_catalog.pg_roles role_row on role_row.oid = connected.role_oid
 where not role_row.rolsuper;

-- NOINHERIT does not prevent SET ROLE. Remove every direct membership edge in
-- either direction before terminating the snapshotted sessions, closing the
-- race for newly connected membership principals.
select format('revoke %I from %I', granted_role.rolname, member_role.rolname)
  from pg_catalog.pg_auth_members membership
  join pg_catalog.pg_roles granted_role on granted_role.oid = membership.roleid
  join pg_catalog.pg_roles member_role on member_role.oid = membership.member
 where granted_role.rolname in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
    or member_role.rolname in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
\gexec

do $session_fence$
declare
  target record;
  remaining text;
begin
  for target in
    select activity.pid
      from pg_catalog.pg_stat_activity activity
      join pg_temp.leoclean_fenced_roles fenced on fenced.role_name = activity.usename
     where activity.pid <> pg_catalog.pg_backend_pid()
       and activity.backend_type = 'client backend'
  loop
    if not pg_catalog.pg_terminate_backend(target.pid, 5000) then
      raise exception 'could not terminate a pre-existing scoped-role session';
    end if;
  end loop;

  select pg_catalog.string_agg(activity.pid::text, ',' order by activity.pid)
    into remaining
    from pg_catalog.pg_stat_activity activity
    join pg_temp.leoclean_fenced_roles fenced on fenced.role_name = activity.usename
   where activity.pid <> pg_catalog.pg_backend_pid()
     and activity.backend_type = 'client backend';
  if remaining is not null then
    raise exception 'pre-existing scoped-role sessions remain after termination';
  end if;

  select pg_catalog.string_agg(prepared.gid, ',' order by prepared.gid)
    into remaining
    from pg_catalog.pg_prepared_xacts prepared
    join pg_temp.leoclean_fenced_roles fenced on fenced.role_name = prepared.owner;
  if remaining is not null then
    raise exception 'scoped-role prepared transactions must be resolved before provisioning';
  end if;
end
$session_fence$;

-- This is deliberately after the autocommitted NOLOGIN/session/membership
-- fence. Interruption can leave a disabled role, never a still-enabled old
-- login or an old connected session. The wrapper supplies the two prompts.
\password leoclean_kb_runtime

alter role leoclean_kb_runtime reset all;
alter role leoclean_kb_stage_owner reset all;
drop table pg_temp.leoclean_fenced_roles;

select pg_catalog.format(
         'alter role %I in database %I reset all',
         role_row.rolname,
         database_row.datname
       )
  from pg_catalog.pg_db_role_setting setting_row
  join pg_catalog.pg_roles role_row on role_row.oid = setting_row.setrole
  join pg_catalog.pg_database database_row on database_row.oid = setting_row.setdatabase
 where role_row.rolname in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
\gexec

-- PostgreSQL normally grants TEMP to PUBLIC. A role-specific REVOKE cannot
-- override that inherited grant, while REVOKE TEMP FROM PUBLIC would be a
-- database-wide change that could break kb_apply/review or operator workflows.
-- Leave PUBLIC TEMP policy unchanged here and surface it in the final receipt.
-- TEMP cannot shadow this SECURITY DEFINER boundary: pg_catalog is first,
-- pg_temp is explicitly last, and every relation reference is schema-qualified.
-- Database CREATE, direct TEMP grants, and all unexpected direct database ACLs
-- are rejected below. A database-wide TEMP removal requires a separate role-use
-- inventory and explicit grants to every workload that still needs it.

alter role leoclean_kb_runtime in database teleo_canonical reset all;
alter role leoclean_kb_stage_owner in database teleo_canonical reset all;
alter role leoclean_kb_runtime in database teleo_kb reset all;
alter role leoclean_kb_stage_owner in database teleo_kb reset all;

alter role leoclean_kb_runtime in database teleo_canonical
  set search_path = pg_catalog, public, kb_stage;
alter role leoclean_kb_runtime in database teleo_canonical
  set statement_timeout = '15s';
alter role leoclean_kb_runtime in database teleo_canonical
  set lock_timeout = '2s';

\connect teleo_canonical

begin;

-- CONNECT granted to PUBLIC cannot be denied to one role with a role-specific
-- REVOKE. Preserve every existing non-scoped principal's effective CONNECT
-- set as explicit ACLs, remove the cluster-wide PUBLIC grant from every
-- database (including templates), and then grant the runtime its sole target.
-- This runs in the same transaction as every remaining privilege/topology
-- assertion, so a later failure restores the original ACLs atomically.
create temporary table leoclean_preserved_database_connect (
  role_name name not null,
  database_name name not null,
  primary key (role_name, database_name)
) on commit drop;

insert into pg_temp.leoclean_preserved_database_connect (role_name, database_name)
select role_row.rolname, database_row.datname
  from pg_catalog.pg_roles role_row
 cross join pg_catalog.pg_database database_row
 where role_row.rolname not in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
   and pg_catalog.has_database_privilege(role_row.oid, database_row.oid, 'CONNECT');

select pg_catalog.format('revoke connect on database %I from public', database_row.datname)
  from pg_catalog.pg_database database_row
\gexec

select pg_catalog.format(
         'revoke all privileges on database %I from leoclean_kb_runtime, leoclean_kb_stage_owner',
         database_row.datname
       )
  from pg_catalog.pg_database database_row
\gexec

select pg_catalog.format(
         'grant connect on database %I to %I',
         preserved.database_name,
         preserved.role_name
       )
  from pg_temp.leoclean_preserved_database_connect preserved
 order by preserved.database_name, preserved.role_name
\gexec

grant connect on database teleo_canonical to leoclean_kb_runtime;

-- The deploy principal needs temporary ownership authority to replace an
-- existing function on idempotent reruns. This membership is transaction-local
-- and is revoked before verification/commit.
select format('grant leoclean_kb_stage_owner to %I', current_user)
 where current_user <> 'leoclean_kb_stage_owner'
\gexec

revoke all on schema public, kb_stage
  from leoclean_kb_runtime, leoclean_kb_stage_owner;
revoke all privileges on all tables in schema public, kb_stage
  from leoclean_kb_runtime, leoclean_kb_stage_owner;
revoke all privileges on all sequences in schema public, kb_stage
  from leoclean_kb_runtime, leoclean_kb_stage_owner;
revoke all privileges on all functions in schema public, kb_stage
  from leoclean_kb_runtime, leoclean_kb_stage_owner;
revoke all privileges on all procedures in schema public, kb_stage
  from leoclean_kb_runtime, leoclean_kb_stage_owner;

-- Built-in defaults also grant PUBLIC EXECUTE on newly created application
-- routines. Preserve every existing non-scoped role's effective access,
-- remove PUBLIC/scoped execution from the complete application routine set,
-- and restore the inventory explicitly. The reviewed staging function is
-- normalized again after CREATE OR REPLACE below.
create temporary table leoclean_preserved_app_routine_execute (
  role_name name not null,
  function_oid oid not null,
  primary key (role_name, function_oid)
) on commit drop;

insert into pg_temp.leoclean_preserved_app_routine_execute (role_name, function_oid)
select role_row.rolname, function_row.oid
  from pg_catalog.pg_roles role_row
 cross join pg_catalog.pg_proc function_row
  join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
 where role_row.rolname not in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
   and namespace.nspname in ('public', 'kb_stage')
   and pg_catalog.has_function_privilege(role_row.oid, function_row.oid, 'EXECUTE');

select pg_catalog.format(
         'revoke execute on %s %I.%I(%s) from public, leoclean_kb_runtime, leoclean_kb_stage_owner',
         case when function_row.prokind = 'p' then 'procedure' else 'function' end,
         namespace.nspname,
         function_row.proname,
         pg_catalog.pg_get_function_identity_arguments(function_row.oid)
       )
  from pg_catalog.pg_proc function_row
  join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
 where namespace.nspname in ('public', 'kb_stage')
\gexec

select pg_catalog.format(
         'grant execute on %s %I.%I(%s) to %I',
         case when function_row.prokind = 'p' then 'procedure' else 'function' end,
         namespace.nspname,
         function_row.proname,
         pg_catalog.pg_get_function_identity_arguments(function_row.oid),
         preserved.role_name
       )
  from pg_temp.leoclean_preserved_app_routine_execute preserved
  join pg_catalog.pg_proc function_row on function_row.oid = preserved.function_oid
  join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
 order by namespace.nspname, function_row.proname, preserved.role_name
\gexec

select pg_catalog.format(
         'revoke all privileges on large object %s from leoclean_kb_runtime, leoclean_kb_stage_owner',
         metadata.oid
       )
  from pg_catalog.pg_largeobject_metadata metadata
\gexec

-- Cloud SQL owns pg_catalog and does not expose its true PostgreSQL
-- superuser. Its customer administrator therefore cannot revoke the built-in
-- PUBLIC EXECUTE grants on large-object mutators. Keep that provider-owned
-- residual immutable and explicit: reject inventory drift and any direct ACL
-- granted to either scoped role, while separately proving that neither role
-- owns or can reach an existing large object. The no-send staging runtime has
-- no raw-SQL surface and its query-contract checker rejects every routine in
-- this inventory. This is a staging-only compensating boundary, not a claim
-- that arbitrary use of the raw database credential is write-free.
create temporary table leoclean_expected_lo_routine_residual (
  signature text primary key,
  public_execute boolean not null
) on commit drop;

insert into pg_temp.leoclean_expected_lo_routine_residual (signature, public_execute) values
  ('lo_creat(integer)', true),
  ('lo_create(oid)', true),
  ('lo_export(oid, text)', false),
  ('lo_from_bytea(oid, bytea)', true),
  ('lo_import(text)', false),
  ('lo_import(text, oid)', false),
  ('lo_open(oid, integer)', true),
  ('lo_put(oid, bigint, bytea)', true),
  ('lo_truncate(integer, integer)', true),
  ('lo_truncate64(integer, bigint)', true),
  ('lo_unlink(oid)', true),
  ('lowrite(integer, bytea)', true);

do $large_object_residual$
declare
  unexpected text;
begin
  with actual as (
    select function_row.oid,
           pg_catalog.format(
             '%I(%s)',
             function_row.proname,
             pg_catalog.pg_get_function_identity_arguments(function_row.oid)
           ) as signature,
           exists (
             select 1
               from pg_catalog.aclexplode(
                 coalesce(function_row.proacl, pg_catalog.acldefault('f', function_row.proowner))
               ) acl
              where acl.grantee = 0
                and acl.privilege_type = 'EXECUTE'
           ) as public_execute,
           pg_catalog.has_function_privilege('leoclean_kb_runtime', function_row.oid, 'EXECUTE') as runtime_execute,
           pg_catalog.has_function_privilege('leoclean_kb_stage_owner', function_row.oid, 'EXECUTE') as owner_execute
      from pg_catalog.pg_proc function_row
      join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
     where namespace.nspname = 'pg_catalog'
       and function_row.proname in (
         'lo_creat', 'lo_create', 'lo_export', 'lo_from_bytea', 'lo_import',
         'lo_open', 'lo_put', 'lo_truncate', 'lo_truncate64', 'lo_unlink', 'lowrite'
       )
  )
  select pg_catalog.string_agg(
           coalesce(expected.signature, actual.signature),
           ', '
           order by coalesce(expected.signature, actual.signature)
         )
    into unexpected
    from pg_temp.leoclean_expected_lo_routine_residual expected
    full join actual using (signature)
   where expected.signature is null
      or actual.signature is null
      or actual.public_execute is distinct from expected.public_execute
      or actual.runtime_execute is distinct from expected.public_execute
      or actual.owner_execute is distinct from expected.public_execute;
  if unexpected is not null then
    raise exception 'provider-owned large-object routine residual drifted: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format(
             '%I(%s):%s',
             function_row.proname,
             pg_catalog.pg_get_function_identity_arguments(function_row.oid),
             role_row.rolname
           ),
           ', '
           order by function_row.proname, role_row.rolname
         )
    into unexpected
    from pg_catalog.pg_proc function_row
    join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
   cross join lateral pg_catalog.aclexplode(function_row.proacl) acl
    join pg_catalog.pg_roles role_row on role_row.oid = acl.grantee
   where namespace.nspname = 'pg_catalog'
     and function_row.proname in (
       'lo_creat', 'lo_create', 'lo_export', 'lo_from_bytea', 'lo_import',
       'lo_open', 'lo_put', 'lo_truncate', 'lo_truncate64', 'lo_unlink', 'lowrite'
     )
     and role_row.rolname in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
     and acl.privilege_type = 'EXECUTE';
  if unexpected is not null then
    raise exception 'scoped Leo roles have direct large-object routine ACLs: %', unexpected;
  end if;
end
$large_object_residual$;

select pg_catalog.format(
         'revoke all privileges on parameter %I from leoclean_kb_runtime, leoclean_kb_stage_owner',
         parameter_acl.parname
       )
  from pg_catalog.pg_parameter_acl parameter_acl
\gexec

-- Table-level REVOKE does not clear column ACLs. Clear every column ACL for both
-- principals before rebuilding the exact allowlist below.
select format(
         'revoke all privileges (%s) on table %I.%I from %I',
         pg_catalog.string_agg(pg_catalog.quote_ident(attribute.attname), ', ' order by attribute.attnum),
         namespace.nspname,
         relation.relname,
         target.rolname
       )
  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
 cross join (
   values ('leoclean_kb_runtime'), ('leoclean_kb_stage_owner')
 ) as target(rolname)
 where namespace.nspname in ('public', 'kb_stage')
   and relation.relkind in ('r', 'p', 'v', 'm', 'f')
   and attribute.attnum > 0
   and not attribute.attisdropped
 group by namespace.nspname, relation.relname, target.rolname
\gexec

grant usage on schema public, kb_stage to leoclean_kb_runtime;

-- Bind runtime reads to the exact 92 columns used by the reviewed adapter.
-- Table-level SELECT is deliberately absent so later columns do not become
-- readable before this manifest and the adapter are reviewed together.
create temporary table leoclean_runtime_read_column_allowlist (
  schema_name name not null,
  relation_name name not null,
  column_name name not null,
  column_ordinal smallint not null,
  primary key (schema_name, relation_name, column_name),
  unique (schema_name, relation_name, column_ordinal)
) on commit drop;

insert into pg_temp.leoclean_runtime_read_column_allowlist (
  schema_name,
  relation_name,
  column_name,
  column_ordinal
) values
  ('public', 'agents', 'id', 1),
  ('public', 'agents', 'handle', 2),
  ('public', 'claims', 'id', 1),
  ('public', 'claims', 'type', 2),
  ('public', 'claims', 'text', 3),
  ('public', 'claims', 'status', 4),
  ('public', 'claims', 'confidence', 5),
  ('public', 'claims', 'tags', 6),
  ('public', 'claims', 'superseded_by', 7),
  ('public', 'claims', 'created_at', 8),
  ('public', 'claims', 'updated_at', 9),
  ('public', 'claim_evidence', 'claim_id', 1),
  ('public', 'claim_evidence', 'source_id', 2),
  ('public', 'claim_evidence', 'role', 3),
  ('public', 'claim_evidence', 'weight', 4),
  ('public', 'claim_edges', 'id', 1),
  ('public', 'claim_edges', 'from_claim', 2),
  ('public', 'claim_edges', 'to_claim', 3),
  ('public', 'claim_edges', 'edge_type', 4),
  ('public', 'sources', 'id', 1),
  ('public', 'sources', 'source_type', 2),
  ('public', 'sources', 'url', 3),
  ('public', 'sources', 'storage_path', 4),
  ('public', 'sources', 'excerpt', 5),
  ('public', 'sources', 'hash', 6),
  ('public', 'personas', 'agent_id', 1),
  ('public', 'personas', 'name', 2),
  ('public', 'personas', 'voice', 3),
  ('public', 'personas', 'role', 4),
  ('public', 'personas', 'source_ref', 5),
  ('public', 'personas', 'lens', 6),
  ('public', 'strategies', 'agent_id', 1),
  ('public', 'strategies', 'diagnosis', 2),
  ('public', 'strategies', 'guiding_policy', 3),
  ('public', 'strategies', 'proximate_objectives', 4),
  ('public', 'strategies', 'version', 5),
  ('public', 'strategies', 'active', 6),
  ('public', 'beliefs', 'agent_id', 1),
  ('public', 'beliefs', 'level', 2),
  ('public', 'beliefs', 'statement', 3),
  ('public', 'beliefs', 'falsifier', 4),
  ('public', 'beliefs', 'rank', 5),
  ('public', 'beliefs', 'status', 6),
  ('public', 'blindspots', 'agent_id', 1),
  ('public', 'blindspots', 'name', 2),
  ('public', 'blindspots', 'description', 3),
  ('public', 'blindspots', 'correction', 4),
  ('public', 'blindspots', 'kind', 5),
  ('public', 'blindspots', 'status', 6),
  ('public', 'agent_roles', 'agent_id', 1),
  ('public', 'agent_roles', 'title', 2),
  ('public', 'agent_roles', 'description', 3),
  ('public', 'peer_models', 'subject_id', 1),
  ('public', 'peer_models', 'peer_id', 2),
  ('public', 'peer_models', 'domain', 3),
  ('public', 'peer_models', 'outranks_on', 4),
  ('public', 'peer_models', 'deference_rule', 5),
  ('public', 'behavioral_rules', 'agent_id', 1),
  ('public', 'behavioral_rules', 'category', 2),
  ('public', 'behavioral_rules', 'rule', 3),
  ('public', 'behavioral_rules', 'rationale', 4),
  ('public', 'contributor_rules', 'agent_id', 1),
  ('public', 'contributor_rules', 'name', 2),
  ('public', 'contributor_rules', 'directive', 3),
  ('public', 'contributor_rules', 'ci_tier', 4),
  ('public', 'contributor_rules', 'weighting', 5),
  ('public', 'contributor_rules', 'rationale', 6),
  ('public', 'reasoning_tools', 'agent_id', 1),
  ('public', 'reasoning_tools', 'name', 2),
  ('public', 'reasoning_tools', 'description', 3),
  ('public', 'reasoning_tools', 'category', 4),
  ('public', 'governance_gates', 'agent_id', 1),
  ('public', 'governance_gates', 'name', 2),
  ('public', 'governance_gates', 'criteria', 3),
  ('public', 'governance_gates', 'evidence_bar', 4),
  ('public', 'governance_gates', 'pass_condition', 5),
  ('kb_stage', 'kb_proposals', 'id', 1),
  ('kb_stage', 'kb_proposals', 'proposal_type', 2),
  ('kb_stage', 'kb_proposals', 'status', 3),
  ('kb_stage', 'kb_proposals', 'proposed_by_handle', 4),
  ('kb_stage', 'kb_proposals', 'proposed_by_agent_id', 5),
  ('kb_stage', 'kb_proposals', 'channel', 6),
  ('kb_stage', 'kb_proposals', 'source_ref', 7),
  ('kb_stage', 'kb_proposals', 'rationale', 8),
  ('kb_stage', 'kb_proposals', 'payload', 9),
  ('kb_stage', 'kb_proposals', 'reviewed_by_handle', 10),
  ('kb_stage', 'kb_proposals', 'reviewed_at', 11),
  ('kb_stage', 'kb_proposals', 'review_note', 12),
  ('kb_stage', 'kb_proposals', 'applied_by_handle', 13),
  ('kb_stage', 'kb_proposals', 'applied_at', 14),
  ('kb_stage', 'kb_proposals', 'created_at', 15),
  ('kb_stage', 'kb_proposals', 'updated_at', 16);

select pg_catalog.format(
         'grant select (%s) on table %I.%I to leoclean_kb_runtime',
         pg_catalog.string_agg(
           pg_catalog.quote_ident(allowlist.column_name),
           ', '
           order by allowlist.column_ordinal
         ),
         allowlist.schema_name,
         allowlist.relation_name
       )
  from pg_temp.leoclean_runtime_read_column_allowlist allowlist
 group by allowlist.schema_name, allowlist.relation_name
 order by allowlist.schema_name, allowlist.relation_name
\gexec

-- The function owner is a dedicated NOLOGIN role. Grant only the columns the
-- function reads/inserts. CREATE is temporary and is revoked immediately after
-- ownership is assigned.
grant usage, create on schema kb_stage to leoclean_kb_stage_owner;
grant usage on schema public to leoclean_kb_stage_owner;
grant select (id, handle) on public.agents to leoclean_kb_stage_owner;
grant select on kb_stage.kb_proposals to leoclean_kb_stage_owner;
grant insert (
  proposal_type,
  status,
  proposed_by_handle,
  proposed_by_agent_id,
  channel,
  source_ref,
  rationale,
  payload
) on kb_stage.kb_proposals to leoclean_kb_stage_owner;

-- Remove the prior caller-supplied-identity overload before installing the
-- session-bound form. A runtime caller can only stage as canonical agent Leo.
drop function if exists kb_stage.stage_leoclean_proposal(text, text, text, text, text, jsonb);

create or replace function kb_stage.stage_leoclean_proposal(
  p_proposal_type text,
  p_channel text,
  p_source_ref text,
  p_rationale text,
  p_payload jsonb
) returns jsonb
language plpgsql
security definer
set search_path = pg_catalog, pg_temp
as $function$
declare
  staged kb_stage.kb_proposals%rowtype;
  proposer_id uuid;
begin
  if session_user <> 'leoclean_kb_runtime' then
    raise exception 'stage_leoclean_proposal is restricted to leoclean_kb_runtime'
      using errcode = '42501';
  end if;
  if p_proposal_type not in ('revise_claim', 'revise_strategy', 'add_edge') then
    raise exception 'unsupported Leo proposal type: %', p_proposal_type using errcode = '22023';
  end if;
  if nullif(btrim(p_source_ref), '') is null then
    raise exception 'source_ref is required' using errcode = '22023';
  end if;
  if nullif(btrim(p_rationale), '') is null then
    raise exception 'rationale is required' using errcode = '22023';
  end if;
  if p_payload is null or jsonb_typeof(p_payload) <> 'object' then
    raise exception 'proposal payload must be a JSON object' using errcode = '22023';
  end if;

  select agent.id
    into proposer_id
    from public.agents as agent
   where agent.handle = 'leo';

  if proposer_id is null then
    raise exception 'canonical Leo agent row is required' using errcode = '23503';
  end if;

  insert into kb_stage.kb_proposals (
    proposal_type,
    status,
    proposed_by_handle,
    proposed_by_agent_id,
    channel,
    source_ref,
    rationale,
    payload
  ) values (
    p_proposal_type,
    'pending_review',
    'leo',
    proposer_id,
    coalesce(nullif(p_channel, ''), 'telegram'),
    p_source_ref,
    p_rationale,
    p_payload
  )
  returning * into staged;

  return to_jsonb(staged);
end
$function$;

alter function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb)
  owner to leoclean_kb_stage_owner;
revoke create on schema kb_stage from leoclean_kb_stage_owner;

-- Normalize explicit ACL entries left by CREATE OR REPLACE or an earlier grant.
-- A newly-created function starts with owner EXECUTE, while CREATE OR REPLACE
-- preserves an existing ACL on reruns. Revoke every scoped/default entry here
-- so the first successful provision already has the same runtime-only ACL as
-- every later provision.
revoke all on function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb)
  from public, leoclean_kb_runtime, leoclean_kb_stage_owner;
select format(
         'revoke all on function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb) from %I',
         grantee.rolname
       )
  from pg_catalog.pg_proc function_row
 cross join lateral pg_catalog.aclexplode(
   coalesce(
     function_row.proacl,
     pg_catalog.acldefault('f', function_row.proowner)
   )
 ) as acl
  join pg_catalog.pg_roles grantee on grantee.oid = acl.grantee
 where function_row.oid = 'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)'::pg_catalog.regprocedure
\gexec

grant execute on function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb)
  to leoclean_kb_runtime;

select format('revoke leoclean_kb_stage_owner from %I', current_user)
 where current_user <> 'leoclean_kb_stage_owner'
\gexec

do $verification$
declare
  unexpected text;
  runtime_oid oid := 'leoclean_kb_runtime'::pg_catalog.regrole::oid;
  owner_oid oid := 'leoclean_kb_stage_owner'::pg_catalog.regrole::oid;
  stage_function oid := 'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)'::pg_catalog.regprocedure::oid;
begin
  if exists (
    select 1
      from pg_catalog.pg_roles role_row
     where role_row.rolname = 'leoclean_kb_runtime'
       and (role_row.rolcanlogin
            or role_row.rolsuper
            or role_row.rolcreatedb
            or role_row.rolcreaterole
            or role_row.rolinherit
            or role_row.rolreplication
            or role_row.rolbypassrls
            or role_row.rolconnlimit <> 8)
  ) then
    raise exception 'leoclean_kb_runtime has unsafe provisioning role attributes';
  end if;

  if exists (
    select 1
      from pg_catalog.pg_roles role_row
     where role_row.rolname = 'leoclean_kb_stage_owner'
       and (role_row.rolcanlogin
            or role_row.rolsuper
            or role_row.rolcreatedb
            or role_row.rolcreaterole
            or role_row.rolinherit
            or role_row.rolreplication
            or role_row.rolbypassrls
            or role_row.rolconnlimit <> -1)
  ) then
    raise exception 'leoclean_kb_stage_owner has unsafe role attributes';
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%I->%I', granted_role.rolname, member_role.rolname),
           ', '
           order by granted_role.rolname, member_role.rolname
         )
    into unexpected
    from pg_catalog.pg_auth_members membership
    join pg_catalog.pg_roles granted_role on granted_role.oid = membership.roleid
    join pg_catalog.pg_roles member_role on member_role.oid = membership.member
   where membership.roleid in (runtime_oid, owner_oid)
      or membership.member in (runtime_oid, owner_oid);
  if unexpected is not null then
    raise exception 'scoped Leo roles retain role memberships: %', unexpected;
  end if;

  if not exists (
    select 1
      from pg_catalog.pg_class relation
      join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
     where namespace.nspname = 'kb_stage'
       and relation.relname = 'kb_proposals'
       and relation.relkind = 'r'
       and relation.relpersistence = 'p'
       and not relation.relrowsecurity
       and not relation.relforcerowsecurity
  ) then
    raise exception 'kb_stage.kb_proposals is not the reviewed permanent base table';
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%I.%I:%s', namespace.nspname, relation.relname, relation.relkind),
           ', '
           order by namespace.nspname, relation.relname
         )
    into unexpected
    from pg_catalog.pg_class relation
    join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
   where (namespace.nspname, relation.relname) in (
     ('public', 'agents'), ('public', 'claims'), ('public', 'claim_evidence'),
     ('public', 'claim_edges'), ('public', 'sources'), ('public', 'personas'),
     ('public', 'strategies'), ('public', 'beliefs'), ('public', 'blindspots'),
     ('public', 'agent_roles'), ('public', 'peer_models'), ('public', 'behavioral_rules'),
     ('public', 'contributor_rules'), ('public', 'reasoning_tools'),
     ('public', 'governance_gates'), ('kb_stage', 'kb_proposals')
   )
     and relation.relkind <> 'r';
  if unexpected is not null then
    raise exception 'Leo read allowlist contains a non-base relation: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%s->%s', inheritance.inhparent::pg_catalog.regclass, inheritance.inhrelid::pg_catalog.regclass),
           ', '
         )
    into unexpected
    from pg_catalog.pg_inherits inheritance
   where inheritance.inhparent in (
           'public.agents'::pg_catalog.regclass, 'public.claims'::pg_catalog.regclass,
           'public.claim_evidence'::pg_catalog.regclass, 'public.claim_edges'::pg_catalog.regclass,
           'public.sources'::pg_catalog.regclass, 'public.personas'::pg_catalog.regclass,
           'public.strategies'::pg_catalog.regclass, 'public.beliefs'::pg_catalog.regclass,
           'public.blindspots'::pg_catalog.regclass, 'public.agent_roles'::pg_catalog.regclass,
           'public.peer_models'::pg_catalog.regclass, 'public.behavioral_rules'::pg_catalog.regclass,
           'public.contributor_rules'::pg_catalog.regclass, 'public.reasoning_tools'::pg_catalog.regclass,
           'public.governance_gates'::pg_catalog.regclass, 'kb_stage.kb_proposals'::pg_catalog.regclass
         )
      or inheritance.inhrelid in (
           'public.agents'::pg_catalog.regclass, 'public.claims'::pg_catalog.regclass,
           'public.claim_evidence'::pg_catalog.regclass, 'public.claim_edges'::pg_catalog.regclass,
           'public.sources'::pg_catalog.regclass, 'public.personas'::pg_catalog.regclass,
           'public.strategies'::pg_catalog.regclass, 'public.beliefs'::pg_catalog.regclass,
           'public.blindspots'::pg_catalog.regclass, 'public.agent_roles'::pg_catalog.regclass,
           'public.peer_models'::pg_catalog.regclass, 'public.behavioral_rules'::pg_catalog.regclass,
           'public.contributor_rules'::pg_catalog.regclass, 'public.reasoning_tools'::pg_catalog.regclass,
           'public.governance_gates'::pg_catalog.regclass, 'kb_stage.kb_proposals'::pg_catalog.regclass
         );
  if unexpected is not null then
    raise exception 'Leo read allowlist has inheritance edges: %', unexpected;
  end if;

  with expected(ordinal, attname, typname, not_null, has_default, collated) as (
    values
      (1, 'id', 'uuid', true, true, false),
      (2, 'proposal_type', 'text', true, false, true),
      (3, 'status', 'text', true, true, true),
      (4, 'proposed_by_handle', 'text', false, false, true),
      (5, 'proposed_by_agent_id', 'uuid', false, false, false),
      (6, 'channel', 'text', true, true, true),
      (7, 'source_ref', 'text', false, false, true),
      (8, 'rationale', 'text', true, false, true),
      (9, 'payload', 'jsonb', true, false, false),
      (10, 'reviewed_by_handle', 'text', false, false, true),
      (11, 'reviewed_by_agent_id', 'uuid', false, false, false),
      (12, 'reviewed_at', 'timestamptz', false, false, false),
      (13, 'review_note', 'text', false, false, true),
      (14, 'applied_by_handle', 'text', false, false, true),
      (15, 'applied_by_agent_id', 'uuid', false, false, false),
      (16, 'applied_at', 'timestamptz', false, false, false),
      (17, 'created_at', 'timestamptz', true, true, false),
      (18, 'updated_at', 'timestamptz', true, true, false)
  ), actual as (
    select attribute.attnum::int as ordinal,
           attribute.attname,
           type_row.typname,
           attribute.attnotnull as not_null,
           attribute.atthasdef as has_default,
           attribute.attidentity,
           attribute.attgenerated,
           attribute.atttypmod,
           type_namespace.nspname as type_namespace,
           attribute.attcollation = 'pg_catalog.default'::pg_catalog.regcollation as collated
      from pg_catalog.pg_attribute attribute
      join pg_catalog.pg_type type_row on type_row.oid = attribute.atttypid
      join pg_catalog.pg_namespace type_namespace on type_namespace.oid = type_row.typnamespace
     where attribute.attrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
       and attribute.attnum > 0
       and not attribute.attisdropped
  )
  select pg_catalog.string_agg(coalesce(expected.attname, actual.attname), ', ' order by coalesce(expected.ordinal, actual.ordinal))
    into unexpected
    from expected
    full join actual using (ordinal, attname)
   where expected.ordinal is null
      or actual.ordinal is null
      or expected.typname is distinct from actual.typname
      or expected.not_null is distinct from actual.not_null
      or expected.has_default is distinct from actual.has_default
      or expected.collated is distinct from actual.collated
      or actual.attidentity <> ''
      or actual.attgenerated <> ''
      or actual.atttypmod <> -1
      or actual.type_namespace <> 'pg_catalog';
  if unexpected is not null then
    raise exception 'kb_stage.kb_proposals column contract drifted: %', unexpected;
  end if;

  if exists (
    select 1
      from pg_catalog.pg_trigger trigger_row
     where trigger_row.tgrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
       and not trigger_row.tgisinternal
  ) or exists (
    select 1
      from pg_catalog.pg_rewrite rewrite_row
     where rewrite_row.ev_class = 'kb_stage.kb_proposals'::pg_catalog.regclass
  ) or exists (
    select 1
      from pg_catalog.pg_policy policy_row
     where policy_row.polrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
  ) or exists (
    select 1
      from pg_catalog.pg_attribute attribute
     where attribute.attrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
       and attribute.attnum > 0
       and not attribute.attisdropped
       and attribute.attgenerated <> ''
  ) then
    raise exception 'kb_stage.kb_proposals has unsafe trigger/rule/RLS/generated topology';
  end if;

  with expected(attname, expression) as (
    values
      ('id', 'gen_random_uuid()'),
      ('status', '''pending_review''::text'),
      ('channel', '''cli''::text'),
      ('created_at', 'now()'),
      ('updated_at', 'now()')
  ), actual as (
    select attribute.attname,
           pg_catalog.pg_get_expr(default_row.adbin, default_row.adrelid) as expression
      from pg_catalog.pg_attribute attribute
      join pg_catalog.pg_attrdef default_row
        on default_row.adrelid = attribute.attrelid
       and default_row.adnum = attribute.attnum
     where attribute.attrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
       and attribute.attnum > 0
       and not attribute.attisdropped
  )
  select pg_catalog.string_agg(coalesce(expected.attname, actual.attname), ', ')
    into unexpected
    from expected
    full join actual using (attname)
   where expected.expression is distinct from actual.expression;
  if unexpected is not null then
    raise exception 'kb_stage.kb_proposals default topology drifted: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%s:%s', dependency.refclassid::pg_catalog.regclass, dependency.refobjid),
           ', '
         )
    into unexpected
    from pg_catalog.pg_attrdef default_row
    join pg_catalog.pg_depend dependency
      on dependency.classid = 'pg_catalog.pg_attrdef'::pg_catalog.regclass
     and dependency.objid = default_row.oid
   where default_row.adrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
     and (
       dependency.refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass
       and dependency.refobjid not in (
         'pg_catalog.gen_random_uuid()'::pg_catalog.regprocedure,
         'pg_catalog.now()'::pg_catalog.regprocedure
       )
       or dependency.refclassid = 'pg_catalog.pg_type'::pg_catalog.regclass
          and not exists (
            select 1
              from pg_catalog.pg_type type_row
              join pg_catalog.pg_namespace namespace on namespace.oid = type_row.typnamespace
             where type_row.oid = dependency.refobjid
               and namespace.nspname = 'pg_catalog'
          )
       or dependency.refclassid = 'pg_catalog.pg_operator'::pg_catalog.regclass
          and not exists (
            select 1
              from pg_catalog.pg_operator operator_row
              join pg_catalog.pg_namespace namespace on namespace.oid = operator_row.oprnamespace
             where operator_row.oid = dependency.refobjid
               and namespace.nspname = 'pg_catalog'
          )
       or dependency.refclassid = 'pg_catalog.pg_collation'::pg_catalog.regclass
          and not exists (
            select 1
              from pg_catalog.pg_collation collation_row
              join pg_catalog.pg_namespace namespace on namespace.oid = collation_row.collnamespace
             where collation_row.oid = dependency.refobjid
               and namespace.nspname = 'pg_catalog'
          )
     );
  if unexpected is not null then
    raise exception 'kb_stage.kb_proposals defaults depend on unreviewed objects: %', unexpected;
  end if;

  if (
    select pg_catalog.count(*) <> 6
        or pg_catalog.count(*) filter (
          where (constraint_row.conname, constraint_row.contype) in (
            ('kb_proposals_pkey', 'p'),
            ('kb_proposals_proposal_type_check', 'c'),
            ('kb_proposals_status_check', 'c'),
            ('kb_proposals_applied_by_agent_id_fkey', 'f'),
            ('kb_proposals_proposed_by_agent_id_fkey', 'f'),
            ('kb_proposals_reviewed_by_agent_id_fkey', 'f')
          )
        ) <> 6
      from pg_catalog.pg_constraint constraint_row
     where constraint_row.conrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
  ) or exists (
    select 1
      from pg_catalog.pg_constraint constraint_row
      join pg_catalog.pg_depend dependency
        on dependency.classid = 'pg_catalog.pg_constraint'::pg_catalog.regclass
       and dependency.objid = constraint_row.oid
       and dependency.refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass
      join pg_catalog.pg_proc function_row on function_row.oid = dependency.refobjid
      join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
     where constraint_row.conrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
       and namespace.nspname <> 'pg_catalog'
  ) or exists (
    select 1
      from pg_catalog.pg_index index_row
     where index_row.indrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
       and (
         index_row.indexprs is not null
         or (
           index_row.indpred is not null
           and pg_catalog.pg_get_expr(index_row.indpred, index_row.indrelid)
               <> '(proposed_by_handle IS NOT NULL)'
         )
       )
  ) then
    raise exception 'kb_stage.kb_proposals constraint/index topology drifted';
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format(
             '%I:%s:%s:grantable=%s',
             database_row.datname,
             grantee.rolname,
             acl.privilege_type,
             acl.is_grantable
           ),
           ', '
           order by database_row.datname, grantee.rolname, acl.privilege_type
         )
    into unexpected
    from pg_catalog.pg_database database_row
   cross join lateral pg_catalog.aclexplode(
     coalesce(
       database_row.datacl,
       pg_catalog.acldefault('d', database_row.datdba)
     )
   ) as acl
    join pg_catalog.pg_roles grantee on grantee.oid = acl.grantee
   where grantee.rolname in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
     and not (
       database_row.datname = 'teleo_canonical'
       and grantee.rolname = 'leoclean_kb_runtime'
       and acl.privilege_type = 'CONNECT'
       and not acl.is_grantable
     );
  if unexpected is not null then
    raise exception 'scoped Leo roles retain unexpected direct database ACLs: %', unexpected;
  end if;

  select pg_catalog.string_agg(database_row.datname, ', ' order by database_row.datname)
    into unexpected
    from pg_catalog.pg_database database_row
   where database_row.datname <> 'teleo_canonical'
     and has_database_privilege('leoclean_kb_runtime', database_row.oid, 'CONNECT');
  if unexpected is not null then
    raise exception 'runtime CONNECT reaches a noncanonical database: %', unexpected;
  end if;

  select pg_catalog.string_agg(database_row.datname, ', ' order by database_row.datname)
    into unexpected
    from pg_catalog.pg_database database_row
   where has_database_privilege('leoclean_kb_stage_owner', database_row.oid, 'CONNECT');
  if unexpected is not null then
    raise exception 'stage owner CONNECT reaches a database: %', unexpected;
  end if;

  select pg_catalog.string_agg(database_row.datname, ', ' order by database_row.datname)
    into unexpected
    from pg_catalog.pg_database database_row
   cross join lateral pg_catalog.aclexplode(
     coalesce(database_row.datacl, pg_catalog.acldefault('d', database_row.datdba))
   ) acl
   where acl.grantee = 0
     and acl.privilege_type = 'CONNECT';
  if unexpected is not null then
    raise exception 'PUBLIC retains database CONNECT: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%s:%s', setting_row.setdatabase, setting_row.setconfig),
           ', '
         )
    into unexpected
    from pg_catalog.pg_db_role_setting setting_row
   where setting_row.setrole in (runtime_oid, owner_oid)
     and not (
       setting_row.setrole = runtime_oid
       and setting_row.setdatabase = (
         select database_row.oid
           from pg_catalog.pg_database database_row
          where database_row.datname = 'teleo_canonical'
       )
       and pg_catalog.cardinality(setting_row.setconfig) = 3
       and setting_row.setconfig @> array[
         'search_path=pg_catalog, public, kb_stage',
         'statement_timeout=15s',
         'lock_timeout=2s'
       ]::text[]
     );
  if unexpected is not null or not exists (
    select 1
      from pg_catalog.pg_db_role_setting setting_row
     where setting_row.setrole = runtime_oid
       and setting_row.setdatabase = (
         select database_row.oid
           from pg_catalog.pg_database database_row
          where database_row.datname = 'teleo_canonical'
       )
       and pg_catalog.cardinality(setting_row.setconfig) = 3
       and setting_row.setconfig @> array[
         'search_path=pg_catalog, public, kb_stage',
         'statement_timeout=15s',
         'lock_timeout=2s'
       ]::text[]
  ) then
    raise exception 'scoped Leo role settings differ from the reviewed contract';
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%I:%s', database_row.datname, scoped_role.name),
           ', '
           order by database_row.datname, scoped_role.name
         )
    into unexpected
    from pg_catalog.pg_database database_row
   cross join (values ('leoclean_kb_runtime'), ('leoclean_kb_stage_owner')) scoped_role(name)
   where has_database_privilege(scoped_role.name, database_row.oid, 'CREATE');
  if unexpected is not null then
    raise exception 'a scoped Leo role unexpectedly has database CREATE: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%s:%s:%s', dependency.classid::pg_catalog.regclass, dependency.objid, dependency.objsubid),
           ', '
         )
    into unexpected
    from pg_catalog.pg_shdepend dependency
   where dependency.refclassid = 'pg_catalog.pg_authid'::pg_catalog.regclass
     and dependency.refobjid = runtime_oid
     and dependency.deptype = 'o';
  if unexpected is not null then
    raise exception 'leoclean_kb_runtime unexpectedly owns database objects: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%s:%s:%s', dependency.classid::pg_catalog.regclass, dependency.objid, dependency.objsubid),
           ', '
         )
    into unexpected
    from pg_catalog.pg_shdepend dependency
   where dependency.refclassid = 'pg_catalog.pg_authid'::pg_catalog.regclass
     and dependency.refobjid = owner_oid
     and dependency.deptype = 'o'
     and not (
       dependency.dbid = (select database_row.oid from pg_catalog.pg_database database_row where database_row.datname = current_database())
       and
       dependency.classid = 'pg_catalog.pg_proc'::pg_catalog.regclass
       and dependency.objid = stage_function
       and dependency.objsubid = 0
     );
  if unexpected is not null then
    raise exception 'leoclean_kb_stage_owner owns unexpected database objects: %', unexpected;
  end if;

  select pg_catalog.string_agg(metadata.oid::text, ', ' order by metadata.oid)
    into unexpected
    from pg_catalog.pg_largeobject_metadata metadata
   where metadata.lomowner in (runtime_oid, owner_oid)
      or exists (
        select 1
          from pg_catalog.aclexplode(
            coalesce(metadata.lomacl, pg_catalog.acldefault('L', metadata.lomowner))
          ) acl
         where acl.grantee in (0, runtime_oid, owner_oid)
           and acl.privilege_type in ('SELECT', 'UPDATE')
      );
  if unexpected is not null then
    raise exception 'scoped Leo roles retain large-object ownership or ACL reachability: %', unexpected;
  end if;

  with actual as (
    select function_row.oid,
           pg_catalog.format(
             '%I(%s)',
             function_row.proname,
             pg_catalog.pg_get_function_identity_arguments(function_row.oid)
           ) as signature,
           exists (
             select 1
               from pg_catalog.aclexplode(
                 coalesce(function_row.proacl, pg_catalog.acldefault('f', function_row.proowner))
               ) acl
              where acl.grantee = 0
                and acl.privilege_type = 'EXECUTE'
           ) as public_execute,
           pg_catalog.has_function_privilege('leoclean_kb_runtime', function_row.oid, 'EXECUTE') as runtime_execute,
           pg_catalog.has_function_privilege('leoclean_kb_stage_owner', function_row.oid, 'EXECUTE') as owner_execute
      from pg_catalog.pg_proc function_row
      join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
     where namespace.nspname = 'pg_catalog'
       and function_row.proname in (
         'lo_creat', 'lo_create', 'lo_export', 'lo_from_bytea', 'lo_import',
         'lo_open', 'lo_put', 'lo_truncate', 'lo_truncate64', 'lo_unlink', 'lowrite'
       )
  )
  select pg_catalog.string_agg(
           coalesce(expected.signature, actual.signature),
           ', '
           order by coalesce(expected.signature, actual.signature)
         )
    into unexpected
    from pg_temp.leoclean_expected_lo_routine_residual expected
    full join actual using (signature)
   where expected.signature is null
      or actual.signature is null
      or actual.public_execute is distinct from expected.public_execute
      or actual.runtime_execute is distinct from expected.public_execute
      or actual.owner_execute is distinct from expected.public_execute;
  if unexpected is not null then
    raise exception 'provider-owned large-object routine residual drifted: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format(
             '%I(%s):%s',
             function_row.proname,
             pg_catalog.pg_get_function_identity_arguments(function_row.oid),
             role_row.rolname
           ),
           ', '
           order by function_row.proname, role_row.rolname
         )
    into unexpected
    from pg_catalog.pg_proc function_row
    join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
   cross join lateral pg_catalog.aclexplode(function_row.proacl) acl
    join pg_catalog.pg_roles role_row on role_row.oid = acl.grantee
   where namespace.nspname = 'pg_catalog'
     and function_row.proname in (
       'lo_creat', 'lo_create', 'lo_export', 'lo_from_bytea', 'lo_import',
       'lo_open', 'lo_put', 'lo_truncate', 'lo_truncate64', 'lo_unlink', 'lowrite'
     )
     and role_row.rolname in ('leoclean_kb_runtime', 'leoclean_kb_stage_owner')
     and acl.privilege_type = 'EXECUTE';
  if unexpected is not null then
    raise exception 'scoped Leo roles have direct large-object routine ACLs: %', unexpected;
  end if;

  if pg_catalog.current_setting('lo_compat_privileges') <> 'off' then
    raise exception 'lo_compat_privileges must remain off for scoped large-object ACL enforcement';
  end if;

  select pg_catalog.string_agg(parameter_acl.parname, ', ' order by parameter_acl.parname)
    into unexpected
    from pg_catalog.pg_parameter_acl parameter_acl
   where has_parameter_privilege('leoclean_kb_runtime', parameter_acl.parname, 'SET')
      or has_parameter_privilege('leoclean_kb_runtime', parameter_acl.parname, 'ALTER SYSTEM')
      or has_parameter_privilege('leoclean_kb_stage_owner', parameter_acl.parname, 'SET')
      or has_parameter_privilege('leoclean_kb_stage_owner', parameter_acl.parname, 'ALTER SYSTEM');
  if unexpected is not null then
    raise exception 'scoped Leo roles retain parameter privileges: %', unexpected;
  end if;

  if has_schema_privilege('leoclean_kb_runtime', 'public', 'CREATE')
     or has_schema_privilege('leoclean_kb_runtime', 'kb_stage', 'CREATE')
     or has_schema_privilege('leoclean_kb_stage_owner', 'public', 'CREATE')
     or has_schema_privilege('leoclean_kb_stage_owner', 'kb_stage', 'CREATE') then
    raise exception 'a scoped Leo role unexpectedly has schema CREATE';
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%I.%I:%s', namespace.nspname, relation.relname, privilege.name),
           ', '
           order by namespace.nspname, relation.relname, privilege.name
         )
    into unexpected
    from pg_catalog.pg_class relation
    join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
   cross join (values ('INSERT'), ('UPDATE'), ('DELETE'), ('TRUNCATE'), ('REFERENCES'), ('TRIGGER')) privilege(name)
   where namespace.nspname in ('public', 'kb_stage')
     and relation.relkind in ('r', 'p', 'v', 'm', 'f')
     and has_table_privilege('leoclean_kb_runtime', relation.oid, privilege.name);
  if unexpected is not null then
    raise exception 'leoclean_kb_runtime unexpectedly has table-level DML: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%I.%I.%I:%s', namespace.nspname, relation.relname, attribute.attname, privilege.name),
           ', '
           order by namespace.nspname, relation.relname, attribute.attnum, privilege.name
         )
    into unexpected
    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
   cross join (values ('INSERT'), ('UPDATE'), ('REFERENCES')) privilege(name)
   where namespace.nspname in ('public', 'kb_stage')
     and relation.relkind in ('r', 'p', 'v', 'm', 'f')
     and attribute.attnum > 0
     and not attribute.attisdropped
     and has_column_privilege('leoclean_kb_runtime', relation.oid, attribute.attnum, privilege.name);
  if unexpected is not null then
    raise exception 'leoclean_kb_runtime unexpectedly has column-level DML: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%I.%I', namespace.nspname, relation.relname),
           ', '
           order by namespace.nspname, relation.relname
         )
    into unexpected
    from pg_catalog.pg_class relation
    join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
   where namespace.nspname in ('public', 'kb_stage')
     and relation.relkind in ('r', 'p', 'v', 'm', 'f')
     and has_table_privilege('leoclean_kb_runtime', relation.oid, 'SELECT');
  if unexpected is not null then
    raise exception 'leoclean_kb_runtime unexpectedly has table-level SELECT: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%I.%I.%I', allowlist.schema_name, allowlist.relation_name, allowlist.column_name),
           ', '
           order by allowlist.schema_name, allowlist.relation_name, allowlist.column_ordinal
         )
    into unexpected
    from pg_temp.leoclean_runtime_read_column_allowlist allowlist
    left join pg_catalog.pg_namespace namespace
      on namespace.nspname = allowlist.schema_name
    left join pg_catalog.pg_class relation
      on relation.relnamespace = namespace.oid
     and relation.relname = allowlist.relation_name
    left join pg_catalog.pg_attribute attribute
      on attribute.attrelid = relation.oid
     and attribute.attname = allowlist.column_name
     and attribute.attnum > 0
     and not attribute.attisdropped
   where attribute.attnum is null
      or not coalesce(
        has_column_privilege('leoclean_kb_runtime', relation.oid, attribute.attnum, 'SELECT'),
        false
      );
  if unexpected is not null then
    raise exception 'leoclean_kb_runtime is missing required column SELECT: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%I.%I.%I', namespace.nspname, relation.relname, attribute.attname),
           ', '
           order by namespace.nspname, relation.relname, attribute.attnum
         )
    into unexpected
    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
    left join pg_temp.leoclean_runtime_read_column_allowlist allowlist
      on allowlist.schema_name = namespace.nspname
     and allowlist.relation_name = relation.relname
     and allowlist.column_name = attribute.attname
   where namespace.nspname in ('public', 'kb_stage')
     and relation.relkind in ('r', 'p', 'v', 'm', 'f')
     and attribute.attnum > 0
     and not attribute.attisdropped
     and has_column_privilege('leoclean_kb_runtime', relation.oid, attribute.attnum, 'SELECT')
     and allowlist.column_name is null;
  if unexpected is not null then
    raise exception 'leoclean_kb_runtime has column SELECT outside its allowlist: %', unexpected;
  end if;

  if has_table_privilege('leoclean_kb_stage_owner', 'public.agents', 'SELECT')
     or not has_column_privilege('leoclean_kb_stage_owner', 'public.agents', 'id', 'SELECT')
     or not has_column_privilege('leoclean_kb_stage_owner', 'public.agents', 'handle', 'SELECT')
     or not has_table_privilege('leoclean_kb_stage_owner', 'kb_stage.kb_proposals', 'SELECT')
     or has_table_privilege('leoclean_kb_stage_owner', 'kb_stage.kb_proposals', 'INSERT') then
    raise exception 'leoclean_kb_stage_owner does not have the exact read/insert grant shape';
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%I.%I.%I', namespace.nspname, relation.relname, attribute.attname),
           ', '
           order by namespace.nspname, relation.relname, attribute.attnum
         )
    into unexpected
    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 in ('public', 'kb_stage')
     and relation.relkind in ('r', 'p', 'v', 'm', 'f')
     and attribute.attnum > 0
     and not attribute.attisdropped
     and has_column_privilege('leoclean_kb_stage_owner', relation.oid, attribute.attnum, 'INSERT')
     and not (
       namespace.nspname = 'kb_stage'
       and relation.relname = 'kb_proposals'
       and attribute.attname in (
         'proposal_type',
         'status',
         'proposed_by_handle',
         'proposed_by_agent_id',
         'channel',
         'source_ref',
         'rationale',
         'payload'
       )
     );
  if unexpected is not null then
    raise exception 'leoclean_kb_stage_owner can INSERT unexpected columns: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%I.%I:%s', namespace.nspname, relation.relname, privilege.name),
           ', '
           order by namespace.nspname, relation.relname, privilege.name
         )
    into unexpected
    from pg_catalog.pg_class relation
    join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
   cross join (values ('INSERT'), ('UPDATE'), ('DELETE'), ('TRUNCATE'), ('REFERENCES'), ('TRIGGER')) privilege(name)
   where namespace.nspname in ('public', 'kb_stage')
     and relation.relkind in ('r', 'p', 'v', 'm', 'f')
     and has_table_privilege('leoclean_kb_stage_owner', relation.oid, privilege.name);
  if unexpected is not null then
    raise exception 'leoclean_kb_stage_owner unexpectedly has table-level DML: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%I.%I.%I:%s', namespace.nspname, relation.relname, attribute.attname, privilege.name),
           ', '
           order by namespace.nspname, relation.relname, attribute.attnum, privilege.name
         )
    into unexpected
    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
   cross join (values ('UPDATE'), ('REFERENCES')) privilege(name)
   where namespace.nspname in ('public', 'kb_stage')
     and relation.relkind in ('r', 'p', 'v', 'm', 'f')
     and attribute.attnum > 0
     and not attribute.attisdropped
     and has_column_privilege('leoclean_kb_stage_owner', relation.oid, attribute.attnum, privilege.name);
  if unexpected is not null then
    raise exception 'leoclean_kb_stage_owner unexpectedly has column-level DML: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%I.%I.%I', namespace.nspname, relation.relname, attribute.attname),
           ', '
           order by namespace.nspname, relation.relname, attribute.attnum
         )
    into unexpected
    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 in ('public', 'kb_stage')
     and relation.relkind in ('r', 'p', 'v', 'm', 'f')
     and attribute.attnum > 0
     and not attribute.attisdropped
     and has_column_privilege('leoclean_kb_stage_owner', relation.oid, attribute.attnum, 'SELECT')
     and not (
       (namespace.nspname = 'kb_stage' and relation.relname = 'kb_proposals')
       or (
         namespace.nspname = 'public'
         and relation.relname = 'agents'
         and attribute.attname in ('id', 'handle')
       )
     );
  if unexpected is not null then
    raise exception 'leoclean_kb_stage_owner can SELECT unexpected columns: %', unexpected;
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%I.%I:%s:%s', namespace.nspname, relation.relname, scoped_role.name, privilege.name),
           ', '
           order by namespace.nspname, relation.relname, scoped_role.name, privilege.name
         )
    into unexpected
    from pg_catalog.pg_class relation
    join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace
   cross join (values ('leoclean_kb_runtime'), ('leoclean_kb_stage_owner')) scoped_role(name)
   cross join (values ('USAGE'), ('SELECT'), ('UPDATE')) privilege(name)
   where namespace.nspname in ('public', 'kb_stage')
     and relation.relkind = 'S'
     and has_sequence_privilege(scoped_role.name, relation.oid, privilege.name);
  if unexpected is not null then
    raise exception 'a scoped Leo role unexpectedly has sequence privileges: %', unexpected;
  end if;

  if exists (
    select 1
      from pg_catalog.pg_attribute attribute
     where attribute.attrelid = 'kb_stage.kb_proposals'::pg_catalog.regclass
       and attribute.attname in (
         'proposal_type',
         'status',
         'proposed_by_handle',
         'proposed_by_agent_id',
         'channel',
         'source_ref',
         'rationale',
         'payload'
       )
       and not has_column_privilege(
         'leoclean_kb_stage_owner',
         attribute.attrelid,
         attribute.attnum,
         'INSERT'
       )
  ) then
    raise exception 'leoclean_kb_stage_owner is missing a required INSERT column grant';
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format('%I.%I(%s)', namespace.nspname, function_row.proname, pg_catalog.pg_get_function_identity_arguments(function_row.oid)),
           ', '
         )
    into unexpected
    from pg_catalog.pg_proc function_row
    join pg_catalog.pg_namespace namespace on namespace.oid = function_row.pronamespace
   where namespace.nspname in ('public', 'kb_stage')
     and function_row.prosecdef
     and function_row.oid <> stage_function
     and has_function_privilege('leoclean_kb_runtime', function_row.oid, 'EXECUTE');
  if unexpected is not null then
    raise exception 'leoclean_kb_runtime can execute unexpected SECURITY DEFINER functions: %', unexpected;
  end if;

  if not exists (
    select 1
      from pg_catalog.pg_proc function_row
     where function_row.oid = stage_function
       and function_row.prosecdef
       and function_row.proowner = owner_oid
       and function_row.proconfig = array['search_path=pg_catalog, pg_temp']::text[]
  ) then
    raise exception 'stage_leoclean_proposal owner/security/search_path contract is invalid';
  end if;

  select pg_catalog.string_agg(
           pg_catalog.format(
             'grantee=%s privilege=%s grantable=%s',
             acl.grantee,
             acl.privilege_type,
             acl.is_grantable
           ),
           ', '
         )
    into unexpected
    from pg_catalog.pg_proc function_row
   cross join lateral pg_catalog.aclexplode(
     coalesce(
       function_row.proacl,
       pg_catalog.acldefault('f', function_row.proowner)
     )
   ) as acl
   where function_row.oid = stage_function
     and not (
       acl.grantee = runtime_oid
       and acl.privilege_type = 'EXECUTE'
       and not acl.is_grantable
     );
  if unexpected is not null then
    raise exception 'stage_leoclean_proposal has unexpected ACL entries: %', unexpected;
  end if;

  if not has_function_privilege(
    'leoclean_kb_runtime',
    'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)',
    'EXECUTE'
  ) then
    raise exception 'leoclean_kb_runtime cannot execute stage_leoclean_proposal';
  end if;

  if has_function_privilege(
    'leoclean_kb_stage_owner',
    'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)',
    'EXECUTE'
  ) then
    raise exception 'leoclean_kb_stage_owner must not execute stage_leoclean_proposal';
  end if;
end
$verification$;

-- LOGIN is the last state change in the same transaction as every database ACL,
-- canonical privilege, large-object ownership/residual, and topology
-- assertion. Any failure rolls back the complete privilege migration and
-- leaves the role NOLOGIN.
alter role leoclean_kb_runtime
  with login nocreatedb nocreaterole noinherit connection limit 8;

do $login_verification$
begin
  if exists (
    select 1
      from pg_catalog.pg_roles role_row
     where role_row.rolname = 'leoclean_kb_runtime'
       and (
         not role_row.rolcanlogin
         or role_row.rolsuper
         or role_row.rolcreatedb
         or role_row.rolcreaterole
         or role_row.rolinherit
         or role_row.rolreplication
         or role_row.rolbypassrls
         or role_row.rolconnlimit <> 8
       )
  ) then
    raise exception 'leoclean_kb_runtime final login attributes are unsafe';
  end if;
end
$login_verification$;

commit;
