from __future__ import annotations import json import os import shutil import subprocess import time import uuid from collections.abc import Iterator from contextlib import contextmanager from pathlib import Path import pytest from tests.docker_volume_lifecycle import ( POSTGRES_TMPFS_SPEC, acquire_docker_volume_set_guard, inspect_postgres_tmpfs_container, release_docker_volume_set_guard, remove_container_and_volumes, ) REPO_ROOT = Path(__file__).resolve().parents[1] MIGRATION = REPO_ROOT / "scripts" / "kb_apply_prereqs.sql" POSTGRES_IMAGE = os.environ.get("LEOCLEAN_RUNTIME_POSTGRES_IMAGE", "postgres:16-alpine") DATABASE = "teleo_clone" LEGACY_APPROVED_ID = "aaaaaaaa-0000-4000-8000-000000000001" LEGACY_APPLIED_ID = "aaaaaaaa-0000-4000-8000-000000000002" FRESH_PROPOSAL_ID = "aaaaaaaa-0000-4000-8000-000000000003" TIMEZONE_PROPOSAL_ID = "aaaaaaaa-0000-4000-8000-000000000004" REVIEWER_ID = "11111111-1111-1111-1111-111111111111" STALE_REVIEWER_ID = "22222222-2222-2222-2222-222222222222" LEGACY_REVIEWER_HANDLE = "m3ta" CANONICAL_REVIEWER_HANDLE = "m3taversal" BOOTSTRAP_SQL = r""" create role kb_gate_owner nologin inherit; create role kb_review login inherit; create role kb_apply login inherit; create role broad_writer nologin; create role rogue_owner nologin; create role rogue_grantee nologin; create schema kb_stage; create table public.agents ( id uuid primary key, handle text not null unique, kind text not null ); create table public.strategies ( id uuid primary key, agent_id uuid not null, active boolean not null default true ); create table public.strategy_nodes (id uuid primary key); create table public.claim_evidence ( id uuid primary key, claim_id uuid not null, source_id uuid not null, role text not null ); create table public.claim_edges (id uuid primary key); create table public.claims (id uuid primary key); create table public.sources ( id uuid primary key, hash text not null ); create table public.reasoning_tools (id uuid primary key); insert into public.agents (id, handle, kind) values ('11111111-1111-1111-1111-111111111111', 'm3ta', 'human'); create table kb_stage.kb_proposals ( id uuid primary key, proposal_type text not null, status text not null, payload jsonb not null, reviewed_by_handle text, reviewed_by_agent_id uuid, reviewed_at timestamptz, review_note text, applied_by_handle text, applied_by_agent_id uuid, applied_at timestamptz, updated_at timestamptz not null default now() ); create table kb_stage.kb_review_principals ( db_role name primary key, reviewed_by_handle text not null, reviewed_by_agent_id uuid not null references public.agents(id), active boolean not null default true, created_at timestamptz not null default now() ); create table kb_stage.kb_proposal_approvals ( proposal_id uuid primary key references kb_stage.kb_proposals(id), proposal_type text not null, payload jsonb not null, reviewed_by_handle text not null, reviewed_by_agent_id uuid not null references public.agents(id), reviewed_by_db_role name not null, reviewed_at timestamptz not null, review_note text not null ); insert into kb_stage.kb_proposals ( id, proposal_type, status, payload, reviewed_by_handle, reviewed_by_agent_id, reviewed_at, review_note, applied_by_handle, applied_by_agent_id, applied_at, updated_at ) values ( 'aaaaaaaa-0000-4000-8000-000000000001', 'add_edge', 'approved', '{"apply_payload":{"from_claim":"aaaaaaaa-1000-4000-8000-000000000001"}}'::jsonb, 'm3ta', '11111111-1111-1111-1111-111111111111', '2026-07-18T10:00:00Z', 'Exact still-approved legacy row.', null, null, null, '2026-07-18T10:00:01Z' ), ( 'aaaaaaaa-0000-4000-8000-000000000002', 'add_edge', 'applied', '{"apply_payload":{"from_claim":"aaaaaaaa-1000-4000-8000-000000000002"}}'::jsonb, 'm3ta', '11111111-1111-1111-1111-111111111111', '2026-07-18T09:00:00Z', 'Applied before exact approved rows were retained.', 'kb-apply', '44444444-4444-4444-4444-444444444444', '2026-07-18T09:01:00Z', '2026-07-18T09:01:00Z' ); insert into kb_stage.kb_proposal_approvals ( proposal_id, proposal_type, payload, reviewed_by_handle, reviewed_by_agent_id, reviewed_by_db_role, reviewed_at, review_note ) values ( 'aaaaaaaa-0000-4000-8000-000000000001', 'add_edge', '{"apply_payload":{"from_claim":"aaaaaaaa-1000-4000-8000-000000000001"}}'::jsonb, 'm3ta', '11111111-1111-1111-1111-111111111111', 'kb_review', '2026-07-18T10:00:00Z', 'Exact still-approved legacy row.' ), ( 'aaaaaaaa-0000-4000-8000-000000000002', 'add_edge', '{"apply_payload":{"from_claim":"aaaaaaaa-1000-4000-8000-000000000002"}}'::jsonb, 'm3ta', '11111111-1111-1111-1111-111111111111', 'kb_review', '2026-07-18T09:00:00Z', 'Applied before exact approved rows were retained.' ); create function kb_stage.approve_strict_proposal(uuid, text, text) returns jsonb language sql security definer as $function$ select '{}'::jsonb $function$; create function kb_stage.approve_strict_proposal(uuid, text, jsonb, text, text) returns jsonb language sql security definer as $function$ select '{}'::jsonb $function$; create function kb_stage.assert_approved_proposal( uuid, text, jsonb, text, uuid, timestamptz, text ) returns void language sql security definer as $function$ select $function$; create function kb_stage.finish_approved_proposal( uuid, text, jsonb, text, uuid, timestamptz, text, text ) returns jsonb language sql security definer as $function$ select '{}'::jsonb $function$; grant all privileges on all tables in schema public to kb_gate_owner, kb_apply, kb_review; grant all privileges on all tables in schema kb_stage to public, kb_gate_owner, kb_apply, kb_review; grant all privileges on all functions in schema kb_stage to public, kb_gate_owner, kb_apply, kb_review; grant update (review_note) on kb_stage.kb_proposals to kb_apply; grant select (review_note) on kb_stage.kb_proposals to public; """ def _psql(container: str, sql: str, *, check: bool = True) -> subprocess.CompletedProcess[str]: completed = subprocess.run( [ "docker", "exec", "-i", container, "psql", "-X", "-v", "ON_ERROR_STOP=1", "-U", "postgres", "-d", DATABASE, "-At", "-F", "|", ], input=sql, text=True, capture_output=True, check=False, ) if check and completed.returncode != 0: raise AssertionError( f"psql failed with exit {completed.returncode}\nstdout:\n{completed.stdout}\nstderr:\n{completed.stderr}" ) return completed def _apply_migration(container: str, *, check: bool = True) -> subprocess.CompletedProcess[str]: return _psql(container, MIGRATION.read_text(), check=check) @contextmanager def _postgres_clone() -> Iterator[str]: if shutil.which("docker") is None: pytest.skip("Docker is required for the disposable PostgreSQL migration canary") volume_guard = acquire_docker_volume_set_guard( context="kb_apply_prereqs PostgreSQL fixture", ) container = f"kb-apply-prereqs-{uuid.uuid4().hex[:12]}" try: started = subprocess.run( [ "docker", "run", "--pull=never", "-d", "--name", container, "--tmpfs", POSTGRES_TMPFS_SPEC, "-e", "POSTGRES_PASSWORD=postgres", "-e", f"POSTGRES_DB={DATABASE}", POSTGRES_IMAGE, ], text=True, capture_output=True, check=False, ) if started.returncode != 0: pytest.fail(f"could not start disposable PostgreSQL: {started.stderr}") inspect_postgres_tmpfs_container(container) for _ in range(120): logs = subprocess.run( ["docker", "logs", container], text=True, capture_output=True, check=False, ) query = _psql(container, "select 1;", check=False) ready_transitions = (logs.stdout + logs.stderr).count("database system is ready to accept connections") if ready_transitions >= 2 and query.returncode == 0: break time.sleep(0.25) else: pytest.fail("disposable PostgreSQL did not become ready") _psql(container, BOOTSTRAP_SQL) yield container finally: try: remove_container_and_volumes(container) finally: release_docker_volume_set_guard(volume_guard) @pytest.fixture(scope="module") def migrated_postgres() -> Iterator[str]: with _postgres_clone() as container: _apply_migration(container) yield container def _lines(completed: subprocess.CompletedProcess[str]) -> set[str]: return {line for line in completed.stdout.splitlines() if line} def test_legacy_only_reviewer_is_canonicalized_without_rewriting_history( migrated_postgres: str, ) -> None: identity_rows = _psql( migrated_postgres, f""" select id::text || '|' || handle || '|' || kind from public.agents where handle in ('{LEGACY_REVIEWER_HANDLE}', '{CANONICAL_REVIEWER_HANDLE}') order by handle; """, ).stdout.splitlines() assert identity_rows == [f"{REVIEWER_ID}|{CANONICAL_REVIEWER_HANDLE}|human"] historical_rows = _psql( migrated_postgres, f""" select 'proposal|' || id::text || '|' || reviewed_by_handle || '|' || reviewed_by_agent_id::text from kb_stage.kb_proposals where id in ('{LEGACY_APPROVED_ID}'::uuid, '{LEGACY_APPLIED_ID}'::uuid) order by id; select 'approval|' || proposal_id::text || '|' || reviewed_by_handle || '|' || reviewed_by_agent_id::text from kb_stage.kb_proposal_approvals where proposal_id in ('{LEGACY_APPROVED_ID}'::uuid, '{LEGACY_APPLIED_ID}'::uuid) order by proposal_id; """, ).stdout.splitlines() assert historical_rows == [ f"proposal|{LEGACY_APPROVED_ID}|{LEGACY_REVIEWER_HANDLE}|{REVIEWER_ID}", f"proposal|{LEGACY_APPLIED_ID}|{LEGACY_REVIEWER_HANDLE}|{REVIEWER_ID}", f"approval|{LEGACY_APPROVED_ID}|{LEGACY_REVIEWER_HANDLE}|{REVIEWER_ID}", f"approval|{LEGACY_APPLIED_ID}|{LEGACY_REVIEWER_HANDLE}|{REVIEWER_ID}", ] def test_already_current_reviewer_migration_is_idempotent() -> None: with _postgres_clone() as container: _psql( container, f"update public.agents set handle = '{CANONICAL_REVIEWER_HANDLE}' " f"where handle = '{LEGACY_REVIEWER_HANDLE}';", ) _apply_migration(container) state_sql = f""" select principal.reviewed_by_handle || '|' || principal.reviewed_by_agent_id::text || '|' || principal.active::text || '|' || principal.created_at::text || '|' || (select count(*) from public.agents where handle = '{LEGACY_REVIEWER_HANDLE}')::text from kb_stage.kb_review_principals principal where principal.db_role = 'kb_review'; """ first_state = _psql(container, state_sql).stdout.strip() _apply_migration(container) second_state = _psql(container, state_sql).stdout.strip() expected_state = f"{CANONICAL_REVIEWER_HANDLE}|{REVIEWER_ID}|true|1970-01-01 00:00:00+00|0" assert first_state == expected_state assert second_state == first_state def test_stale_reviewer_principal_is_repaired_without_resetting_created_at() -> None: with _postgres_clone() as container: _psql( container, f""" insert into public.agents (id, handle, kind) values ('{STALE_REVIEWER_ID}', 'stale-reviewer', 'human'); insert into kb_stage.kb_review_principals (db_role, reviewed_by_handle, reviewed_by_agent_id, active, created_at) values ( 'kb_review', 'stale-reviewer', '{STALE_REVIEWER_ID}', false, '2001-02-03T04:05:06Z' ); """, ) _apply_migration(container) principal_state = _psql( container, """ select reviewed_by_handle || '|' || reviewed_by_agent_id::text || '|' || active::text || '|' || created_at::text from kb_stage.kb_review_principals where db_role = 'kb_review'; """, ).stdout.strip() expected_state = f"{CANONICAL_REVIEWER_HANDLE}|{REVIEWER_ID}|true|2001-02-03 04:05:06+00" assert principal_state == expected_state def test_migration_refuses_distinct_legacy_and_canonical_reviewers() -> None: with _postgres_clone() as container: _psql( container, f""" insert into public.agents (id, handle, kind) values ('{STALE_REVIEWER_ID}', '{CANONICAL_REVIEWER_HANDLE}', 'human'); """, ) refused = _apply_migration(container, check=False) identity_rows = _psql( container, f""" select handle || '|' || id::text from public.agents where handle in ('{LEGACY_REVIEWER_HANDLE}', '{CANONICAL_REVIEWER_HANDLE}') order by handle; """, ).stdout.splitlines() assert refused.returncode != 0 assert "both reviewer handles m3ta" in refused.stderr assert "exist with different agent UUIDs" in refused.stderr assert identity_rows == [ f"{LEGACY_REVIEWER_HANDLE}|{REVIEWER_ID}", f"{CANONICAL_REVIEWER_HANDLE}|{STALE_REVIEWER_ID}", ] def test_migration_refuses_to_fabricate_missing_human_reviewer() -> None: with _postgres_clone() as container: _psql( container, f""" delete from kb_stage.kb_proposal_approvals; delete from public.agents where handle = '{LEGACY_REVIEWER_HANDLE}'; """, ) refused = _apply_migration(container, check=False) remaining_agents = _psql(container, "select count(*) from public.agents;").stdout.strip() assert refused.returncode != 0 assert "required human reviewer agent is missing" in refused.stderr assert "handle m3ta or m3taversal" in refused.stderr assert remaining_agents == "0" def test_reviewer_principal_seed_timestamp_is_deterministic(migrated_postgres: str) -> None: created_at = _psql( migrated_postgres, "select created_at::text from kb_stage.kb_review_principals where db_role = 'kb_review';", ).stdout.strip() assert created_at == "1970-01-01 00:00:00+00" def test_upgrade_backfills_only_still_exact_approved_rows(migrated_postgres: str) -> None: snapshot_state = _psql( migrated_postgres, f""" select proposal_id::text || '|' || (approved_proposal_snapshot is not null)::text || '|' || coalesce(approved_proposal_snapshot->>'status', 'unavailable') || '|' || coalesce(approved_proposal_snapshot->>'reviewed_by_handle', 'unavailable') from kb_stage.kb_proposal_approvals where proposal_id in ('{LEGACY_APPROVED_ID}'::uuid, '{LEGACY_APPLIED_ID}'::uuid) order by proposal_id; """, ).stdout.splitlines() assert snapshot_state == [ f"{LEGACY_APPROVED_ID}|true|approved|{LEGACY_REVIEWER_HANDLE}", f"{LEGACY_APPLIED_ID}|false|unavailable|unavailable", ] unavailable = _psql( migrated_postgres, f""" set session authorization kb_apply; select kb_stage.export_applied_proposal_replay_rows('{LEGACY_APPLIED_ID}'::uuid); """, check=False, ) assert unavailable.returncode != 0 assert "exact applied transition unavailable" in unavailable.stderr def test_new_approval_preserves_exact_post_approval_row_for_apply_export(migrated_postgres: str) -> None: payload = '{"apply_payload":{"from_claim":"aaaaaaaa-1000-4000-8000-000000000003"}}' _psql( migrated_postgres, f""" insert into kb_stage.kb_proposals (id, proposal_type, status, payload, updated_at) values ('{FRESH_PROPOSAL_ID}'::uuid, 'add_edge', 'pending_review', '{payload}'::jsonb, '2026-07-18T11:00:00Z'); set session authorization kb_review; select kb_stage.approve_strict_proposal( '{FRESH_PROPOSAL_ID}'::uuid, 'add_edge', '{payload}'::jsonb, '{CANONICAL_REVIEWER_HANDLE}', 'Fresh exact approval snapshot.' ); """, ) approval_identity = _psql( migrated_postgres, f""" select proposal.reviewed_by_handle || '|' || proposal.reviewed_by_agent_id::text || '|' || approval.reviewed_by_handle || '|' || approval.reviewed_by_agent_id::text || '|' || (approval.approved_proposal_snapshot->>'reviewed_by_handle') || '|' || (approval.approved_proposal_snapshot->>'reviewed_by_agent_id') from kb_stage.kb_proposals proposal join kb_stage.kb_proposal_approvals approval on approval.proposal_id = proposal.id where proposal.id = '{FRESH_PROPOSAL_ID}'::uuid; """, ).stdout.strip() assert approval_identity == ( f"{CANONICAL_REVIEWER_HANDLE}|{REVIEWER_ID}|" f"{CANONICAL_REVIEWER_HANDLE}|{REVIEWER_ID}|" f"{CANONICAL_REVIEWER_HANDLE}|{REVIEWER_ID}" ) approval_state = _psql( migrated_postgres, f""" select jsonb_build_object( 'matches', approval.approved_proposal_snapshot = to_jsonb(proposal), 'status', approval.approved_proposal_snapshot->>'status', 'updated_at_matches', approval.approved_proposal_snapshot->'updated_at' = to_jsonb(proposal)->'updated_at' )::text from kb_stage.kb_proposal_approvals approval join kb_stage.kb_proposals proposal on proposal.id = approval.proposal_id where proposal.id = '{FRESH_PROPOSAL_ID}'::uuid; """, ).stdout.strip() assert approval_state == '{"status": "approved", "matches": true, "updated_at_matches": true}' direct_approval_read = _psql( migrated_postgres, "set session authorization kb_apply; select * from kb_stage.kb_proposal_approvals;", check=False, ) assert direct_approval_read.returncode != 0 assert "permission denied for table kb_proposal_approvals" in direct_approval_read.stderr premature_export = _psql( migrated_postgres, f""" set session authorization kb_apply; select kb_stage.export_applied_proposal_replay_rows('{FRESH_PROPOSAL_ID}'::uuid); """, check=False, ) assert premature_export.returncode != 0 assert "exact applied transition unavailable" in premature_export.stderr _psql( migrated_postgres, f""" update kb_stage.kb_proposal_approvals set approved_proposal_snapshot = approved_proposal_snapshot || '{{"rationale":"tampered after approval"}}'::jsonb where proposal_id = '{FRESH_PROPOSAL_ID}'::uuid; """, ) for guard_call in ( "kb_stage.assert_approved_proposal(" "proposal.id, proposal.proposal_type, proposal.payload, proposal.reviewed_by_handle, " "proposal.reviewed_by_agent_id, proposal.reviewed_at, proposal.review_note)", "kb_stage.finish_approved_proposal(" "proposal.id, proposal.proposal_type, proposal.payload, proposal.reviewed_by_handle, " "proposal.reviewed_by_agent_id, proposal.reviewed_at, proposal.review_note, 'kb-apply')", ): refused = _psql( migrated_postgres, f""" set session authorization kb_apply; select {guard_call} from kb_stage.kb_proposals proposal where proposal.id = '{FRESH_PROPOSAL_ID}'::uuid; """, check=False, ) assert refused.returncode != 0 assert "immutable approved snapshot" in refused.stderr _psql( migrated_postgres, f""" update kb_stage.kb_proposal_approvals approval set approved_proposal_snapshot = to_jsonb(proposal) from kb_stage.kb_proposals proposal where approval.proposal_id = proposal.id and proposal.id = '{FRESH_PROPOSAL_ID}'::uuid; """, ) exported = _psql( migrated_postgres, f""" set session authorization kb_apply; select kb_stage.assert_approved_proposal( proposal.id, proposal.proposal_type, proposal.payload, proposal.reviewed_by_handle, proposal.reviewed_by_agent_id, proposal.reviewed_at, proposal.review_note ) from kb_stage.kb_proposals proposal where proposal.id = '{FRESH_PROPOSAL_ID}'::uuid; select kb_stage.finish_approved_proposal( proposal.id, proposal.proposal_type, proposal.payload, proposal.reviewed_by_handle, proposal.reviewed_by_agent_id, proposal.reviewed_at, proposal.review_note, 'kb-apply' ) from kb_stage.kb_proposals proposal where proposal.id = '{FRESH_PROPOSAL_ID}'::uuid; select jsonb_object_keys(kb_stage.export_applied_proposal_replay_rows('{FRESH_PROPOSAL_ID}'::uuid)) order by 1; """, ).stdout.splitlines() assert exported[-3:] == ["applied_proposal", "approval_snapshot", "approved_proposal"] def test_gate_functions_are_timezone_independent_across_connections( migrated_postgres: str, ) -> None: payload = '{"apply_payload":{"from_claim":"aaaaaaaa-1000-4000-8000-000000000004"}}' _psql( migrated_postgres, f""" insert into kb_stage.kb_proposals (id, proposal_type, status, payload, updated_at) values ('{TIMEZONE_PROPOSAL_ID}'::uuid, 'add_edge', 'pending_review', '{payload}'::jsonb, '2026-07-18T12:00:00Z'); set timezone = 'America/Los_Angeles'; set session authorization kb_review; select kb_stage.approve_strict_proposal( '{TIMEZONE_PROPOSAL_ID}'::uuid, 'add_edge', '{payload}'::jsonb, '{CANONICAL_REVIEWER_HANDLE}', 'Cross-timezone exact approval snapshot.' ); """, ) asserted_and_finished = _psql( migrated_postgres, f""" set timezone = 'Asia/Tokyo'; set session authorization kb_apply; select kb_stage.assert_approved_proposal( proposal.id, proposal.proposal_type, proposal.payload, proposal.reviewed_by_handle, proposal.reviewed_by_agent_id, proposal.reviewed_at, proposal.review_note ) from kb_stage.kb_proposals proposal where proposal.id = '{TIMEZONE_PROPOSAL_ID}'::uuid; select kb_stage.finish_approved_proposal( proposal.id, proposal.proposal_type, proposal.payload, proposal.reviewed_by_handle, proposal.reviewed_by_agent_id, proposal.reviewed_at, proposal.review_note, 'kb-apply' ) from kb_stage.kb_proposals proposal where proposal.id = '{TIMEZONE_PROPOSAL_ID}'::uuid; """, ) assert '"status": "applied"' in asserted_and_finished.stdout exported = json.loads( _psql( migrated_postgres, f""" set timezone = 'Pacific/Auckland'; set session authorization kb_apply; select kb_stage.export_applied_proposal_replay_rows('{TIMEZONE_PROPOSAL_ID}'::uuid)::text; """, ).stdout.splitlines()[-1] ) approved = exported["approved_proposal"] applied = exported["applied_proposal"] approval = exported["approval_snapshot"] assert approved["reviewed_at"].endswith("+00:00") assert applied["reviewed_at"] == approved["reviewed_at"] assert approval["reviewed_at"] == approved["reviewed_at"] assert applied["status"] == "applied" def test_transition_export_rollback_preserves_evidence_and_reinstalls_cleanly() -> None: with _postgres_clone() as container: _apply_migration(container) before = _psql( container, f""" select approved_proposal_snapshot::text from kb_stage.kb_proposal_approvals where proposal_id = '{LEGACY_APPROVED_ID}'::uuid; """, ).stdout.strip() _psql( container, """ revoke all on function kb_stage.export_applied_proposal_replay_rows(uuid) from public, kb_gate_owner, kb_apply, kb_review; drop function kb_stage.export_applied_proposal_replay_rows(uuid); """, ) rolled_back = _psql( container, f""" select pg_catalog.to_regprocedure( 'kb_stage.export_applied_proposal_replay_rows(uuid)' ) is null; select approved_proposal_snapshot::text from kb_stage.kb_proposal_approvals where proposal_id = '{LEGACY_APPROVED_ID}'::uuid; """, ).stdout.splitlines() assert rolled_back == ["t", before] _apply_migration(container) restored = _psql( container, f""" select pg_catalog.to_regprocedure( 'kb_stage.export_applied_proposal_replay_rows(uuid)' ) is not null; select approved_proposal_snapshot::text from kb_stage.kb_proposal_approvals where proposal_id = '{LEGACY_APPROVED_ID}'::uuid; """, ).stdout.splitlines() assert restored == ["t", before] def _catalog_snapshot(container: str) -> str: return _psql( container, r""" select 'role|' || rolname || '|' || rolcanlogin || '|' || rolinherit || '|' || rolsuper from pg_catalog.pg_roles where rolname in ('kb_gate_owner', 'kb_review', 'kb_apply') order by rolname; select 'owner|' || namespace.nspname || '|' || relation.relname || '|' || owner_role.rolname from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace join pg_catalog.pg_roles owner_role on owner_role.oid = relation.relowner where (namespace.nspname, relation.relname) in ( ('public', 'agents'), ('public', 'strategies'), ('public', 'strategy_nodes'), ('public', 'claim_evidence'), ('public', 'claim_edges'), ('public', 'claims'), ('public', 'sources'), ('public', 'reasoning_tools'), ('kb_stage', 'kb_proposals'), ('kb_stage', 'kb_review_principals'), ('kb_stage', 'kb_proposal_approvals') ) order by namespace.nspname, relation.relname; select 'function|' || procedure.proname || '(' || pg_catalog.oidvectortypes(procedure.proargtypes) || ')|' || owner_role.rolname || '|' || procedure.prosecdef || '|' || procedure.proconfig::text from pg_catalog.pg_proc procedure join pg_catalog.pg_namespace namespace on namespace.oid = procedure.pronamespace join pg_catalog.pg_roles owner_role on owner_role.oid = procedure.proowner where namespace.nspname = 'kb_stage' and procedure.proname in ( 'approve_strict_proposal', 'assert_approved_proposal', 'finish_approved_proposal', 'export_applied_proposal_replay_rows' ) order by procedure.proname, procedure.proargtypes::text; select 'column|' || attribute.attname || '|' || attribute.atttypid::regtype::text || '|' || attribute.attnotnull from pg_catalog.pg_attribute attribute where attribute.attrelid = 'kb_stage.kb_proposal_approvals'::regclass and attribute.attname = 'approved_proposal_snapshot' and not attribute.attisdropped; select 'table_acl|' || namespace.nspname || '|' || relation.relname || '|' || case when acl.grantee = 0 then 'PUBLIC' else grantee_role.rolname end || '|' || pg_catalog.upper(acl.privilege_type) || '|' || acl.is_grantable from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace cross join lateral pg_catalog.aclexplode( coalesce(relation.relacl, pg_catalog.acldefault('r', relation.relowner)) ) acl left join pg_catalog.pg_roles grantee_role on grantee_role.oid = acl.grantee where (namespace.nspname, relation.relname) in ( ('public', 'agents'), ('public', 'strategies'), ('public', 'strategy_nodes'), ('public', 'claim_evidence'), ('public', 'claim_edges'), ('public', 'claims'), ('public', 'sources'), ('public', 'reasoning_tools'), ('kb_stage', 'kb_proposals'), ('kb_stage', 'kb_review_principals'), ('kb_stage', 'kb_proposal_approvals') ) and acl.grantee <> relation.relowner and (acl.grantee = 0 or grantee_role.rolname in ('kb_gate_owner', 'kb_review', 'kb_apply')) order by namespace.nspname, relation.relname, grantee_role.rolname, acl.privilege_type; select 'function_acl|' || procedure.proname || '(' || pg_catalog.oidvectortypes(procedure.proargtypes) || ')|' || case when acl.grantee = 0 then 'PUBLIC' else pg_catalog.pg_get_userbyid(acl.grantee) end || '|' || pg_catalog.upper(acl.privilege_type) || '|' || acl.is_grantable from pg_catalog.pg_proc procedure join pg_catalog.pg_namespace namespace on namespace.oid = procedure.pronamespace cross join lateral pg_catalog.aclexplode( coalesce(procedure.proacl, pg_catalog.acldefault('f', procedure.proowner)) ) acl where namespace.nspname = 'kb_stage' and procedure.proname in ( 'approve_strict_proposal', 'assert_approved_proposal', 'finish_approved_proposal', 'export_applied_proposal_replay_rows' ) and acl.grantee <> procedure.proowner order by procedure.proname, procedure.proargtypes::text, acl.grantee; """, ).stdout def test_upgrade_normalizes_exact_role_ownership_and_acl_matrix(migrated_postgres: str) -> None: role_rows = _lines( _psql( migrated_postgres, """ select rolname || '|' || rolcanlogin || '|' || rolinherit || '|' || rolsuper || '|' || rolcreatedb || '|' || rolcreaterole || '|' || rolreplication || '|' || rolbypassrls from pg_catalog.pg_roles where rolname in ('kb_gate_owner', 'kb_review', 'kb_apply') order by rolname; """, ) ) assert role_rows == { "kb_apply|true|false|false|false|false|false|false", "kb_gate_owner|false|false|false|false|false|false|false", "kb_review|true|false|false|false|false|false|false", } assert ( _psql( migrated_postgres, """ select count(*) from pg_catalog.pg_auth_members membership join pg_catalog.pg_roles member_role on member_role.oid = membership.member join pg_catalog.pg_roles granted_role on granted_role.oid = membership.roleid where member_role.rolname in ('kb_gate_owner', 'kb_review', 'kb_apply') or granted_role.rolname in ('kb_gate_owner', 'kb_review', 'kb_apply'); """, ).stdout.strip() == "0" ) schema_rows = _lines( _psql( migrated_postgres, """ select protected_role.rolname || '|' || protected_schema.nspname || '|' || pg_catalog.has_schema_privilege(protected_role.rolname, protected_schema.nspname, 'USAGE') || '|' || pg_catalog.has_schema_privilege(protected_role.rolname, protected_schema.nspname, 'CREATE') from (values ('kb_gate_owner'), ('kb_review'), ('kb_apply')) protected_role(rolname) cross join (values ('public'), ('kb_stage')) protected_schema(nspname); """, ) ) assert schema_rows == { f"{role}|{schema}|true|false" for role in ("kb_gate_owner", "kb_review", "kb_apply") for schema in ("public", "kb_stage") } function_rows = _lines( _psql( migrated_postgres, """ select procedure.proname || '(' || pg_catalog.oidvectortypes(procedure.proargtypes) || ')|' || owner_role.rolname || '|' || procedure.prosecdef || '|' || ( pg_catalog.cardinality(procedure.proconfig) = 2 and procedure.proconfig @> array[ 'search_path=pg_catalog, pg_temp'::text, 'TimeZone=UTC'::text ] ) from pg_catalog.pg_proc procedure join pg_catalog.pg_namespace namespace on namespace.oid = procedure.pronamespace join pg_catalog.pg_roles owner_role on owner_role.oid = procedure.proowner where namespace.nspname = 'kb_stage' and procedure.proname in ( 'approve_strict_proposal', 'assert_approved_proposal', 'finish_approved_proposal', 'export_applied_proposal_replay_rows' ); """, ) ) assert function_rows == { "approve_strict_proposal(uuid, text, jsonb, text, text)|kb_gate_owner|true|true", "assert_approved_proposal(uuid, text, jsonb, text, uuid, timestamp with time zone, text)|" "kb_gate_owner|true|true", "finish_approved_proposal(uuid, text, jsonb, text, uuid, timestamp with time zone, text, text)|" "kb_gate_owner|true|true", "export_applied_proposal_replay_rows(uuid)|kb_gate_owner|true|true", } assert ( _psql( migrated_postgres, "select pg_catalog.to_regprocedure('kb_stage.approve_strict_proposal(uuid,text,text)') is null;", ).stdout.strip() == "t" ) assert ( _psql( migrated_postgres, """ select count(*) from pg_catalog.pg_attribute attribute join pg_catalog.pg_class relation on relation.oid = attribute.attrelid join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace cross join lateral pg_catalog.aclexplode(attribute.attacl) acl left join pg_catalog.pg_roles grantee_role on grantee_role.oid = acl.grantee where (namespace.nspname, relation.relname) in ( ('public', 'agents'), ('public', 'strategies'), ('public', 'strategy_nodes'), ('public', 'claim_evidence'), ('public', 'claim_edges'), ('public', 'claims'), ('public', 'sources'), ('public', 'reasoning_tools'), ('kb_stage', 'kb_proposals'), ('kb_stage', 'kb_review_principals'), ('kb_stage', 'kb_proposal_approvals') ) and (acl.grantee = 0 or grantee_role.rolname in ('kb_gate_owner', 'kb_review', 'kb_apply')); """, ).stdout.strip() == "0" ) for runtime_role, target_role in ( ("kb_apply", "broad_writer"), ("kb_apply", "kb_gate_owner"), ("kb_review", "kb_gate_owner"), ): denied = _psql( migrated_postgres, f"set session authorization {runtime_role}; set role {target_role};", check=False, ) assert denied.returncode != 0 assert "permission denied to set role" in denied.stderr snapshot_lines = set(_catalog_snapshot(migrated_postgres).splitlines()) expected_table_acls = { "table_acl|public|agents|kb_gate_owner|SELECT|false", "table_acl|public|agents|kb_apply|SELECT|false", "table_acl|public|agents|kb_review|SELECT|false", "table_acl|public|strategies|kb_apply|SELECT|false", "table_acl|public|strategies|kb_apply|INSERT|false", "table_acl|public|strategies|kb_apply|UPDATE|false", "table_acl|public|strategy_nodes|kb_apply|SELECT|false", "table_acl|public|strategy_nodes|kb_apply|INSERT|false", "table_acl|public|strategy_nodes|kb_apply|UPDATE|false", "table_acl|public|claim_evidence|kb_apply|SELECT|false", "table_acl|public|claim_evidence|kb_apply|INSERT|false", "table_acl|public|claim_edges|kb_apply|SELECT|false", "table_acl|public|claim_edges|kb_apply|INSERT|false", "table_acl|public|claims|kb_apply|SELECT|false", "table_acl|public|claims|kb_apply|INSERT|false", "table_acl|public|sources|kb_apply|SELECT|false", "table_acl|public|sources|kb_apply|INSERT|false", "table_acl|public|reasoning_tools|kb_apply|SELECT|false", "table_acl|public|reasoning_tools|kb_apply|INSERT|false", "table_acl|kb_stage|kb_proposals|kb_gate_owner|SELECT|false", "table_acl|kb_stage|kb_proposals|kb_gate_owner|UPDATE|false", "table_acl|kb_stage|kb_proposals|kb_apply|SELECT|false", "table_acl|kb_stage|kb_proposals|kb_review|SELECT|false", } assert {line for line in snapshot_lines if line.startswith("table_acl|")} == expected_table_acls assert {line for line in snapshot_lines if line.startswith("function_acl|")} == { "function_acl|approve_strict_proposal(uuid, text, jsonb, text, text)|kb_review|EXECUTE|false", "function_acl|assert_approved_proposal(uuid, text, jsonb, text, uuid, timestamp with time zone, text)|" "kb_apply|EXECUTE|false", "function_acl|finish_approved_proposal(uuid, text, jsonb, text, uuid, timestamp with time zone, text, text)|" "kb_apply|EXECUTE|false", "function_acl|export_applied_proposal_replay_rows(uuid)|kb_apply|EXECUTE|false", } def test_migration_rerun_preserves_catalog_snapshot(migrated_postgres: str) -> None: before = _catalog_snapshot(migrated_postgres) _apply_migration(migrated_postgres) assert _catalog_snapshot(migrated_postgres) == before def _assert_migration_fails(container: str, expected_error: str) -> None: completed = _apply_migration(container, check=False) assert completed.returncode != 0 assert expected_error in completed.stderr def test_migration_fails_closed_on_protected_role_membership(migrated_postgres: str) -> None: _psql(migrated_postgres, "grant broad_writer to kb_apply;") try: _assert_migration_fails(migrated_postgres, "unexpected protected role membership") finally: _psql(migrated_postgres, "revoke broad_writer from kb_apply;") def test_migration_fails_closed_on_ownership_drift(migrated_postgres: str) -> None: _psql(migrated_postgres, "alter table kb_stage.kb_proposal_approvals owner to rogue_owner;") try: _assert_migration_fails(migrated_postgres, "unexpected protected table owner") finally: _psql(migrated_postgres, "alter table kb_stage.kb_proposal_approvals owner to kb_gate_owner;") def test_migration_fails_closed_on_unexpected_table_grantee(migrated_postgres: str) -> None: _psql(migrated_postgres, "grant select on kb_stage.kb_proposal_approvals to rogue_grantee;") try: _assert_migration_fails(migrated_postgres, "unexpected protected table grantee") finally: _psql(migrated_postgres, "revoke all on kb_stage.kb_proposal_approvals from rogue_grantee;") def test_migration_preserves_current_and_complete_observatory_read_surfaces() -> None: observatory_acl_sql = """ select namespace.nspname || '|' || relation.relname || '|' || acl.privilege_type || '|' || acl.is_grantable::text from pg_catalog.pg_class relation join pg_catalog.pg_namespace namespace on namespace.oid = relation.relnamespace cross join lateral pg_catalog.aclexplode(relation.relacl) acl join pg_catalog.pg_roles grantee on grantee.oid = acl.grantee where grantee.rolname = 'kb_observatory_read' order by namespace.nspname, relation.relname, acl.privilege_type; """ with _postgres_clone() as container: _psql( container, """ create role kb_observatory_read nologin noinherit nosuperuser nocreatedb nocreaterole noreplication nobypassrls; create role observatory_principal login noinherit nosuperuser nocreatedb nocreaterole noreplication nobypassrls; grant kb_observatory_read to observatory_principal; grant usage on schema public, kb_stage to kb_observatory_read; grant select on public.claims, public.claim_evidence, public.claim_edges to kb_observatory_read; """, ) # The current VPS manifest has this exact three-table read projection. _apply_migration(container) _apply_migration(container) assert _lines(_psql(container, observatory_acl_sql)) == { "public|claim_edges|SELECT|false", "public|claim_evidence|SELECT|false", "public|claims|SELECT|false", } # The independently managed Observatory installer extends the same # bounded surface to source and proposal provenance reads. _psql( container, """ grant select on public.sources, kb_stage.kb_proposals to kb_observatory_read; """, ) _apply_migration(container) assert _lines(_psql(container, observatory_acl_sql)) == { "kb_stage|kb_proposals|SELECT|false", "public|claim_edges|SELECT|false", "public|claim_evidence|SELECT|false", "public|claims|SELECT|false", "public|sources|SELECT|false", } for grant_sql, revoke_sql, expected_error in ( ( "grant insert on public.claims to kb_observatory_read;", "revoke insert on public.claims from kb_observatory_read;", "unexpected protected table grantee", ), ( "grant select on public.agents to kb_observatory_read;", "revoke select on public.agents from kb_observatory_read;", "unexpected protected table grantee", ), ( "grant select on public.sources to kb_observatory_read with grant option;", "revoke grant option for select on public.sources from kb_observatory_read;", "unexpected protected table grantee", ), ( "grant select (id) on public.claims to kb_observatory_read;", "revoke select (id) on public.claims from kb_observatory_read;", "unexpected protected table grantee", ), ( "grant execute on function " "kb_stage.assert_approved_proposal(uuid,text,jsonb,text,uuid,timestamptz,text) " "to kb_observatory_read;", "revoke execute on function " "kb_stage.assert_approved_proposal(uuid,text,jsonb,text,uuid,timestamptz,text) " "from kb_observatory_read;", "unexpected protected function grantee", ), ( "grant broad_writer to kb_observatory_read;", "revoke broad_writer from kb_observatory_read;", "unexpected protected role membership", ), ): _psql(container, grant_sql) try: _assert_migration_fails(container, expected_error) finally: _psql(container, revoke_sql) _apply_migration(container) def test_migration_refuses_rogue_canonical_insert_before_install_and_normalizes_after_removal() -> None: with _postgres_clone() as container: _psql(container, "grant usage on schema public to rogue_grantee;") for grant_sql, revoke_sql in ( ( "grant insert on public.claims to rogue_grantee;", "revoke insert on public.claims from rogue_grantee;", ), ( "grant insert (id) on public.claims to rogue_grantee;", "revoke insert (id) on public.claims from rogue_grantee;", ), ): _psql(container, grant_sql) refused = _apply_migration(container, check=False) assert refused.returncode != 0 assert "unexpected protected table grantee" in refused.stderr assert _psql(container, "select count(*) from public.claims;").stdout.strip() == "0" _psql(container, revoke_sql) _apply_migration(container) rogue_insert = _psql( container, "set session authorization rogue_grantee; " "insert into public.claims (id) values ('99999999-9999-4999-8999-999999999999');", check=False, ) assert rogue_insert.returncode != 0 assert "permission denied for table claims" in rogue_insert.stderr assert _psql(container, "select count(*) from public.claims;").stdout.strip() == "0" def test_migration_fails_closed_on_unexpected_column_grantee(migrated_postgres: str) -> None: _psql( migrated_postgres, "grant select (review_note) on kb_stage.kb_proposal_approvals to rogue_grantee;", ) try: _assert_migration_fails(migrated_postgres, "unexpected protected table grantee") finally: _psql( migrated_postgres, "revoke select (review_note) on kb_stage.kb_proposal_approvals from rogue_grantee;", ) def test_migration_fails_closed_on_unexpected_function_grantee(migrated_postgres: str) -> None: signature = "kb_stage.assert_approved_proposal(uuid,text,jsonb,text,uuid,timestamptz,text)" _psql(migrated_postgres, f"grant execute on function {signature} to rogue_grantee;") try: _assert_migration_fails(migrated_postgres, "unexpected protected function grantee") finally: _psql(migrated_postgres, f"revoke all on function {signature} from rogue_grantee;") def test_migration_fails_closed_on_unexpected_gate_overload(migrated_postgres: str) -> None: _psql( migrated_postgres, """ create function kb_stage.assert_approved_proposal(text) returns void language sql security definer as $function$ select $function$; """, ) try: _assert_migration_fails(migrated_postgres, "unexpected protected function overload") finally: _psql(migrated_postgres, "drop function kb_stage.assert_approved_proposal(text);")