1926 lines
79 KiB
Python
1926 lines
79 KiB
Python
#!/usr/bin/env python3
|
|
"""Verify the minimum V3 epistemic contract in disposable PostgreSQL 16.
|
|
|
|
The verifier accepts no database address or credentials. It creates its own
|
|
networkless, tmpfs-backed Docker container, installs the additive migration,
|
|
round-trips proposal-linked epistemic rows, exercises adversarial failures,
|
|
rolls the contract back, reinstalls it, rolls it back again, and removes the
|
|
container. Production and persistent databases are unreachable by design.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import re
|
|
import secrets
|
|
import shutil
|
|
import subprocess
|
|
import time
|
|
import uuid
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
MIGRATION_SQL = REPO_ROOT / "ops" / "teleo_v3_epistemic_contract.sql"
|
|
ROLLBACK_SQL = REPO_ROOT / "ops" / "rollback_teleo_v3_epistemic_contract.sql"
|
|
|
|
RECEIPT_SCHEMA = "livingip.teleoV3EpistemicContractReceipt.v1"
|
|
POSTGRES_IMAGE = os.environ.get("LEOCLEAN_RUNTIME_POSTGRES_IMAGE", "postgres:16-alpine")
|
|
DATABASE = "teleo_v3_contract"
|
|
CONTAINER_LABEL = "livingip.canary=teleo-v3-epistemic-contract"
|
|
TRIGGER_CONTRACT_PATTERN = re.compile(
|
|
r"\$trigger_contract\$\s*(\[.*?\])\s*\$trigger_contract\$",
|
|
re.DOTALL,
|
|
)
|
|
CATALOG_LOCK_FUNCTION_NAME = "teleo_v3_acquire_protected_relation_locks"
|
|
CATALOG_LOCK_FUNCTION_SOURCE_SHA256 = "897345944ba0f1b2a5c01b098fb17de6f881a122e906277086fa7889e76219e8"
|
|
|
|
AGENT_ID = "11111111-1111-4111-8111-111111111111"
|
|
LEGACY_CLAIM_ID = "20000000-0000-4000-8000-000000000001"
|
|
LEGACY_RELATED_CLAIM_ID = "20000000-0000-4000-8000-000000000002"
|
|
LEGACY_SOURCE_ID = "20000000-0000-4000-8000-000000000003"
|
|
LEGACY_EVIDENCE_ID = "20000000-0000-4000-8000-000000000004"
|
|
LEGACY_EDGE_ID = "20000000-0000-4000-8000-000000000005"
|
|
|
|
PROPOSAL_ID = "30000000-0000-4000-8000-000000000001"
|
|
PENDING_PROPOSAL_ID = "30000000-0000-4000-8000-000000000002"
|
|
APPLIED_WITHOUT_RECEIPT_PROPOSAL_ID = "30000000-0000-4000-8000-000000000003"
|
|
PRIMARY_CLAIM_ID = "40000000-0000-4000-8000-000000000001"
|
|
SUPPORT_CLAIM_ID = "40000000-0000-4000-8000-000000000002"
|
|
SOURCE_ID = "40000000-0000-4000-8000-000000000003"
|
|
EVIDENCE_ID = "40000000-0000-4000-8000-000000000004"
|
|
ASSESSMENT_ID = "40000000-0000-4000-8000-000000000005"
|
|
EDGE_ID = "40000000-0000-4000-8000-000000000006"
|
|
|
|
SOURCE_PAYLOAD = b"LivingIP V3 source payload: immutable epistemic provenance.\n"
|
|
SOURCE_CONTENT_HASH = hashlib.sha256(SOURCE_PAYLOAD).hexdigest()
|
|
EVIDENCE_SET_HASH = hashlib.sha256(
|
|
json.dumps(
|
|
{
|
|
"claim_id": PRIMARY_CLAIM_ID,
|
|
"evidence": [
|
|
{
|
|
"source_id": SOURCE_ID,
|
|
"source_content_hash": SOURCE_CONTENT_HASH,
|
|
"polarity": "supports",
|
|
"locator": {"paragraph": 1, "section": "epistemic-contract"},
|
|
}
|
|
],
|
|
},
|
|
separators=(",", ":"),
|
|
sort_keys=True,
|
|
).encode("utf-8")
|
|
).hexdigest()
|
|
|
|
PRIMARY_PROPOSITION = "An admitted V3 claim preserves exact evidence provenance."
|
|
PRIMARY_BODY = (
|
|
"The claim records a proposition and explanatory body together, pins a content-hashed source locator, "
|
|
"and remains attributable to the reviewed proposal that admitted it."
|
|
)
|
|
PRIMARY_SCOPE = "Minimum disposable PostgreSQL V3 contract verification."
|
|
PRIMARY_REVISION_CRITERIA = (
|
|
"Revise if the stored source hash, locator, proposal decision, or evidence assessment cannot be reproduced."
|
|
)
|
|
|
|
|
|
def load_expected_trigger_contract() -> list[dict[str, Any]]:
|
|
match = TRIGGER_CONTRACT_PATTERN.search(MIGRATION_SQL.read_text(encoding="utf-8"))
|
|
if match is None:
|
|
raise VerificationError("migration does not declare the V3 trigger catalog contract")
|
|
contract = json.loads(match.group(1))
|
|
if not isinstance(contract, list):
|
|
raise VerificationError("migration V3 trigger catalog contract is not a JSON array")
|
|
return contract
|
|
|
|
|
|
def trigger_catalog_is_exact(catalog: dict[str, Any]) -> bool:
|
|
expected_triggers = load_expected_trigger_contract()
|
|
actual_triggers = catalog["triggers"]
|
|
if len(actual_triggers) != len(expected_triggers):
|
|
return False
|
|
|
|
expected_by_key = {(row["schema_name"], row["table_name"], row["trigger_name"]): row for row in expected_triggers}
|
|
actual_by_key = {(row["schema_name"], row["table_name"], row["trigger_name"]): row for row in actual_triggers}
|
|
if len(expected_by_key) != len(expected_triggers) or len(actual_by_key) != len(actual_triggers):
|
|
return False
|
|
if actual_by_key.keys() != expected_by_key.keys():
|
|
return False
|
|
|
|
for key, expected in expected_by_key.items():
|
|
actual = actual_by_key[key]
|
|
if actual["enabled_state"] != "O" or actual["is_internal"]:
|
|
return False
|
|
if actual["trigger_type"] != expected["trigger_type"]:
|
|
return False
|
|
if actual["update_columns"] != expected["update_columns"]:
|
|
return False
|
|
if actual["when_expression"] != expected["when_expression"]:
|
|
return False
|
|
if actual["argument_count"] != expected["argument_count"]:
|
|
return False
|
|
if actual["argument_hex"] != expected["argument_hex"]:
|
|
return False
|
|
if any(
|
|
(
|
|
actual["constraint_oid"] != "0",
|
|
actual["is_deferrable"],
|
|
actual["is_initially_deferred"],
|
|
actual["constraint_relation_oid"] != "0",
|
|
actual["parent_trigger_oid"] != "0",
|
|
actual["old_transition_table"] is not None,
|
|
actual["new_transition_table"] is not None,
|
|
)
|
|
):
|
|
return False
|
|
if actual["function_schema"] != expected["function_schema"]:
|
|
return False
|
|
if actual["function_name"] != expected["function_name"]:
|
|
return False
|
|
if actual["function_identity_arguments"] != expected["function_identity_arguments"]:
|
|
return False
|
|
|
|
expected_functions = {
|
|
(
|
|
row["function_schema"],
|
|
row["function_name"],
|
|
row["function_identity_arguments"],
|
|
): row
|
|
for row in expected_triggers
|
|
}
|
|
actual_functions = {
|
|
(row["schema_name"], row["function_name"], row["arguments"]): row for row in catalog["functions"]
|
|
}
|
|
if len(actual_functions) != len(catalog["functions"]):
|
|
return False
|
|
catalog_lock_function = actual_functions.pop(
|
|
("kb_stage", CATALOG_LOCK_FUNCTION_NAME, ""),
|
|
None,
|
|
)
|
|
if catalog_lock_function is None or any(
|
|
(
|
|
catalog_lock_function["owner_name"] != "kb_gate_owner",
|
|
not catalog_lock_function["security_definer"],
|
|
catalog_lock_function["config"] != ["search_path=pg_catalog, pg_temp"],
|
|
catalog_lock_function["source_sha256"] != CATALOG_LOCK_FUNCTION_SOURCE_SHA256,
|
|
catalog_lock_function["language_name"] != "plpgsql",
|
|
catalog_lock_function["function_kind"] != "f",
|
|
catalog_lock_function["argument_count"] != 0,
|
|
catalog_lock_function["return_type"] != "void",
|
|
catalog_lock_function["non_owner_execute_grantees"] != ["kb_apply"],
|
|
)
|
|
):
|
|
return False
|
|
if actual_functions.keys() != expected_functions.keys():
|
|
return False
|
|
for key, expected in expected_functions.items():
|
|
actual = actual_functions[key]
|
|
if actual["owner_name"] != expected["function_owner"]:
|
|
return False
|
|
if actual["security_definer"] != expected["function_security_definer"]:
|
|
return False
|
|
if actual["config"] != expected["function_config"]:
|
|
return False
|
|
if actual["source_sha256"] != expected["function_source_sha256"]:
|
|
return False
|
|
if expected["function_owner_only_execute"] is not None and (
|
|
actual["owner_only_execute"] != expected["function_owner_only_execute"]
|
|
):
|
|
return False
|
|
if actual["language_name"] != "plpgsql":
|
|
return False
|
|
if actual["function_kind"] != "f" or actual["argument_count"] != 0:
|
|
return False
|
|
if actual["return_type"] != "trigger":
|
|
return False
|
|
return True
|
|
|
|
|
|
class VerificationError(RuntimeError):
|
|
"""Raised when the disposable runtime cannot prove the contract."""
|
|
|
|
|
|
def canonical_sha256(value: Any) -> str:
|
|
payload = json.dumps(value, ensure_ascii=False, separators=(",", ":"), sort_keys=True)
|
|
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
|
|
|
|
|
|
def parse_last_json_line(output: str) -> Any:
|
|
for line in reversed(output.splitlines()):
|
|
candidate = line.strip()
|
|
if not candidate:
|
|
continue
|
|
try:
|
|
return json.loads(candidate)
|
|
except json.JSONDecodeError:
|
|
continue
|
|
return None
|
|
|
|
|
|
def safe_error_text(value: str, *, limit: int = 1600) -> str:
|
|
return " ".join(value.split())[-limit:]
|
|
|
|
|
|
def sql_literal(value: str) -> str:
|
|
escaped = value.replace("\\", "\\\\").replace("'", "''")
|
|
return f"E'{escaped}'"
|
|
|
|
|
|
def docker_available() -> bool:
|
|
docker = shutil.which("docker")
|
|
if not docker:
|
|
return False
|
|
result = subprocess.run(
|
|
[docker, "info", "--format", "{{.ServerVersion}}"],
|
|
text=True,
|
|
capture_output=True,
|
|
check=False,
|
|
timeout=15,
|
|
)
|
|
return result.returncode == 0
|
|
|
|
|
|
BOOTSTRAP_SQL = f"""
|
|
\\set ON_ERROR_STOP on
|
|
create schema kb_stage;
|
|
create role kb_gate_owner nologin noinherit;
|
|
create role kb_apply nologin noinherit;
|
|
create role kb_review login noinherit;
|
|
create type evidence_role as enum ('grounds', 'illustrates', 'contradicts');
|
|
create type edge_type as enum (
|
|
'supports', 'challenges', 'requires', 'relates', 'contradicts', 'supersedes',
|
|
'derives_from', 'cites', 'causes', 'constrains', 'accelerates'
|
|
);
|
|
|
|
create table public.agents (
|
|
id uuid primary key,
|
|
handle text not null unique,
|
|
kind text not null,
|
|
created_at timestamptz not null default now()
|
|
);
|
|
|
|
create table public.claims (
|
|
id uuid primary key,
|
|
type text not null,
|
|
text text not null,
|
|
status text not null,
|
|
confidence numeric,
|
|
tags text[] not null default '{{}}',
|
|
created_by uuid references public.agents(id),
|
|
superseded_by uuid references public.claims(id),
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now()
|
|
);
|
|
|
|
create table public.sources (
|
|
id uuid primary key,
|
|
source_type text not null,
|
|
url text,
|
|
storage_path text,
|
|
excerpt text,
|
|
hash text not null,
|
|
created_by uuid references public.agents(id),
|
|
captured_at timestamptz,
|
|
created_at timestamptz not null default now()
|
|
);
|
|
|
|
create table public.claim_evidence (
|
|
id uuid primary key default gen_random_uuid(),
|
|
claim_id uuid not null references public.claims(id),
|
|
source_id uuid not null references public.sources(id),
|
|
role evidence_role not null,
|
|
weight numeric,
|
|
created_by uuid references public.agents(id),
|
|
created_at timestamptz not null default now()
|
|
);
|
|
|
|
create table public.claim_edges (
|
|
id uuid primary key,
|
|
from_claim uuid not null references public.claims(id),
|
|
to_claim uuid not null references public.claims(id),
|
|
edge_type edge_type not null,
|
|
weight numeric,
|
|
created_by uuid references public.agents(id),
|
|
created_at timestamptz not null default now()
|
|
);
|
|
|
|
create table kb_stage.kb_proposals (
|
|
id uuid primary key,
|
|
proposal_type text not null,
|
|
status text not null,
|
|
proposed_by_handle text,
|
|
proposed_by_agent_id uuid references public.agents(id),
|
|
channel text,
|
|
source_ref text,
|
|
rationale text,
|
|
payload jsonb not null,
|
|
reviewed_by_handle text,
|
|
reviewed_by_agent_id uuid references public.agents(id),
|
|
reviewed_at timestamptz,
|
|
review_note text,
|
|
applied_by_handle text,
|
|
applied_by_agent_id uuid references public.agents(id),
|
|
applied_at timestamptz,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now(),
|
|
constraint kb_proposals_status_check check (
|
|
status in ('pending_review', 'approved', 'rejected', 'applied', 'canceled')
|
|
)
|
|
);
|
|
|
|
grant usage on schema kb_stage to kb_gate_owner;
|
|
grant select, update on kb_stage.kb_proposals to kb_gate_owner;
|
|
|
|
insert into public.agents (id, handle, kind, created_at)
|
|
values ('{AGENT_ID}', 'v3-contract-reviewer', 'human', '2026-07-18T08:00:00Z');
|
|
|
|
insert into public.claims
|
|
(id, type, text, status, confidence, tags, created_by, created_at, updated_at)
|
|
values
|
|
('{LEGACY_CLAIM_ID}', 'structural', 'Legacy claim without a body.', 'open', 0.4,
|
|
array['legacy'], '{AGENT_ID}', '2026-07-18T08:01:00Z', '2026-07-18T08:01:00Z'),
|
|
('{LEGACY_RELATED_CLAIM_ID}', 'structural', 'Second legacy claim.', 'open', null,
|
|
array['legacy'], '{AGENT_ID}', '2026-07-18T08:02:00Z', '2026-07-18T08:02:00Z');
|
|
|
|
insert into public.sources
|
|
(id, source_type, url, storage_path, excerpt, hash, created_by, captured_at, created_at)
|
|
values
|
|
('{LEGACY_SOURCE_ID}', 'legacy_document', null, '/legacy/source.txt', 'Legacy excerpt.', 'legacy-hash',
|
|
'{AGENT_ID}', '2026-07-18T08:03:00Z', '2026-07-18T08:03:00Z');
|
|
|
|
insert into public.claim_evidence
|
|
(id, claim_id, source_id, role, weight, created_by, created_at)
|
|
values
|
|
('{LEGACY_EVIDENCE_ID}', '{LEGACY_CLAIM_ID}', '{LEGACY_SOURCE_ID}', 'grounds', 0.25,
|
|
'{AGENT_ID}', '2026-07-18T08:04:00Z');
|
|
|
|
insert into public.claim_edges
|
|
(id, from_claim, to_claim, edge_type, weight, created_by, created_at)
|
|
values
|
|
('{LEGACY_EDGE_ID}', '{LEGACY_CLAIM_ID}', '{LEGACY_RELATED_CLAIM_ID}', 'relates', 0.5,
|
|
'{AGENT_ID}', '2026-07-18T08:05:00Z');
|
|
|
|
-- This malformed historical row proves the NOT VALID proposal constraint is
|
|
-- additive while the canonical-row trigger still refuses it as authority.
|
|
insert into kb_stage.kb_proposals (
|
|
id, proposal_type, status, proposed_by_handle, proposed_by_agent_id, channel,
|
|
rationale, payload, reviewed_by_handle, reviewed_by_agent_id, reviewed_at, review_note,
|
|
created_at, updated_at
|
|
) values (
|
|
'{APPLIED_WITHOUT_RECEIPT_PROPOSAL_ID}', 'approve_claim', 'applied', 'leo', '{AGENT_ID}',
|
|
'legacy_fixture', 'Historical applied status without a receipt.',
|
|
'{{"contract_version": 2}}'::jsonb, 'v3-contract-reviewer', '{AGENT_ID}',
|
|
'2026-07-18T08:06:00Z', 'Historical malformed receipt retained for compatibility testing.',
|
|
'2026-07-18T08:06:00Z', '2026-07-18T08:06:00Z'
|
|
);
|
|
"""
|
|
|
|
|
|
LEGACY_STATE_SQL = """
|
|
select jsonb_build_object(
|
|
'claims', coalesce((
|
|
select jsonb_agg(to_jsonb(row_data) order by row_data.id)
|
|
from (
|
|
select id, type, text, status, confidence, tags, created_by, superseded_by, created_at, updated_at
|
|
from public.claims
|
|
where id in (
|
|
'20000000-0000-4000-8000-000000000001'::uuid,
|
|
'20000000-0000-4000-8000-000000000002'::uuid
|
|
)
|
|
) row_data
|
|
), '[]'::jsonb),
|
|
'sources', coalesce((
|
|
select jsonb_agg(to_jsonb(row_data) order by row_data.id)
|
|
from (
|
|
select id, source_type, url, storage_path, excerpt, hash, created_by, captured_at, created_at
|
|
from public.sources
|
|
where id = '20000000-0000-4000-8000-000000000003'::uuid
|
|
) row_data
|
|
), '[]'::jsonb),
|
|
'claim_evidence', coalesce((
|
|
select jsonb_agg(to_jsonb(row_data) order by row_data.id)
|
|
from (
|
|
select id, claim_id, source_id, role, weight, created_by, created_at
|
|
from public.claim_evidence
|
|
where id = '20000000-0000-4000-8000-000000000004'::uuid
|
|
) row_data
|
|
), '[]'::jsonb),
|
|
'claim_edges', coalesce((
|
|
select jsonb_agg(to_jsonb(row_data) order by row_data.id)
|
|
from (
|
|
select id, from_claim, to_claim, edge_type, weight, created_by, created_at
|
|
from public.claim_edges
|
|
where id = '20000000-0000-4000-8000-000000000005'::uuid
|
|
) row_data
|
|
), '[]'::jsonb)
|
|
)::text;
|
|
"""
|
|
|
|
|
|
CONTRACT_CATALOG_SQL = """
|
|
with contract_columns as (
|
|
select table_schema, table_name, column_name, data_type, udt_name, is_nullable, column_default
|
|
from information_schema.columns
|
|
where table_schema = 'public'
|
|
and (
|
|
(table_name = 'claims' and column_name in (
|
|
'owner_agent_id', 'proposition', 'body_markdown', 'scope', 'access_scope', 'admission_state',
|
|
'revision_criteria', 'revision_kind', 'supersedes_id', 'accepted_by_proposal_id'
|
|
))
|
|
or (table_name = 'sources' and column_name in (
|
|
'canonical_url', 'storage_uri', 'content_hash', 'access_scope', 'ingestion_origin',
|
|
'provenance_status', 'source_class', 'auto_promotion_policy', 'accepted_by_proposal_id'
|
|
))
|
|
or (table_name = 'claim_evidence' and column_name in (
|
|
'excerpt', 'locator_json', 'source_content_hash', 'rationale', 'polarity', 'accepted_from_proposal_id'
|
|
))
|
|
or (table_name = 'claim_edges' and column_name in (
|
|
'relation_type', 'rationale', 'scope_or_conditions', 'accepted_by_proposal_id'
|
|
))
|
|
)
|
|
), contract_constraints as (
|
|
select namespace.nspname as schema_name, relation.relname as table_name,
|
|
constraint_row.conname as constraint_name,
|
|
pg_get_constraintdef(constraint_row.oid, true) as definition
|
|
from pg_constraint constraint_row
|
|
join pg_class relation on relation.oid = constraint_row.conrelid
|
|
join pg_namespace namespace on namespace.oid = relation.relnamespace
|
|
where namespace.nspname in ('public', 'kb_stage')
|
|
and constraint_row.conname like 'teleo_v3_%'
|
|
), contract_triggers as (
|
|
select namespace.nspname as schema_name, relation.relname as table_name,
|
|
trigger_row.tgname as trigger_name,
|
|
trigger_row.tgenabled as enabled_state,
|
|
trigger_row.tgisinternal as is_internal,
|
|
trigger_row.tgtype as trigger_type,
|
|
coalesce((
|
|
select jsonb_agg(attribute.attname order by attribute.attname)
|
|
from unnest(trigger_row.tgattr::smallint[]) update_column(attnum)
|
|
join pg_attribute attribute
|
|
on attribute.attrelid = trigger_row.tgrelid
|
|
and attribute.attnum = update_column.attnum
|
|
and not attribute.attisdropped
|
|
), '[]'::jsonb) as update_columns,
|
|
pg_get_expr(trigger_row.tgqual, trigger_row.tgrelid, true) as when_expression,
|
|
trigger_row.tgnargs as argument_count,
|
|
encode(trigger_row.tgargs, 'hex') as argument_hex,
|
|
trigger_row.tgconstraint::text as constraint_oid,
|
|
trigger_row.tgdeferrable as is_deferrable,
|
|
trigger_row.tginitdeferred as is_initially_deferred,
|
|
trigger_row.tgconstrrelid::text as constraint_relation_oid,
|
|
trigger_row.tgparentid::text as parent_trigger_oid,
|
|
trigger_row.tgoldtable as old_transition_table,
|
|
trigger_row.tgnewtable as new_transition_table,
|
|
function_namespace.nspname as function_schema,
|
|
trigger_function.proname as function_name,
|
|
pg_get_function_identity_arguments(trigger_function.oid) as function_identity_arguments,
|
|
pg_get_triggerdef(trigger_row.oid, true) as definition
|
|
from pg_trigger trigger_row
|
|
join pg_class relation on relation.oid = trigger_row.tgrelid
|
|
join pg_namespace namespace on namespace.oid = relation.relnamespace
|
|
join pg_proc trigger_function on trigger_function.oid = trigger_row.tgfoid
|
|
join pg_namespace function_namespace on function_namespace.oid = trigger_function.pronamespace
|
|
where left(trigger_row.tgname, 9) = 'teleo_v3_'
|
|
), contract_functions as (
|
|
select namespace.nspname as schema_name, procedure.proname as function_name,
|
|
pg_get_function_identity_arguments(procedure.oid) as arguments,
|
|
owner_role.rolname as owner_name,
|
|
procedure.prosecdef as security_definer,
|
|
procedure.proconfig as config,
|
|
encode(sha256(convert_to(procedure.prosrc, 'UTF8')), 'hex') as source_sha256,
|
|
language.lanname as language_name,
|
|
procedure.prokind as function_kind,
|
|
procedure.pronargs as argument_count,
|
|
procedure.prorettype::regtype::text as return_type,
|
|
not exists (
|
|
select 1
|
|
from pg_catalog.aclexplode(
|
|
coalesce(
|
|
procedure.proacl,
|
|
pg_catalog.acldefault('f', procedure.proowner)
|
|
)
|
|
) acl
|
|
where acl.privilege_type = 'EXECUTE'
|
|
and acl.grantee <> procedure.proowner
|
|
) as owner_only_execute,
|
|
coalesce((
|
|
select jsonb_agg(coalesce(grantee.rolname, 'PUBLIC') order by coalesce(grantee.rolname, 'PUBLIC'))
|
|
from pg_catalog.aclexplode(
|
|
coalesce(
|
|
procedure.proacl,
|
|
pg_catalog.acldefault('f', procedure.proowner)
|
|
)
|
|
) acl
|
|
left join pg_catalog.pg_roles grantee on grantee.oid = acl.grantee
|
|
where acl.privilege_type = 'EXECUTE'
|
|
and acl.grantee <> procedure.proowner
|
|
), '[]'::jsonb) as non_owner_execute_grantees
|
|
from pg_proc procedure
|
|
join pg_namespace namespace on namespace.oid = procedure.pronamespace
|
|
join pg_roles owner_role on owner_role.oid = procedure.proowner
|
|
join pg_language language on language.oid = procedure.prolang
|
|
where namespace.nspname = 'kb_stage'
|
|
and left(procedure.proname, 9) = 'teleo_v3_'
|
|
), contract_indexes as (
|
|
select schemaname, tablename, indexname, indexdef
|
|
from pg_indexes
|
|
where schemaname = 'public' and indexname like 'teleo_v3_%'
|
|
)
|
|
select jsonb_build_object(
|
|
'assessment_table', to_regclass('public.claim_evidence_assessments')::text,
|
|
'columns', coalesce((
|
|
select jsonb_agg(to_jsonb(contract_columns) order by table_name, column_name) from contract_columns
|
|
), '[]'::jsonb),
|
|
'constraints', coalesce((
|
|
select jsonb_agg(to_jsonb(contract_constraints) order by schema_name, table_name, constraint_name)
|
|
from contract_constraints
|
|
), '[]'::jsonb),
|
|
'triggers', coalesce((
|
|
select jsonb_agg(to_jsonb(contract_triggers) order by schema_name, table_name, trigger_name)
|
|
from contract_triggers
|
|
), '[]'::jsonb),
|
|
'functions', coalesce((
|
|
select jsonb_agg(to_jsonb(contract_functions) order by schema_name, function_name, arguments)
|
|
from contract_functions
|
|
), '[]'::jsonb),
|
|
'indexes', coalesce((
|
|
select jsonb_agg(to_jsonb(contract_indexes) order by schemaname, tablename, indexname)
|
|
from contract_indexes
|
|
), '[]'::jsonb)
|
|
)::text;
|
|
"""
|
|
|
|
|
|
POSTGRES_READY_LOG_LINE = "database system is ready to accept connections"
|
|
POSTGRES_STARTUP_TIMEOUT_SECONDS = 45.0
|
|
POSTGRES_STARTUP_PROBE_TIMEOUT_SECONDS = 10.0
|
|
POSTGRES_STARTUP_POLL_INTERVAL_SECONDS = 0.25
|
|
POSTGRES_CLEANUP_COMMAND_TIMEOUT_SECONDS = 10.0
|
|
POSTGRES_CLEANUP_MAX_ATTEMPTS = 5
|
|
POSTGRES_CLEANUP_POLL_INTERVAL_SECONDS = 0.25
|
|
DOCKER_CONTAINER_ID_PATTERN = re.compile(r"[0-9a-f]{12,64}")
|
|
|
|
|
|
def postgres_final_startup_ready(
|
|
logs: str,
|
|
database_probe: subprocess.CompletedProcess[str],
|
|
) -> bool:
|
|
return (
|
|
logs.count(POSTGRES_READY_LOG_LINE) >= 2
|
|
and database_probe.returncode == 0
|
|
and database_probe.stdout.strip() == "1"
|
|
)
|
|
|
|
|
|
class DisposablePostgres:
|
|
def __init__(self) -> None:
|
|
self.docker = shutil.which("docker")
|
|
if not self.docker:
|
|
raise VerificationError("docker executable is unavailable")
|
|
self.name = f"teleo-v3-contract-{uuid.uuid4().hex[:12]}"
|
|
self.password = secrets.token_urlsafe(32)
|
|
self.started = False
|
|
self.cleanup_required = False
|
|
self.environment: dict[str, Any] = {}
|
|
|
|
def run(
|
|
self,
|
|
arguments: list[str],
|
|
*,
|
|
input_text: str | None = None,
|
|
check: bool = True,
|
|
timeout: float = 120,
|
|
environment: dict[str, str] | None = None,
|
|
) -> subprocess.CompletedProcess[str]:
|
|
result = subprocess.run(
|
|
[self.docker, *arguments],
|
|
input=input_text,
|
|
text=True,
|
|
capture_output=True,
|
|
check=False,
|
|
timeout=timeout,
|
|
env=environment,
|
|
)
|
|
if check and result.returncode != 0:
|
|
detail = safe_error_text(result.stderr or result.stdout)
|
|
raise VerificationError(f"docker {arguments[0]} failed ({result.returncode}): {detail}")
|
|
return result
|
|
|
|
def _redacted_startup_detail(self, value: str | bytes | None) -> str:
|
|
if isinstance(value, bytes):
|
|
value = value.decode("utf-8", errors="replace")
|
|
raw_detail = value or ""
|
|
if self.password:
|
|
raw_detail = raw_detail.replace(self.password, "<redacted>")
|
|
detail = safe_error_text(raw_detail, limit=500)
|
|
return detail or "<empty>"
|
|
|
|
def _startup_result_diagnostic(
|
|
self,
|
|
stage: str,
|
|
result: subprocess.CompletedProcess[str],
|
|
) -> str:
|
|
detail = self._redacted_startup_detail(result.stderr or result.stdout)
|
|
return f"stage={stage} kind=nonzero returncode={result.returncode} detail={detail}"
|
|
|
|
def _startup_exception_diagnostic(
|
|
self,
|
|
stage: str,
|
|
exc: OSError | subprocess.TimeoutExpired,
|
|
) -> str:
|
|
if isinstance(exc, subprocess.TimeoutExpired):
|
|
detail = self._redacted_startup_detail(exc.stderr or exc.output)
|
|
return f"stage={stage} kind=timeout timeout_seconds={exc.timeout:g} detail={detail}"
|
|
return f"stage={stage} kind=launch_error detail={self._redacted_startup_detail(str(exc))}"
|
|
|
|
def _wait_for_final_startup(
|
|
self,
|
|
*,
|
|
startup_timeout: float = POSTGRES_STARTUP_TIMEOUT_SECONDS,
|
|
probe_timeout: float = POSTGRES_STARTUP_PROBE_TIMEOUT_SECONDS,
|
|
poll_interval: float = POSTGRES_STARTUP_POLL_INTERVAL_SECONDS,
|
|
) -> None:
|
|
if startup_timeout <= 0 or probe_timeout <= 0 or poll_interval < 0:
|
|
raise ValueError("PostgreSQL startup timing values must be positive")
|
|
|
|
deadline = time.monotonic() + startup_timeout
|
|
ready_streak = 0
|
|
attempts = 0
|
|
last_diagnostic = "stage=startup kind=no_probe_completed"
|
|
while True:
|
|
remaining = deadline - time.monotonic()
|
|
if remaining <= 0:
|
|
break
|
|
|
|
attempts += 1
|
|
try:
|
|
logs = self.run(
|
|
["logs", self.name],
|
|
check=False,
|
|
timeout=min(probe_timeout, remaining),
|
|
)
|
|
except (OSError, subprocess.TimeoutExpired) as exc:
|
|
ready_streak = 0
|
|
last_diagnostic = self._startup_exception_diagnostic("docker_logs", exc)
|
|
else:
|
|
if logs.returncode != 0:
|
|
ready_streak = 0
|
|
last_diagnostic = self._startup_result_diagnostic("docker_logs", logs)
|
|
else:
|
|
remaining = deadline - time.monotonic()
|
|
if remaining <= 0:
|
|
last_diagnostic = "stage=target_database_probe kind=deadline_exhausted_after_logs"
|
|
break
|
|
try:
|
|
database_probe = self.psql(
|
|
"select 1;",
|
|
check=False,
|
|
timeout=min(probe_timeout, remaining),
|
|
)
|
|
except (OSError, subprocess.TimeoutExpired) as exc:
|
|
ready_streak = 0
|
|
last_diagnostic = self._startup_exception_diagnostic("target_database_probe", exc)
|
|
else:
|
|
if postgres_final_startup_ready(logs.stdout + logs.stderr, database_probe):
|
|
ready_streak += 1
|
|
if ready_streak == 2:
|
|
return
|
|
else:
|
|
ready_streak = 0
|
|
if database_probe.returncode != 0:
|
|
last_diagnostic = self._startup_result_diagnostic(
|
|
"target_database_probe",
|
|
database_probe,
|
|
)
|
|
else:
|
|
ready_transitions = (logs.stdout + logs.stderr).count(POSTGRES_READY_LOG_LINE)
|
|
probe_output = self._redacted_startup_detail(database_probe.stdout)
|
|
last_diagnostic = (
|
|
"stage=readiness_gate kind=not_ready "
|
|
f"ready_transitions={ready_transitions} "
|
|
f"database_probe_returncode={database_probe.returncode} "
|
|
f"database_probe_output={probe_output}"
|
|
)
|
|
|
|
remaining = deadline - time.monotonic()
|
|
if remaining <= 0:
|
|
break
|
|
time.sleep(min(poll_interval, remaining))
|
|
|
|
raise VerificationError(
|
|
f"disposable PostgreSQL did not become stably ready within {startup_timeout:g}s "
|
|
f"after {attempts} attempt(s); last_probe={last_diagnostic}"
|
|
)
|
|
|
|
def _launch_container(self) -> subprocess.CompletedProcess[str]:
|
|
arguments = [
|
|
"run",
|
|
"--detach",
|
|
"--rm",
|
|
"--name",
|
|
self.name,
|
|
"--network",
|
|
"none",
|
|
"--label",
|
|
CONTAINER_LABEL,
|
|
"--label",
|
|
f"livingip.canary.instance={self.name}",
|
|
"--tmpfs",
|
|
"/var/lib/postgresql/data:rw,nosuid,nodev,size=384m",
|
|
"--env",
|
|
"POSTGRES_PASSWORD",
|
|
"--env",
|
|
f"POSTGRES_DB={DATABASE}",
|
|
POSTGRES_IMAGE,
|
|
]
|
|
launch_environment = os.environ.copy()
|
|
launch_environment["POSTGRES_PASSWORD"] = self.password
|
|
self.cleanup_required = True
|
|
try:
|
|
launched = self.run(
|
|
arguments,
|
|
check=False,
|
|
environment=launch_environment,
|
|
)
|
|
except (OSError, subprocess.TimeoutExpired) as exc:
|
|
diagnostic = self._startup_exception_diagnostic("docker_run", exc)
|
|
raise VerificationError(
|
|
"disposable PostgreSQL launch state is ambiguous; cleanup_required=true; "
|
|
f"{diagnostic}"
|
|
) from None
|
|
if launched.returncode != 0:
|
|
diagnostic = self._startup_result_diagnostic("docker_run", launched)
|
|
raise VerificationError(
|
|
"disposable PostgreSQL launch failed; cleanup_required=true; "
|
|
f"{diagnostic}"
|
|
)
|
|
self.started = True
|
|
return launched
|
|
|
|
def start(self) -> dict[str, Any]:
|
|
launched = self._launch_container()
|
|
|
|
self._wait_for_final_startup()
|
|
|
|
inspected = json.loads(self.run(["inspect", self.name]).stdout)[0]
|
|
labels = inspected.get("Config", {}).get("Labels", {})
|
|
mounts = inspected.get("Mounts", [])
|
|
tmpfs = inspected.get("HostConfig", {}).get("Tmpfs", {})
|
|
self.environment = {
|
|
"container_id_sha256": hashlib.sha256(launched.stdout.strip().encode("utf-8")).hexdigest(),
|
|
"container_name": self.name,
|
|
"image": inspected.get("Config", {}).get("Image"),
|
|
"image_id": inspected.get("Image"),
|
|
"network_mode": inspected.get("HostConfig", {}).get("NetworkMode"),
|
|
"tmpfs_data_dir": "/var/lib/postgresql/data" in tmpfs,
|
|
"docker_volume_mounts": [
|
|
{"type": mount.get("Type"), "destination": mount.get("Destination")} for mount in mounts
|
|
],
|
|
"canary_label": labels.get("livingip.canary"),
|
|
"instance_label": labels.get("livingip.canary.instance"),
|
|
}
|
|
if self.environment["network_mode"] != "none":
|
|
raise VerificationError("disposable PostgreSQL is not networkless")
|
|
if not self.environment["tmpfs_data_dir"] or mounts:
|
|
raise VerificationError("disposable PostgreSQL must use tmpfs without Docker volumes")
|
|
if self.environment["canary_label"] != "teleo-v3-epistemic-contract":
|
|
raise VerificationError("disposable PostgreSQL canary label is missing")
|
|
if self.environment["instance_label"] != self.name:
|
|
raise VerificationError("disposable PostgreSQL instance label is missing")
|
|
return self.environment
|
|
|
|
def psql(
|
|
self,
|
|
sql: str,
|
|
*,
|
|
check: bool = True,
|
|
timeout: float = 120,
|
|
) -> subprocess.CompletedProcess[str]:
|
|
return self.run(
|
|
[
|
|
"exec",
|
|
"--interactive",
|
|
self.name,
|
|
"psql",
|
|
"--no-psqlrc",
|
|
"--no-password",
|
|
"--set=ON_ERROR_STOP=1",
|
|
"--set=VERBOSITY=verbose",
|
|
"--tuples-only",
|
|
"--no-align",
|
|
"--username",
|
|
"postgres",
|
|
"--dbname",
|
|
DATABASE,
|
|
],
|
|
input_text=sql,
|
|
check=check,
|
|
timeout=timeout,
|
|
)
|
|
|
|
def psql_json(self, sql: str) -> Any:
|
|
result = self.psql(sql)
|
|
parsed = parse_last_json_line(result.stdout)
|
|
if parsed is None:
|
|
raise VerificationError("PostgreSQL readback did not contain JSON")
|
|
return parsed
|
|
|
|
def apply_file(self, path: Path, *, check: bool = True) -> subprocess.CompletedProcess[str]:
|
|
return self.psql(path.read_text(encoding="utf-8"), check=check)
|
|
|
|
def _cleanup_run(
|
|
self,
|
|
arguments: list[str],
|
|
*,
|
|
stage: str,
|
|
) -> tuple[subprocess.CompletedProcess[str] | None, str | None]:
|
|
try:
|
|
return (
|
|
self.run(
|
|
arguments,
|
|
check=False,
|
|
timeout=POSTGRES_CLEANUP_COMMAND_TIMEOUT_SECONDS,
|
|
),
|
|
None,
|
|
)
|
|
except (OSError, subprocess.TimeoutExpired) as exc:
|
|
return None, self._startup_exception_diagnostic(stage, exc)
|
|
|
|
def cleanup(self) -> dict[str, Any]:
|
|
ownership_filters = [
|
|
"container",
|
|
"ls",
|
|
"--all",
|
|
"--no-trunc",
|
|
"--filter",
|
|
f"name=^/{self.name}$",
|
|
"--filter",
|
|
"label=livingip.canary=teleo-v3-epistemic-contract",
|
|
"--filter",
|
|
f"label=livingip.canary.instance={self.name}",
|
|
"--format",
|
|
"{{.ID}}",
|
|
]
|
|
name_readback = [
|
|
"container",
|
|
"ls",
|
|
"--all",
|
|
"--filter",
|
|
f"name=^/{self.name}$",
|
|
"--format",
|
|
"{{.Names}}",
|
|
]
|
|
label_readback = [
|
|
"container",
|
|
"ls",
|
|
"--all",
|
|
"--filter",
|
|
"label=livingip.canary=teleo-v3-epistemic-contract",
|
|
"--filter",
|
|
f"label=livingip.canary.instance={self.name}",
|
|
"--format",
|
|
"{{.Names}}",
|
|
]
|
|
errors: list[str] = []
|
|
observed_owned_ids: set[str] = set()
|
|
remove_returncodes: list[int] = []
|
|
remove_attempted = False
|
|
stable_clear_observations = 0
|
|
inspect: subprocess.CompletedProcess[str] | None = None
|
|
names: subprocess.CompletedProcess[str] | None = None
|
|
labels: subprocess.CompletedProcess[str] | None = None
|
|
attempts = 0
|
|
|
|
for attempts in range(1, POSTGRES_CLEANUP_MAX_ATTEMPTS + 1):
|
|
ownership, ownership_error = self._cleanup_run(
|
|
ownership_filters,
|
|
stage="cleanup_ownership_readback",
|
|
)
|
|
if ownership_error:
|
|
errors.append(ownership_error)
|
|
owned_ids: list[str] = []
|
|
if ownership is not None and ownership.returncode == 0:
|
|
candidates = [line.strip() for line in ownership.stdout.splitlines() if line.strip()]
|
|
if all(DOCKER_CONTAINER_ID_PATTERN.fullmatch(candidate) for candidate in candidates):
|
|
owned_ids = candidates
|
|
observed_owned_ids.update(candidates)
|
|
else:
|
|
errors.append("stage=cleanup_ownership_readback kind=invalid_container_id")
|
|
elif ownership is not None:
|
|
errors.append(self._startup_result_diagnostic("cleanup_ownership_readback", ownership))
|
|
|
|
if self.cleanup_required:
|
|
for container_id in owned_ids:
|
|
remove_attempted = True
|
|
removed, remove_error = self._cleanup_run(
|
|
["rm", "--force", container_id],
|
|
stage="cleanup_remove",
|
|
)
|
|
if remove_error:
|
|
errors.append(remove_error)
|
|
elif removed is not None:
|
|
remove_returncodes.append(removed.returncode)
|
|
if removed.returncode != 0:
|
|
errors.append(self._startup_result_diagnostic("cleanup_remove", removed))
|
|
|
|
inspect, inspect_error = self._cleanup_run(
|
|
["inspect", self.name],
|
|
stage="cleanup_inspect_readback",
|
|
)
|
|
names, names_error = self._cleanup_run(name_readback, stage="cleanup_name_readback")
|
|
labels, labels_error = self._cleanup_run(label_readback, stage="cleanup_label_readback")
|
|
errors.extend(error for error in (inspect_error, names_error, labels_error) if error)
|
|
if names is not None and names.returncode != 0:
|
|
errors.append(self._startup_result_diagnostic("cleanup_name_readback", names))
|
|
if labels is not None and labels.returncode != 0:
|
|
errors.append(self._startup_result_diagnostic("cleanup_label_readback", labels))
|
|
|
|
name_clear = names is not None and names.returncode == 0 and not names.stdout.strip()
|
|
label_clear = labels is not None and labels.returncode == 0 and not labels.stdout.strip()
|
|
inspect_clear = inspect is not None and inspect.returncode != 0
|
|
if inspect_clear and name_clear and label_clear:
|
|
stable_clear_observations += 1
|
|
if stable_clear_observations == 2:
|
|
break
|
|
else:
|
|
stable_clear_observations = 0
|
|
if attempts < POSTGRES_CLEANUP_MAX_ATTEMPTS:
|
|
time.sleep(POSTGRES_CLEANUP_POLL_INTERVAL_SECONDS)
|
|
|
|
name_readback_clear = names is not None and names.returncode == 0 and not names.stdout.strip()
|
|
instance_label_readback_clear = labels is not None and labels.returncode == 0 and not labels.stdout.strip()
|
|
container_absent = (
|
|
inspect is not None
|
|
and inspect.returncode != 0
|
|
and name_readback_clear
|
|
and instance_label_readback_clear
|
|
)
|
|
return {
|
|
"cleanup_required": self.cleanup_required,
|
|
"cleanup_reconciliation_attempted": self.cleanup_required,
|
|
"remove_attempted": remove_attempted,
|
|
"remove_returncode": remove_returncodes[-1] if remove_returncodes else None,
|
|
"remove_returncodes": remove_returncodes,
|
|
"owned_container_ids_observed": sorted(observed_owned_ids),
|
|
"container_absent": container_absent,
|
|
"name_readback_clear": name_readback_clear,
|
|
"instance_label_readback_clear": instance_label_readback_clear,
|
|
"stable_absence_readback": stable_clear_observations >= 2,
|
|
"readback_attempts": attempts,
|
|
"foreign_name_collision_detected": not name_readback_clear and instance_label_readback_clear,
|
|
"errors": errors,
|
|
"network_mode_was_none": self.environment.get("network_mode") == "none",
|
|
"tmpfs_data_dir_was_present": self.environment.get("tmpfs_data_dir") is True,
|
|
"docker_volume_mounts_observed": self.environment.get("docker_volume_mounts", []),
|
|
}
|
|
|
|
|
|
def catalog_readback(postgres: DisposablePostgres) -> dict[str, Any]:
|
|
value = postgres.psql_json(CONTRACT_CATALOG_SQL)
|
|
if not isinstance(value, dict):
|
|
raise VerificationError("contract catalog readback was not an object")
|
|
return value
|
|
|
|
|
|
def catalog_is_absent(catalog: dict[str, Any]) -> bool:
|
|
return catalog.get("assessment_table") is None and all(
|
|
catalog.get(key) == [] for key in ("columns", "constraints", "triggers", "functions", "indexes")
|
|
)
|
|
|
|
|
|
def failure_receipt(result: subprocess.CompletedProcess[str], expected_sqlstate: str) -> dict[str, Any]:
|
|
match = re.search(r"(?:ERROR|FATAL):\s+([0-9A-Z]{5}):", result.stderr)
|
|
observed_sqlstate = match.group(1) if match else None
|
|
return {
|
|
"refused": result.returncode != 0,
|
|
"returncode": result.returncode,
|
|
"expected_sqlstate": expected_sqlstate,
|
|
"observed_sqlstate": observed_sqlstate,
|
|
"expected_sqlstate_observed": observed_sqlstate == expected_sqlstate,
|
|
"stderr_tail": safe_error_text(result.stderr, limit=500),
|
|
}
|
|
|
|
|
|
def expect_failure(
|
|
postgres: DisposablePostgres,
|
|
sql: str,
|
|
*,
|
|
expected_sqlstate: str,
|
|
) -> dict[str, Any]:
|
|
result = postgres.psql(sql, check=False)
|
|
receipt = failure_receipt(result, expected_sqlstate)
|
|
if not receipt["refused"] or not receipt["expected_sqlstate_observed"]:
|
|
raise VerificationError(
|
|
f"adversarial statement did not fail with SQLSTATE {expected_sqlstate}: {receipt['stderr_tail']}"
|
|
)
|
|
return receipt
|
|
|
|
|
|
def proposal_decision_lock_race_check(postgres: DisposablePostgres) -> dict[str, Any]:
|
|
"""Prove a canonical writer locks the accepted decision against concurrent downgrade."""
|
|
|
|
assert postgres.docker is not None
|
|
command = [
|
|
postgres.docker,
|
|
"exec",
|
|
"--interactive",
|
|
postgres.name,
|
|
"psql",
|
|
"--no-psqlrc",
|
|
"--no-password",
|
|
"--set=ON_ERROR_STOP=1",
|
|
"--set=VERBOSITY=verbose",
|
|
"--tuples-only",
|
|
"--no-align",
|
|
"--username",
|
|
"postgres",
|
|
"--dbname",
|
|
DATABASE,
|
|
]
|
|
holder = subprocess.Popen(
|
|
command,
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True,
|
|
)
|
|
assert holder.stdin is not None
|
|
holder.stdin.write(
|
|
f"""
|
|
set application_name = 'teleo_v3_proposal_lock_holder';
|
|
begin;
|
|
select status from kb_stage.kb_proposals where id = '{PROPOSAL_ID}'::uuid for share;
|
|
select pg_sleep(4);
|
|
rollback;
|
|
"""
|
|
)
|
|
holder.stdin.close()
|
|
holder.stdin = None
|
|
try:
|
|
deadline = time.monotonic() + 8
|
|
lock_held = False
|
|
while time.monotonic() < deadline:
|
|
lock_held = postgres.psql_json(
|
|
"""
|
|
select jsonb_build_object(
|
|
'held', exists (
|
|
select 1 from pg_stat_activity
|
|
where application_name = 'teleo_v3_proposal_lock_holder'
|
|
and state = 'active'
|
|
and wait_event = 'PgSleep'
|
|
)
|
|
)::text;
|
|
"""
|
|
)["held"]
|
|
if lock_held:
|
|
break
|
|
time.sleep(0.1)
|
|
if not lock_held:
|
|
raise VerificationError("proposal lock holder did not reach the protected transaction")
|
|
downgrade = postgres.psql(
|
|
f"""
|
|
set statement_timeout = '750ms';
|
|
update kb_stage.kb_proposals set status = 'rejected' where id = '{PROPOSAL_ID}'::uuid;
|
|
""",
|
|
check=False,
|
|
)
|
|
downgrade_receipt = failure_receipt(downgrade, "57014")
|
|
holder_stdout, holder_stderr = holder.communicate(timeout=8)
|
|
status = postgres.psql_json(
|
|
f"select jsonb_build_object('status', status)::text from kb_stage.kb_proposals "
|
|
f"where id = '{PROPOSAL_ID}'::uuid;"
|
|
)["status"]
|
|
return {
|
|
"lock_observed": lock_held,
|
|
"concurrent_downgrade": downgrade_receipt,
|
|
"holder_returncode": holder.returncode,
|
|
"holder_stderr_tail": safe_error_text(holder_stderr, limit=300),
|
|
"holder_output_observed": bool(holder_stdout.strip()),
|
|
"proposal_status_after": status,
|
|
}
|
|
finally:
|
|
if holder.poll() is None:
|
|
holder.terminate()
|
|
try:
|
|
holder.wait(timeout=3)
|
|
except subprocess.TimeoutExpired:
|
|
holder.kill()
|
|
holder.wait(timeout=3)
|
|
|
|
|
|
def accepted_rows_sql() -> str:
|
|
return f"""
|
|
insert into kb_stage.kb_proposals (
|
|
id, proposal_type, status, proposed_by_handle, proposed_by_agent_id, channel,
|
|
source_ref, rationale, payload, reviewed_by_handle, reviewed_by_agent_id,
|
|
reviewed_at, review_note, created_at, updated_at
|
|
) values (
|
|
'{PROPOSAL_ID}', 'approve_claim', 'approved', 'leo', '{AGENT_ID}', 'disposable_verifier',
|
|
'fixture://livingip-v3-contract', 'Admit exact hash-bound V3 epistemic rows.',
|
|
'{{"apply_payload": {{"contract_version": 3}}}}'::jsonb, 'v3-contract-reviewer', '{AGENT_ID}',
|
|
'2026-07-18T09:00:00Z', 'Accepted only for the disposable PostgreSQL contract verifier.',
|
|
'2026-07-18T08:59:00Z', '2026-07-18T09:00:00Z'
|
|
);
|
|
|
|
insert into kb_stage.kb_proposals (
|
|
id, proposal_type, status, proposed_by_handle, proposed_by_agent_id, channel,
|
|
rationale, payload, created_at, updated_at
|
|
) values (
|
|
'{PENDING_PROPOSAL_ID}', 'approve_claim', 'pending_review', 'leo', '{AGENT_ID}',
|
|
'disposable_verifier', 'Pending proposal must not authorize canonical rows.',
|
|
'{{"apply_payload": {{"contract_version": 3}}}}'::jsonb,
|
|
'2026-07-18T09:01:00Z', '2026-07-18T09:01:00Z'
|
|
);
|
|
|
|
insert into public.sources (
|
|
id, source_type, url, storage_path, excerpt, hash, created_by, captured_at, created_at,
|
|
canonical_url, storage_uri, content_hash, access_scope, ingestion_origin,
|
|
provenance_status, source_class, auto_promotion_policy, accepted_by_proposal_id
|
|
) values (
|
|
'{SOURCE_ID}', 'document', 'https://example.invalid/livingip-v3-source', null,
|
|
'Immutable epistemic provenance.', '{SOURCE_CONTENT_HASH}', '{AGENT_ID}',
|
|
'2026-07-18T09:02:00Z', '2026-07-18T09:02:00Z',
|
|
'https://example.invalid/livingip-v3-source', null, '{SOURCE_CONTENT_HASH}',
|
|
'collective_internal', 'operator_upload', 'verified', 'primary_record', 'human_required',
|
|
'{PROPOSAL_ID}'
|
|
);
|
|
|
|
insert into public.claims (
|
|
id, type, text, status, confidence, tags, created_by, created_at, updated_at,
|
|
owner_agent_id, proposition, body_markdown, scope, access_scope, admission_state,
|
|
revision_criteria, revision_kind, supersedes_id, accepted_by_proposal_id
|
|
) values (
|
|
'{PRIMARY_CLAIM_ID}', 'empirical', {sql_literal(PRIMARY_PROPOSITION)}, 'active', null,
|
|
array['v3-contract'], '{AGENT_ID}', '2026-07-18T09:03:00Z', '2026-07-18T09:03:00Z',
|
|
null, {sql_literal(PRIMARY_PROPOSITION)}, {sql_literal(PRIMARY_BODY)}, {sql_literal(PRIMARY_SCOPE)},
|
|
'collective_internal', 'admitted', {sql_literal(PRIMARY_REVISION_CRITERIA)}, 'semantic',
|
|
'{LEGACY_CLAIM_ID}', '{PROPOSAL_ID}'
|
|
), (
|
|
'{SUPPORT_CLAIM_ID}', 'conceptual',
|
|
'Content-hashed locators make source versions reproducible.', 'active', null,
|
|
array['v3-contract'], '{AGENT_ID}', '2026-07-18T09:04:00Z', '2026-07-18T09:04:00Z',
|
|
null, 'Content-hashed locators make source versions reproducible.',
|
|
'A locator and immutable source hash identify the exact source payload used by an evidence link.',
|
|
'Minimum disposable PostgreSQL V3 contract verification.', 'collective_internal', 'admitted',
|
|
'Revise if the hash and locator no longer identify one exact source payload.', 'original', null,
|
|
'{PROPOSAL_ID}'
|
|
);
|
|
|
|
insert into public.claim_evidence (
|
|
id, claim_id, source_id, role, weight, created_by, created_at,
|
|
excerpt, locator_json, source_content_hash, rationale, polarity, accepted_from_proposal_id
|
|
) values (
|
|
'{EVIDENCE_ID}', '{PRIMARY_CLAIM_ID}', '{SOURCE_ID}', 'grounds', null, '{AGENT_ID}',
|
|
'2026-07-18T09:05:00Z', 'Immutable epistemic provenance.',
|
|
'{{"paragraph": 1, "section": "epistemic-contract"}}'::jsonb, '{SOURCE_CONTENT_HASH}',
|
|
'The cited passage directly supports reproducible source provenance.', 'supports', '{PROPOSAL_ID}'
|
|
);
|
|
|
|
insert into public.claim_evidence_assessments (
|
|
id, claim_id, support_tier, challenge_tier, overall_state, rationale,
|
|
evidence_set_hash, accepted_by_proposal_id, created_at
|
|
) values (
|
|
'{ASSESSMENT_ID}', '{PRIMARY_CLAIM_ID}', 'strong', 'none', 'support_dominant',
|
|
'One exact primary-record source supports the bounded contract claim and no challenge is linked.',
|
|
'{EVIDENCE_SET_HASH}', '{PROPOSAL_ID}', '2026-07-18T09:06:00Z'
|
|
);
|
|
|
|
insert into public.claim_edges (
|
|
id, from_claim, to_claim, edge_type, weight, created_by, created_at,
|
|
relation_type, rationale, scope_or_conditions, accepted_by_proposal_id
|
|
) values (
|
|
'{EDGE_ID}', '{SUPPORT_CLAIM_ID}', '{PRIMARY_CLAIM_ID}', 'supports', null, '{AGENT_ID}',
|
|
'2026-07-18T09:07:00Z', 'supports',
|
|
'Reproducible source versions are a prerequisite for exact evidence provenance.',
|
|
'Within the minimum V3 epistemic storage contract.', '{PROPOSAL_ID}'
|
|
);
|
|
"""
|
|
|
|
|
|
def round_trip_sql() -> str:
|
|
return f"""
|
|
select jsonb_build_object(
|
|
'proposal', (select to_jsonb(row_data) from (
|
|
select id, proposal_type, status, reviewed_by_handle, reviewed_by_agent_id,
|
|
reviewed_at, review_note
|
|
from kb_stage.kb_proposals where id = '{PROPOSAL_ID}'::uuid
|
|
) row_data),
|
|
'claim', (select to_jsonb(row_data) from (
|
|
select id, type, text, proposition, body_markdown, scope, access_scope,
|
|
admission_state, revision_criteria, revision_kind, supersedes_id,
|
|
accepted_by_proposal_id
|
|
from public.claims where id = '{PRIMARY_CLAIM_ID}'::uuid
|
|
) row_data),
|
|
'support_claim', (select to_jsonb(row_data) from (
|
|
select id, admission_state, revision_kind, accepted_by_proposal_id
|
|
from public.claims where id = '{SUPPORT_CLAIM_ID}'::uuid
|
|
) row_data),
|
|
'source', (select to_jsonb(row_data) from (
|
|
select id, canonical_url, storage_uri, hash, content_hash, access_scope,
|
|
ingestion_origin, provenance_status, source_class, auto_promotion_policy,
|
|
accepted_by_proposal_id
|
|
from public.sources where id = '{SOURCE_ID}'::uuid
|
|
) row_data),
|
|
'evidence', (select to_jsonb(row_data) from (
|
|
select id, claim_id, source_id, role, excerpt, locator_json, source_content_hash,
|
|
rationale, polarity, accepted_from_proposal_id
|
|
from public.claim_evidence where id = '{EVIDENCE_ID}'::uuid
|
|
) row_data),
|
|
'assessment', (select to_jsonb(row_data) from (
|
|
select id, claim_id, support_tier, challenge_tier, overall_state, rationale,
|
|
evidence_set_hash, accepted_by_proposal_id
|
|
from public.claim_evidence_assessments where id = '{ASSESSMENT_ID}'::uuid
|
|
) row_data),
|
|
'edge', (select to_jsonb(row_data) from (
|
|
select id, from_claim, to_claim, relation_type, rationale, scope_or_conditions,
|
|
accepted_by_proposal_id
|
|
from public.claim_edges where id = '{EDGE_ID}'::uuid
|
|
) row_data)
|
|
)::text;
|
|
"""
|
|
|
|
|
|
def adversarial_checks(postgres: DisposablePostgres) -> dict[str, Any]:
|
|
valid_source_columns = """
|
|
source_type, url, hash, canonical_url, content_hash, access_scope, ingestion_origin,
|
|
provenance_status, source_class, auto_promotion_policy, accepted_by_proposal_id
|
|
"""
|
|
valid_source_values = f"""
|
|
'document', 'https://example.invalid/pending', '{SOURCE_CONTENT_HASH}',
|
|
'https://example.invalid/pending', '{SOURCE_CONTENT_HASH}', 'collective_internal',
|
|
'operator_upload', 'verified', 'primary_record', 'human_required', '{PENDING_PROPOSAL_ID}'
|
|
"""
|
|
checks = {
|
|
"new_legacy_claim_cannot_bypass_v3_contract": expect_failure(
|
|
postgres,
|
|
"""
|
|
insert into public.claims (id, type, text, status)
|
|
values ('50000000-0000-4000-8000-000000000011', 'structural', 'Legacy bypass.', 'open');
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"new_unlinked_source_cannot_bypass_v3_contract": expect_failure(
|
|
postgres,
|
|
f"""
|
|
insert into public.sources (id, source_type, hash)
|
|
values ('50000000-0000-4000-8000-000000000012', 'document', '{SOURCE_CONTENT_HASH}');
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"new_unlinked_evidence_cannot_bypass_v3_contract": expect_failure(
|
|
postgres,
|
|
f"""
|
|
insert into public.claim_evidence (id, claim_id, source_id, role)
|
|
values (
|
|
'50000000-0000-4000-8000-000000000013', '{LEGACY_CLAIM_ID}', '{LEGACY_SOURCE_ID}', 'grounds'
|
|
);
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"new_unlinked_edge_cannot_bypass_v3_contract": expect_failure(
|
|
postgres,
|
|
f"""
|
|
insert into public.claim_edges (id, from_claim, to_claim, edge_type)
|
|
values (
|
|
'50000000-0000-4000-8000-000000000014',
|
|
'{LEGACY_CLAIM_ID}', '{LEGACY_RELATED_CLAIM_ID}', 'relates'
|
|
);
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"pending_proposal_cannot_authorize_source": expect_failure(
|
|
postgres,
|
|
f"insert into public.sources (id, {valid_source_columns}) "
|
|
f"values ('50000000-0000-4000-8000-000000000001', {valid_source_values});",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"applied_status_without_apply_receipt_cannot_authorize_source": expect_failure(
|
|
postgres,
|
|
f"""
|
|
insert into public.sources (id, {valid_source_columns}) values (
|
|
'50000000-0000-4000-8000-000000000015',
|
|
'document', 'https://example.invalid/missing-apply-receipt', '{SOURCE_CONTENT_HASH}',
|
|
'https://example.invalid/missing-apply-receipt', '{SOURCE_CONTENT_HASH}', 'collective_internal',
|
|
'operator_upload', 'verified', 'primary_record', 'human_required',
|
|
'{APPLIED_WITHOUT_RECEIPT_PROPOSAL_ID}'
|
|
);
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"new_applied_proposal_without_receipt_is_rejected": expect_failure(
|
|
postgres,
|
|
"""
|
|
insert into kb_stage.kb_proposals (
|
|
id, proposal_type, status, rationale, payload, reviewed_by_handle, reviewed_at, review_note
|
|
) values (
|
|
'30000000-0000-4000-8000-000000000004', 'approve_claim', 'applied',
|
|
'Invalid new apply receipt.', '{"apply_payload": {"contract_version": 3}}'::jsonb,
|
|
'v3-contract-reviewer', '2026-07-18T09:00:00Z', 'Missing applicator and applied_at.'
|
|
);
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"non_applied_proposal_cannot_carry_apply_metadata": expect_failure(
|
|
postgres,
|
|
f"""
|
|
update kb_stage.kb_proposals
|
|
set applied_by_handle = 'premature-applicator', applied_at = '2026-07-18T09:00:00Z'
|
|
where id = '{PENDING_PROPOSAL_ID}'::uuid;
|
|
""",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"admitted_claim_requires_body_scope_and_revision_criteria": expect_failure(
|
|
postgres,
|
|
f"""
|
|
insert into public.claims (
|
|
id, type, text, status, proposition, body_markdown, scope, access_scope,
|
|
admission_state, revision_criteria, revision_kind, accepted_by_proposal_id
|
|
) values (
|
|
'50000000-0000-4000-8000-000000000002', 'empirical', 'Incomplete admitted claim.', 'active',
|
|
'Incomplete admitted claim.', null, 'Verifier scope.', 'collective_internal',
|
|
'admitted', 'Revise when incomplete.', 'original', '{PROPOSAL_ID}'
|
|
);
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"null_revision_kind_cannot_bypass_claim_contract": expect_failure(
|
|
postgres,
|
|
f"""
|
|
insert into public.claims (
|
|
id, type, text, status, proposition, body_markdown, scope, access_scope,
|
|
admission_state, revision_criteria, revision_kind, accepted_by_proposal_id
|
|
) values (
|
|
'50000000-0000-4000-8000-000000000007', 'empirical', 'Null revision kind claim.', 'active',
|
|
'Null revision kind claim.', 'A complete body cannot compensate for a null required revision kind.',
|
|
'Verifier scope.', 'collective_internal', 'admitted', 'Revise when null checks fail.', null, '{PROPOSAL_ID}'
|
|
);
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"null_source_classification_cannot_bypass_source_contract": expect_failure(
|
|
postgres,
|
|
f"""
|
|
insert into public.sources (
|
|
id, source_type, url, hash, canonical_url, content_hash, access_scope, ingestion_origin,
|
|
provenance_status, source_class, auto_promotion_policy, accepted_by_proposal_id
|
|
) values (
|
|
'50000000-0000-4000-8000-000000000008', 'document', 'https://example.invalid/null-class',
|
|
'{SOURCE_CONTENT_HASH}', 'https://example.invalid/null-class', '{SOURCE_CONTENT_HASH}',
|
|
'collective_internal', 'operator_upload', 'verified', null, 'human_required', '{PROPOSAL_ID}'
|
|
);
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"evidence_hash_must_match_source_version": expect_failure(
|
|
postgres,
|
|
f"""
|
|
insert into public.claim_evidence (
|
|
id, claim_id, source_id, role, weight, locator_json, source_content_hash,
|
|
rationale, polarity, accepted_from_proposal_id
|
|
) values (
|
|
'50000000-0000-4000-8000-000000000003', '{PRIMARY_CLAIM_ID}', '{SOURCE_ID}', 'grounds', null,
|
|
'{{"paragraph": 1}}'::jsonb, '{"0" * 64}', 'Mismatched source hash.', 'supports', '{PROPOSAL_ID}'
|
|
);
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"evidence_requires_locator_and_rationale": expect_failure(
|
|
postgres,
|
|
f"""
|
|
insert into public.claim_evidence (
|
|
id, claim_id, source_id, role, weight, locator_json, source_content_hash,
|
|
rationale, polarity, accepted_from_proposal_id
|
|
) values (
|
|
'50000000-0000-4000-8000-000000000004', '{PRIMARY_CLAIM_ID}', '{SOURCE_ID}', 'grounds', null,
|
|
'{{}}'::jsonb, '{SOURCE_CONTENT_HASH}', '', 'supports', '{PROPOSAL_ID}'
|
|
);
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"null_polarity_cannot_bypass_evidence_contract": expect_failure(
|
|
postgres,
|
|
f"""
|
|
insert into public.claim_evidence (
|
|
id, claim_id, source_id, role, weight, locator_json, source_content_hash,
|
|
rationale, polarity, accepted_from_proposal_id
|
|
) values (
|
|
'50000000-0000-4000-8000-000000000009', '{PRIMARY_CLAIM_ID}', '{SOURCE_ID}', 'grounds', null,
|
|
'{{"paragraph": 1}}'::jsonb, '{SOURCE_CONTENT_HASH}', 'Null polarity must fail.', null, '{PROPOSAL_ID}'
|
|
);
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"generic_relates_edge_is_not_admitted": expect_failure(
|
|
postgres,
|
|
f"""
|
|
insert into public.claim_edges (
|
|
id, from_claim, to_claim, edge_type, weight, relation_type, rationale, accepted_by_proposal_id
|
|
) values (
|
|
'50000000-0000-4000-8000-000000000005', '{SUPPORT_CLAIM_ID}', '{PRIMARY_CLAIM_ID}',
|
|
'relates', null, 'relates', 'Generic relation must fail.', '{PROPOSAL_ID}'
|
|
);
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"null_relation_type_cannot_bypass_edge_contract": expect_failure(
|
|
postgres,
|
|
f"""
|
|
insert into public.claim_edges (
|
|
id, from_claim, to_claim, edge_type, weight, relation_type, rationale, accepted_by_proposal_id
|
|
) values (
|
|
'50000000-0000-4000-8000-000000000010', '{SUPPORT_CLAIM_ID}', '{PRIMARY_CLAIM_ID}',
|
|
'supports', null, null, 'Null relation type must fail.', '{PROPOSAL_ID}'
|
|
);
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"truth_apt_assessment_requires_both_tiers": expect_failure(
|
|
postgres,
|
|
f"""
|
|
insert into public.claim_evidence_assessments (
|
|
id, claim_id, support_tier, challenge_tier, overall_state, rationale,
|
|
evidence_set_hash, accepted_by_proposal_id
|
|
) values (
|
|
'50000000-0000-4000-8000-000000000006', '{PRIMARY_CLAIM_ID}', 'strong', null,
|
|
'support_dominant', 'Missing challenge tier must fail.', '{EVIDENCE_SET_HASH}', '{PROPOSAL_ID}'
|
|
);
|
|
""",
|
|
expected_sqlstate="23514",
|
|
),
|
|
"source_hash_is_immutable": expect_failure(
|
|
postgres,
|
|
f"update public.sources set content_hash = '{'f' * 64}' where id = '{SOURCE_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"source_locator_is_immutable": expect_failure(
|
|
postgres,
|
|
f"update public.sources set canonical_url = 'https://example.invalid/mutated' "
|
|
f"where id = '{SOURCE_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"source_classification_is_immutable": expect_failure(
|
|
postgres,
|
|
f"update public.sources set source_class = 'journalistic' where id = '{SOURCE_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"admitted_claim_semantics_are_immutable": expect_failure(
|
|
postgres,
|
|
f"update public.claims set body_markdown = 'Mutated body.' where id = '{PRIMARY_CLAIM_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"admitted_claim_status_is_immutable": expect_failure(
|
|
postgres,
|
|
f"update public.claims set status = 'retracted' where id = '{PRIMARY_CLAIM_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"admitted_claim_update_timestamp_is_immutable": expect_failure(
|
|
postgres,
|
|
f"update public.claims set updated_at = updated_at + interval '1 second' "
|
|
f"where id = '{PRIMARY_CLAIM_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"admitted_claim_cannot_be_deleted": expect_failure(
|
|
postgres,
|
|
f"delete from public.claims where id = '{PRIMARY_CLAIM_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"proposal_linked_source_cannot_be_deleted": expect_failure(
|
|
postgres,
|
|
f"delete from public.sources where id = '{SOURCE_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"proposal_linked_evidence_cannot_be_deleted": expect_failure(
|
|
postgres,
|
|
f"delete from public.claim_evidence where id = '{EVIDENCE_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"proposal_linked_edge_cannot_be_deleted": expect_failure(
|
|
postgres,
|
|
f"delete from public.claim_edges where id = '{EDGE_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"evidence_assessment_cannot_be_deleted": expect_failure(
|
|
postgres,
|
|
f"delete from public.claim_evidence_assessments where id = '{ASSESSMENT_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"linked_proposal_cannot_lose_accepted_decision": expect_failure(
|
|
postgres,
|
|
f"update kb_stage.kb_proposals set status = 'rejected' where id = '{PROPOSAL_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"linked_proposal_decision_metadata_is_immutable": expect_failure(
|
|
postgres,
|
|
f"update kb_stage.kb_proposals set review_note = 'Rewritten decision.' where id = '{PROPOSAL_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
}
|
|
return checks
|
|
|
|
|
|
def applied_receipt_adversarial_checks(postgres: DisposablePostgres) -> dict[str, Any]:
|
|
return {
|
|
"applied_proposal_status_is_immutable": expect_failure(
|
|
postgres,
|
|
f"update kb_stage.kb_proposals set status = 'approved' where id = '{PROPOSAL_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"applied_proposal_timestamp_is_immutable": expect_failure(
|
|
postgres,
|
|
f"update kb_stage.kb_proposals set applied_at = applied_at + interval '1 second' "
|
|
f"where id = '{PROPOSAL_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
"applied_proposal_applicator_is_immutable": expect_failure(
|
|
postgres,
|
|
f"update kb_stage.kb_proposals set applied_by_handle = 'rewritten-applicator' "
|
|
f"where id = '{PROPOSAL_ID}'::uuid;",
|
|
expected_sqlstate="55000",
|
|
),
|
|
}
|
|
|
|
|
|
def cleanup_canary_rows_sql() -> str:
|
|
return f"""
|
|
begin;
|
|
-- This verifier owns an isolated disposable database and runs as its superuser.
|
|
-- Production writers must never receive a role capable of disabling triggers.
|
|
set local session_replication_role = replica;
|
|
delete from public.claim_evidence_assessments where id = '{ASSESSMENT_ID}'::uuid;
|
|
delete from public.claim_edges where id = '{EDGE_ID}'::uuid;
|
|
delete from public.claim_evidence where id = '{EVIDENCE_ID}'::uuid;
|
|
delete from public.claims where id in ('{PRIMARY_CLAIM_ID}'::uuid, '{SUPPORT_CLAIM_ID}'::uuid);
|
|
delete from public.sources where id = '{SOURCE_ID}'::uuid;
|
|
delete from kb_stage.kb_proposals where id in (
|
|
'{PROPOSAL_ID}'::uuid,
|
|
'{PENDING_PROPOSAL_ID}'::uuid,
|
|
'{APPLIED_WITHOUT_RECEIPT_PROPOSAL_ID}'::uuid
|
|
);
|
|
commit;
|
|
"""
|
|
|
|
|
|
def build_checks(
|
|
receipt: dict[str, Any],
|
|
*,
|
|
first_catalog: dict[str, Any],
|
|
second_catalog: dict[str, Any],
|
|
reinstall_catalog: dict[str, Any],
|
|
rollback_catalog: dict[str, Any],
|
|
final_catalog: dict[str, Any],
|
|
baseline_legacy: dict[str, Any],
|
|
after_install_legacy: dict[str, Any],
|
|
after_cleanup_legacy: dict[str, Any],
|
|
after_rollback_legacy: dict[str, Any],
|
|
) -> dict[str, bool]:
|
|
round_trip = receipt["round_trip"]
|
|
claim = round_trip["claim"]
|
|
source = round_trip["source"]
|
|
evidence = round_trip["evidence"]
|
|
assessment = round_trip["assessment"]
|
|
edge = round_trip["edge"]
|
|
proposal = round_trip["proposal"]
|
|
linked_ids = {
|
|
claim["accepted_by_proposal_id"],
|
|
source["accepted_by_proposal_id"],
|
|
evidence["accepted_from_proposal_id"],
|
|
assessment["accepted_by_proposal_id"],
|
|
edge["accepted_by_proposal_id"],
|
|
}
|
|
adversarial = receipt["adversarial"]
|
|
accepted_proposal_gate = next(
|
|
(
|
|
function
|
|
for function in first_catalog["functions"]
|
|
if function["function_name"] == "teleo_v3_require_accepted_proposal" and function["arguments"] == ""
|
|
),
|
|
{},
|
|
)
|
|
cutover_gate = next(
|
|
(
|
|
function
|
|
for function in first_catalog["functions"]
|
|
if function["function_name"] == "teleo_v3_enforce_apply_contract_cutover" and function["arguments"] == ""
|
|
),
|
|
{},
|
|
)
|
|
cutover_trigger = next(
|
|
(
|
|
trigger
|
|
for trigger in first_catalog["triggers"]
|
|
if trigger["trigger_name"] == "teleo_v3_00_enforce_apply_contract_cutover"
|
|
),
|
|
{},
|
|
)
|
|
return {
|
|
"container_network_is_none": receipt["environment"]["network_mode"] == "none",
|
|
"container_data_is_tmpfs": receipt["environment"]["tmpfs_data_dir"] is True,
|
|
"container_has_no_volume_mounts": receipt["environment"]["docker_volume_mounts"] == [],
|
|
"canary_cleanup_bypass_is_disposable_superuser_only": receipt["canary_cleanup"]
|
|
== {
|
|
"trigger_bypass": "set local session_replication_role=replica",
|
|
"scope": "isolated-disposable-superuser-transaction",
|
|
},
|
|
"legacy_rows_unchanged_by_install": baseline_legacy == after_install_legacy,
|
|
"legacy_rows_unchanged_by_canary_cleanup": baseline_legacy == after_cleanup_legacy,
|
|
"legacy_rows_unchanged_by_rollback": baseline_legacy == after_rollback_legacy,
|
|
"migration_is_idempotent": first_catalog == second_catalog,
|
|
"security_definer_trigger_owner_and_acl_are_narrow": accepted_proposal_gate
|
|
== {
|
|
"schema_name": "kb_stage",
|
|
"function_name": "teleo_v3_require_accepted_proposal",
|
|
"arguments": "",
|
|
"owner_name": "kb_gate_owner",
|
|
"security_definer": True,
|
|
"config": ["search_path=pg_catalog, pg_temp"],
|
|
"source_sha256": "ca398ef2f185488c0712bd5ebc22402ab9e41e5dfd2f5823b367e6a382e8a64f",
|
|
"language_name": "plpgsql",
|
|
"function_kind": "f",
|
|
"argument_count": 0,
|
|
"return_type": "trigger",
|
|
"owner_only_execute": True,
|
|
"non_owner_execute_grantees": [],
|
|
},
|
|
"cutover_trigger_function_owner_and_acl_are_narrow": cutover_gate
|
|
== {
|
|
"schema_name": "kb_stage",
|
|
"function_name": "teleo_v3_enforce_apply_contract_cutover",
|
|
"arguments": "",
|
|
"owner_name": "kb_gate_owner",
|
|
"security_definer": True,
|
|
"config": ["search_path=pg_catalog, pg_temp"],
|
|
"source_sha256": "3ab2d5e1d5d1d18b13579fe2c0cae487036c7b147cf7f1b6b9a3633274756c18",
|
|
"language_name": "plpgsql",
|
|
"function_kind": "f",
|
|
"argument_count": 0,
|
|
"return_type": "trigger",
|
|
"owner_only_execute": True,
|
|
"non_owner_execute_grantees": [],
|
|
},
|
|
"full_trigger_catalog_is_exact": all(
|
|
trigger_catalog_is_exact(catalog) for catalog in (first_catalog, second_catalog, reinstall_catalog)
|
|
),
|
|
"cutover_trigger_is_enabled_and_exactly_bound": (
|
|
cutover_trigger.get("schema_name") == "kb_stage"
|
|
and cutover_trigger.get("table_name") == "kb_proposals"
|
|
and cutover_trigger.get("enabled_state") == "O"
|
|
and cutover_trigger.get("function_schema") == "kb_stage"
|
|
and cutover_trigger.get("function_name") == "teleo_v3_enforce_apply_contract_cutover"
|
|
and cutover_trigger.get("function_identity_arguments") == ""
|
|
),
|
|
"claim_contract_round_trip": claim
|
|
== {
|
|
"id": PRIMARY_CLAIM_ID,
|
|
"type": "empirical",
|
|
"text": PRIMARY_PROPOSITION,
|
|
"proposition": PRIMARY_PROPOSITION,
|
|
"body_markdown": PRIMARY_BODY,
|
|
"scope": PRIMARY_SCOPE,
|
|
"access_scope": "collective_internal",
|
|
"admission_state": "admitted",
|
|
"revision_criteria": PRIMARY_REVISION_CRITERIA,
|
|
"revision_kind": "semantic",
|
|
"supersedes_id": LEGACY_CLAIM_ID,
|
|
"accepted_by_proposal_id": PROPOSAL_ID,
|
|
},
|
|
"source_contract_round_trip": source["content_hash"] == SOURCE_CONTENT_HASH
|
|
and source["hash"] == SOURCE_CONTENT_HASH
|
|
and source["canonical_url"] == "https://example.invalid/livingip-v3-source"
|
|
and source["provenance_status"] == "verified"
|
|
and source["source_class"] == "primary_record",
|
|
"evidence_contract_round_trip": evidence["claim_id"] == PRIMARY_CLAIM_ID
|
|
and evidence["source_id"] == SOURCE_ID
|
|
and evidence["source_content_hash"] == SOURCE_CONTENT_HASH
|
|
and evidence["locator_json"] == {"paragraph": 1, "section": "epistemic-contract"}
|
|
and evidence["polarity"] == "supports"
|
|
and bool(evidence["rationale"]),
|
|
"assessment_contract_round_trip": assessment["claim_id"] == PRIMARY_CLAIM_ID
|
|
and assessment["support_tier"] == "strong"
|
|
and assessment["challenge_tier"] == "none"
|
|
and assessment["overall_state"] == "support_dominant"
|
|
and assessment["evidence_set_hash"] == EVIDENCE_SET_HASH,
|
|
"typed_edge_contract_round_trip": edge["from_claim"] == SUPPORT_CLAIM_ID
|
|
and edge["to_claim"] == PRIMARY_CLAIM_ID
|
|
and edge["relation_type"] == "supports"
|
|
and bool(edge["rationale"]),
|
|
"accepted_proposal_linkage_round_trip": linked_ids == {PROPOSAL_ID}
|
|
and proposal["status"] == "approved"
|
|
and proposal["reviewed_at"] is not None
|
|
and bool(proposal["review_note"]),
|
|
"accepted_proposal_lock_blocks_concurrent_downgrade": (
|
|
receipt["proposal_decision_lock_race"]["lock_observed"] is True
|
|
and receipt["proposal_decision_lock_race"]["concurrent_downgrade"]["refused"] is True
|
|
and receipt["proposal_decision_lock_race"]["concurrent_downgrade"]["expected_sqlstate_observed"] is True
|
|
and receipt["proposal_decision_lock_race"]["holder_returncode"] == 0
|
|
and receipt["proposal_decision_lock_race"]["proposal_status_after"] == "approved"
|
|
),
|
|
"all_adversarial_writes_refused": all(
|
|
check["refused"] and check["expected_sqlstate_observed"] for check in adversarial.values()
|
|
),
|
|
"approved_to_applied_transition_remains_compatible": (
|
|
receipt["proposal_applied_transition"]["status"] == "applied"
|
|
and receipt["proposal_applied_transition"]["review_note"] == proposal["review_note"]
|
|
and receipt["proposal_applied_transition"]["applied_at"] is not None
|
|
),
|
|
"rollback_refuses_v3_data_loss": receipt["rollback_with_data"]["refused"]
|
|
and receipt["rollback_with_data"]["expected_sqlstate_observed"],
|
|
"rollback_removes_contract": catalog_is_absent(rollback_catalog),
|
|
"reinstall_matches_original_contract": reinstall_catalog == first_catalog,
|
|
"final_rollback_removes_contract": catalog_is_absent(final_catalog),
|
|
"rollback_is_idempotent": receipt["idempotent_rollback_returncode"] == 0,
|
|
}
|
|
|
|
|
|
def write_receipt(output: Path, receipt: dict[str, Any]) -> None:
|
|
output.parent.mkdir(parents=True, exist_ok=True)
|
|
temporary = output.with_name(f".{output.name}.{uuid.uuid4().hex}.tmp")
|
|
temporary.write_text(json.dumps(receipt, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
os.replace(temporary, output)
|
|
|
|
|
|
def run_verification(output: Path) -> dict[str, Any]:
|
|
receipt: dict[str, Any] = {
|
|
"schema": RECEIPT_SCHEMA,
|
|
"status": "fail",
|
|
"required_tier": "T2_runtime",
|
|
"current_tier": "not_verified",
|
|
"production_access_attempted": False,
|
|
"production_migration_shipped": False,
|
|
"database_target_mode": "internally-created-disposable-container-only",
|
|
"migration_files": {
|
|
"install": str(MIGRATION_SQL.relative_to(REPO_ROOT)),
|
|
"rollback": str(ROLLBACK_SQL.relative_to(REPO_ROOT)),
|
|
"install_sha256": hashlib.sha256(MIGRATION_SQL.read_bytes()).hexdigest(),
|
|
"rollback_sha256": hashlib.sha256(ROLLBACK_SQL.read_bytes()).hexdigest(),
|
|
},
|
|
"not_shipped": [
|
|
"production migration execution",
|
|
"live apply-worker wiring",
|
|
"full V3 proposal/evaluation/identity architecture",
|
|
],
|
|
}
|
|
postgres = DisposablePostgres()
|
|
error: str | None = None
|
|
try:
|
|
receipt["environment"] = postgres.start()
|
|
postgres.psql(BOOTSTRAP_SQL)
|
|
baseline_legacy = postgres.psql_json(LEGACY_STATE_SQL)
|
|
receipt["legacy_baseline"] = baseline_legacy
|
|
|
|
first_install = postgres.apply_file(MIGRATION_SQL)
|
|
first_catalog = catalog_readback(postgres)
|
|
after_install_legacy = postgres.psql_json(LEGACY_STATE_SQL)
|
|
receipt["install_first"] = {
|
|
"returncode": first_install.returncode,
|
|
"catalog_sha256": canonical_sha256(first_catalog),
|
|
"catalog": first_catalog,
|
|
}
|
|
|
|
second_install = postgres.apply_file(MIGRATION_SQL)
|
|
second_catalog = catalog_readback(postgres)
|
|
receipt["install_second"] = {
|
|
"returncode": second_install.returncode,
|
|
"catalog_sha256": canonical_sha256(second_catalog),
|
|
}
|
|
|
|
postgres.psql(accepted_rows_sql())
|
|
receipt["round_trip"] = postgres.psql_json(round_trip_sql())
|
|
receipt["proposal_decision_lock_race"] = proposal_decision_lock_race_check(postgres)
|
|
receipt["adversarial"] = adversarial_checks(postgres)
|
|
postgres.psql(
|
|
f"""
|
|
update kb_stage.kb_proposals
|
|
set status = 'applied',
|
|
applied_by_handle = 'v3-contract-apply',
|
|
applied_at = '2026-07-18T09:08:00Z',
|
|
updated_at = '2026-07-18T09:08:00Z'
|
|
where id = '{PROPOSAL_ID}'::uuid;
|
|
"""
|
|
)
|
|
receipt["proposal_applied_transition"] = postgres.psql_json(
|
|
f"""
|
|
select jsonb_build_object(
|
|
'id', id,
|
|
'status', status,
|
|
'review_note', review_note,
|
|
'applied_by_handle', applied_by_handle,
|
|
'applied_at', applied_at
|
|
)::text
|
|
from kb_stage.kb_proposals
|
|
where id = '{PROPOSAL_ID}'::uuid;
|
|
"""
|
|
)
|
|
receipt["adversarial"].update(applied_receipt_adversarial_checks(postgres))
|
|
|
|
rollback_with_data = postgres.apply_file(ROLLBACK_SQL, check=False)
|
|
receipt["rollback_with_data"] = failure_receipt(rollback_with_data, "55000")
|
|
if not (
|
|
receipt["rollback_with_data"]["refused"] and receipt["rollback_with_data"]["expected_sqlstate_observed"]
|
|
):
|
|
raise VerificationError("rollback did not refuse proposal-linked V3 data")
|
|
|
|
receipt["canary_cleanup"] = {
|
|
"trigger_bypass": "set local session_replication_role=replica",
|
|
"scope": "isolated-disposable-superuser-transaction",
|
|
}
|
|
postgres.psql(cleanup_canary_rows_sql())
|
|
after_cleanup_legacy = postgres.psql_json(LEGACY_STATE_SQL)
|
|
|
|
rollback = postgres.apply_file(ROLLBACK_SQL)
|
|
rollback_catalog = catalog_readback(postgres)
|
|
after_rollback_legacy = postgres.psql_json(LEGACY_STATE_SQL)
|
|
receipt["rollback"] = {
|
|
"returncode": rollback.returncode,
|
|
"catalog": rollback_catalog,
|
|
"legacy_sha256": canonical_sha256(after_rollback_legacy),
|
|
}
|
|
|
|
idempotent_rollback = postgres.apply_file(ROLLBACK_SQL)
|
|
receipt["idempotent_rollback_returncode"] = idempotent_rollback.returncode
|
|
|
|
reinstall = postgres.apply_file(MIGRATION_SQL)
|
|
reinstall_catalog = catalog_readback(postgres)
|
|
receipt["reinstall"] = {
|
|
"returncode": reinstall.returncode,
|
|
"catalog_sha256": canonical_sha256(reinstall_catalog),
|
|
}
|
|
|
|
final_rollback = postgres.apply_file(ROLLBACK_SQL)
|
|
final_catalog = catalog_readback(postgres)
|
|
receipt["final_rollback"] = {
|
|
"returncode": final_rollback.returncode,
|
|
"catalog": final_catalog,
|
|
}
|
|
|
|
receipt["checks"] = build_checks(
|
|
receipt,
|
|
first_catalog=first_catalog,
|
|
second_catalog=second_catalog,
|
|
reinstall_catalog=reinstall_catalog,
|
|
rollback_catalog=rollback_catalog,
|
|
final_catalog=final_catalog,
|
|
baseline_legacy=baseline_legacy,
|
|
after_install_legacy=after_install_legacy,
|
|
after_cleanup_legacy=after_cleanup_legacy,
|
|
after_rollback_legacy=after_rollback_legacy,
|
|
)
|
|
except Exception as exc: # retain a bounded failure receipt before returning non-zero
|
|
error = safe_error_text(str(exc))
|
|
receipt["error"] = error
|
|
finally:
|
|
cleanup = postgres.cleanup()
|
|
receipt["cleanup"] = cleanup
|
|
cleanup_ok = (
|
|
cleanup["container_absent"]
|
|
and cleanup["name_readback_clear"]
|
|
and cleanup["instance_label_readback_clear"]
|
|
and cleanup["stable_absence_readback"]
|
|
and cleanup["network_mode_was_none"]
|
|
and cleanup["tmpfs_data_dir_was_present"]
|
|
and cleanup["docker_volume_mounts_observed"] == []
|
|
)
|
|
receipt.setdefault("checks", {})["disposable_container_cleanup_verified"] = cleanup_ok
|
|
all_checks_pass = bool(receipt["checks"]) and all(receipt["checks"].values())
|
|
if error is None and all_checks_pass:
|
|
receipt["status"] = "pass"
|
|
receipt["current_tier"] = "T2_runtime_disposable_postgresql"
|
|
receipt["strongest_claim"] = (
|
|
"The additive minimum V3 epistemic contract installed, round-tripped accepted provenance, "
|
|
"rejected adversarial writes, rolled back, reinstalled, and cleaned up in networkless disposable PostgreSQL."
|
|
if receipt["status"] == "pass"
|
|
else "The disposable V3 epistemic contract verification did not pass every runtime check."
|
|
)
|
|
write_receipt(output, receipt)
|
|
return receipt
|
|
|
|
|
|
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--output", type=Path, required=True, help="retained JSON receipt path")
|
|
return parser.parse_args(argv)
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
args = parse_args(argv)
|
|
receipt = run_verification(args.output)
|
|
print(
|
|
json.dumps(
|
|
{
|
|
"status": receipt["status"],
|
|
"current_tier": receipt["current_tier"],
|
|
"receipt": str(args.output.resolve()),
|
|
"checks_passed": sum(1 for value in receipt.get("checks", {}).values() if value),
|
|
"checks_total": len(receipt.get("checks", {})),
|
|
"cleanup": receipt.get("checks", {}).get("disposable_container_cleanup_verified"),
|
|
"production_access_attempted": receipt["production_access_attempted"],
|
|
},
|
|
sort_keys=True,
|
|
)
|
|
)
|
|
return 0 if receipt["status"] == "pass" else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|