\set ON_ERROR_STOP on
\getenv runtime_password TELEO_LEOCLEAN_DB_PASSWORD

-- One-time GCP staging provisioning for Leo's Cloud SQL identity. The password
-- is supplied only through the process environment and is never stored here.
--
-- 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.
select 'create role leoclean_kb_runtime login 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

select format('alter role leoclean_kb_runtime password %L', :'runtime_password')
\gexec

alter role leoclean_kb_runtime
  with login nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit 8;
alter role leoclean_kb_stage_owner
  with nologin nosuperuser nocreatedb nocreaterole noinherit noreplication nobypassrls connection limit -1
  password null;

alter role leoclean_kb_runtime reset all;
alter role leoclean_kb_stage_owner reset all;

-- NOINHERIT does not prevent SET ROLE. Remove every membership edge in either
-- direction so neither scoped principal can reach, or be reached through, a
-- broader role left behind by an earlier deployment.
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

revoke all privileges on database teleo_canonical from leoclean_kb_runtime;
revoke all privileges on database teleo_canonical from leoclean_kb_stage_owner;
revoke all privileges on database teleo_kb from leoclean_kb_runtime;
revoke all privileges on database teleo_kb from leoclean_kb_stage_owner;
grant connect on database teleo_canonical to leoclean_kb_runtime;

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

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

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

grant select on
  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
to leoclean_kb_runtime;

-- 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.
revoke all on function kb_stage.stage_leoclean_proposal(text, text, text, text, jsonb)
  from public;
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
   and grantee.rolname <> 'leoclean_kb_stage_owner'
\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 (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 has unsafe 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;

  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 database_row.datname in ('teleo_canonical', 'teleo_kb')
     and 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;

  if has_database_privilege('leoclean_kb_runtime', 'teleo_canonical', 'CREATE')
     or has_database_privilege('leoclean_kb_runtime', 'teleo_kb', 'CREATE')
     or has_database_privilege('leoclean_kb_stage_owner', 'teleo_canonical', 'CREATE')
     or has_database_privilege('leoclean_kb_stage_owner', 'teleo_kb', 'CREATE') then
    raise exception 'a scoped Leo role unexpectedly has database CREATE';
  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;

  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')
     and (namespace.nspname, relation.relname) not 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')
     );
  if unexpected is not null then
    raise exception 'leoclean_kb_runtime can SELECT outside its table allowlist: %', 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_runtime', relation.oid, attribute.attnum, 'SELECT')
     and (namespace.nspname, relation.relname) not 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')
     );
  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 in (runtime_oid, owner_oid)
       and acl.privilege_type = 'EXECUTE'
       and (acl.grantee = owner_oid or 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;
end
$verification$;

commit;

-- The legacy restore database is not an audit fallback for the scoped runtime.
-- Strip direct ACLs from both scoped roles and fail if PUBLIC/group grants still
-- make any teleo_restore relation reachable.
\connect teleo_kb

begin;

select format('revoke all on schema %I from leoclean_kb_runtime, leoclean_kb_stage_owner', namespace.nspname)
  from pg_catalog.pg_namespace namespace
 where namespace.nspname = 'teleo_restore'
\gexec

select format('revoke all privileges on all tables in schema %I from leoclean_kb_runtime, leoclean_kb_stage_owner', namespace.nspname)
  from pg_catalog.pg_namespace namespace
 where namespace.nspname = 'teleo_restore'
\gexec

select format('revoke all privileges on all sequences in schema %I from leoclean_kb_runtime, leoclean_kb_stage_owner', namespace.nspname)
  from pg_catalog.pg_namespace namespace
 where namespace.nspname = 'teleo_restore'
\gexec

select format('revoke all privileges on all functions in schema %I from leoclean_kb_runtime, leoclean_kb_stage_owner', namespace.nspname)
  from pg_catalog.pg_namespace namespace
 where namespace.nspname = 'teleo_restore'
\gexec

select format('revoke all privileges on all procedures in schema %I from leoclean_kb_runtime, leoclean_kb_stage_owner', namespace.nspname)
  from pg_catalog.pg_namespace namespace
 where namespace.nspname = 'teleo_restore'
\gexec

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 = 'teleo_restore'
   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

do $audit_verification$
declare
  unexpected text;
  scoped_role text;
begin
  foreach scoped_role in array array['leoclean_kb_runtime', 'leoclean_kb_stage_owner'] loop
    if to_regnamespace('teleo_restore') is not null
       and (has_schema_privilege(scoped_role, 'teleo_restore', 'USAGE')
            or has_schema_privilege(scoped_role, 'teleo_restore', 'CREATE')) then
      raise exception '% unexpectedly has teleo_restore schema access', scoped_role;
    end if;

    select pg_catalog.string_agg(
             pg_catalog.format('%I.%I:%s', namespace.nspname, relation.relname, privilege.name),
             ', '
             order by 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 ('SELECT'), ('INSERT'), ('UPDATE'), ('DELETE'), ('TRUNCATE'), ('REFERENCES'), ('TRIGGER')
     ) privilege(name)
     where namespace.nspname = 'teleo_restore'
       and relation.relkind in ('r', 'p', 'v', 'm', 'f')
       and has_table_privilege(scoped_role, relation.oid, privilege.name);
    if unexpected is not null then
      raise exception '% unexpectedly has teleo_restore table access: %', scoped_role, 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 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 ('SELECT'), ('INSERT'), ('UPDATE'), ('REFERENCES')) privilege(name)
     where namespace.nspname = 'teleo_restore'
       and relation.relkind in ('r', 'p', 'v', 'm', 'f')
       and attribute.attnum > 0
       and not attribute.attisdropped
       and has_column_privilege(scoped_role, relation.oid, attribute.attnum, privilege.name);
    if unexpected is not null then
      raise exception '% unexpectedly has teleo_restore column access: %', scoped_role, unexpected;
    end if;

    select pg_catalog.string_agg(
             pg_catalog.format('%I.%I:%s', namespace.nspname, relation.relname, privilege.name),
             ', '
             order by 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 ('USAGE'), ('SELECT'), ('UPDATE')) privilege(name)
     where namespace.nspname = 'teleo_restore'
       and relation.relkind = 'S'
       and has_sequence_privilege(scoped_role, relation.oid, privilege.name);
    if unexpected is not null then
      raise exception '% unexpectedly has teleo_restore sequence access: %', scoped_role, unexpected;
    end if;
  end loop;
end
$audit_verification$;

commit;

\connect teleo_canonical

select jsonb_build_object(
  'role', rolname,
  'superuser', rolsuper,
  'create_db', rolcreatedb,
  'create_role', rolcreaterole,
  'inherit', rolinherit,
  'replication', rolreplication,
  'bypass_rls', rolbypassrls,
  'has_role_memberships', exists (
    select 1
      from pg_catalog.pg_auth_members membership
     where membership.roleid = pg_catalog.pg_roles.oid
        or membership.member = pg_catalog.pg_roles.oid
  ),
  'can_select_claims', has_table_privilege(rolname, 'public.claims', 'SELECT'),
  'can_select_proposals', has_table_privilege(rolname, 'kb_stage.kb_proposals', 'SELECT'),
  'can_insert_proposals_directly', has_table_privilege(rolname, 'kb_stage.kb_proposals', 'INSERT'),
  'can_create_database_objects', has_database_privilege(rolname, 'teleo_canonical', 'CREATE'),
  'can_create_temp_objects', has_database_privilege(rolname, 'teleo_canonical', 'TEMP'),
  'can_stage_proposal', has_function_privilege(
    rolname,
    'kb_stage.stage_leoclean_proposal(text,text,text,text,jsonb)',
    'EXECUTE'
  )
)::text
from pg_catalog.pg_roles
where rolname = 'leoclean_kb_runtime';
