Add deterministic genesis plus ledger rebuild
This commit is contained in:
parent
4088c9c8f5
commit
1a6b7d51a4
3 changed files with 2205 additions and 7 deletions
|
|
@ -141,9 +141,89 @@ Normal applies mark the SQL hash as `exact_executed_sql`. A later
|
|||
pretends the current engine hash is historical proof of the originally executed
|
||||
program.
|
||||
|
||||
This closes replay-receipt loss for new strict applies. It does not reconstruct
|
||||
legacy freeform applies, and it does not yet execute the complete
|
||||
genesis-plus-ledger blank-database replay.
|
||||
This closes replay-receipt loss for new strict applies. The receipt alone does
|
||||
not retain every column of the proposal ledger, so exact reconstruction also
|
||||
needs the full approved proposal row, immutable approval snapshot, and final
|
||||
applied proposal row.
|
||||
|
||||
## Genesis Plus Strict Ledger: Working Insert-Only Slice
|
||||
|
||||
`ops/run_local_genesis_ledger_rebuild.py` now executes the first exact
|
||||
genesis-plus-ledger slice in one command:
|
||||
|
||||
```bash
|
||||
.venv/bin/python ops/run_local_genesis_ledger_rebuild.py \
|
||||
--genesis-dump /private/path/genesis.dump \
|
||||
--genesis-manifest /private/path/genesis-manifest.jsonl \
|
||||
--ledger /private/path/ledger.json \
|
||||
--ledger-sha256 "$LEDGER_SHA256" \
|
||||
--output /tmp/genesis-ledger-rebuild-receipt.json
|
||||
```
|
||||
|
||||
The v1 ledger pins the genesis dump and manifest, final parity manifest,
|
||||
reconstruction/restore/guard/apply/replay/parity engines, and every ordered
|
||||
private material file. Each material file contains one existing `kb_apply_replay_receipt`, the
|
||||
exact full proposal row immediately before apply, its immutable
|
||||
`kb_proposal_approvals` row, and the exact full proposal row after apply. These
|
||||
files can contain claim text or source excerpts and must remain private.
|
||||
|
||||
The ledger shape is:
|
||||
|
||||
```json
|
||||
{
|
||||
"artifact": "teleo_genesis_plus_ledger",
|
||||
"contract_version": 1,
|
||||
"engine": {
|
||||
"reconstruction_command_sha256": "<sha256>",
|
||||
"base_rebuild_engine_sha256": "<sha256>",
|
||||
"apply_engine_sha256": "<sha256>",
|
||||
"replay_receipt_engine_sha256": "<sha256>",
|
||||
"guard_prerequisites_sha256": "<sha256>",
|
||||
"parity_sql_sha256": "<sha256>"
|
||||
},
|
||||
"genesis": {
|
||||
"dump_sha256": "<sha256>",
|
||||
"parity_manifest_sha256": "<sha256>"
|
||||
},
|
||||
"entries": [{
|
||||
"sequence": 1,
|
||||
"material": "private/0001.json",
|
||||
"sha256": "<material-file-sha256>",
|
||||
"replay_material_sha256": "<receipt-replay-material-sha256>"
|
||||
}],
|
||||
"final_parity": {
|
||||
"manifest": "final-manifest.jsonl",
|
||||
"sha256": "<sha256>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Each referenced material object has exact top-level keys
|
||||
`artifact`, `contract_version`, `sequence`, `approved_proposal`,
|
||||
`approval_snapshot`, `applied_proposal`, and `replay_receipt`. Proposal objects
|
||||
must contain every current `kb_stage.kb_proposals` column; partial envelopes
|
||||
are rejected.
|
||||
|
||||
The command verifies every hash before starting Docker, restores a tmpfs-backed
|
||||
Postgres with `--network none`, reapplies the current guarded prerequisites,
|
||||
and proves genesis parity. For each entry it seeds the receipt's exact row IDs
|
||||
and timestamps in the disposable clone, executes the existing `kb_apply`
|
||||
payload-bound guard and ledger transition, checks exact proposal and canonical
|
||||
row readbacks, then verifies the complete final parity manifest. Its public
|
||||
mode-`0600` receipt contains hashes, IDs, types, counts, parity summaries, and
|
||||
cleanup proof, but no payloads, rows, SQL, source paths, or command errors.
|
||||
|
||||
The exact v1 claim ceiling is intentionally narrow:
|
||||
|
||||
- `add_edge`, `attach_evidence`, and `approve_claim` strict receipts execute;
|
||||
- sequence gaps, hash drift, engine drift, duplicate proposals, and legacy or
|
||||
freeform payloads fail before container start;
|
||||
- `revise_strategy` fails closed because its current receipt omits the prior
|
||||
strategies and nodes that the apply engine deactivates or retires;
|
||||
- full proposal before/after rows are mandatory because the current receipt
|
||||
envelope does not retain proposal origin fields or exact `updated_at`;
|
||||
- this proves only an isolated local reconstruction. It does not touch or prove
|
||||
VPS, GCP, Telegram, a live database, or blank-schema source recompilation.
|
||||
|
||||
The source compiler now turns one raw artifact, its strict UTF-8 extraction,
|
||||
and a reviewed extraction manifest into a deterministic, hash-bound
|
||||
|
|
@ -190,10 +270,11 @@ neither command applies canonical rows.
|
|||
|
||||
The existing full-data clone canary separately proves that a reviewed packet
|
||||
can create source, claim, evidence, and edge rows and affect later reasoning.
|
||||
The remaining recompilation capability is to join genesis, accepted packets,
|
||||
and their row-level receipts into a complete corpus runner. One-document
|
||||
preparation, staging, and receipt capture do not yet prove a clean database can
|
||||
be rebuilt semantically from every source.
|
||||
The remaining reconstruction work is to retain complete deltas for mutating
|
||||
contracts such as `revise_strategy`, backfill or explicitly reject legacy
|
||||
freeform applies, and extend beyond genesis recovery to a blank-schema source
|
||||
compiler. The insert-only ledger runner does not prove that every historical
|
||||
canonical row can be rebuilt semantically from retained sources.
|
||||
|
||||
## Definition Of Working
|
||||
|
||||
|
|
|
|||
1284
ops/run_local_genesis_ledger_rebuild.py
Normal file
1284
ops/run_local_genesis_ledger_rebuild.py
Normal file
File diff suppressed because it is too large
Load diff
833
tests/test_run_local_genesis_ledger_rebuild.py
Normal file
833
tests/test_run_local_genesis_ledger_rebuild.py
Normal file
|
|
@ -0,0 +1,833 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import copy
|
||||
import hashlib
|
||||
import json
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
from collections.abc import Iterator
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from ops import run_local_genesis_ledger_rebuild as rebuild
|
||||
|
||||
CLAIM_A = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"
|
||||
CLAIM_B = "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"
|
||||
REVIEWER_ID = "11111111-1111-4111-8111-111111111111"
|
||||
SERVICE_ID = "44444444-4444-4444-4444-444444444444"
|
||||
PROPOSAL_ID = "99999999-9999-4999-8999-999999999999"
|
||||
EDGE_ID = "eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee"
|
||||
PRIVATE_MARKER = "PRIVATE-REPLAY-MATERIAL-MUST-NOT-LEAK"
|
||||
|
||||
|
||||
def sha256_file(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
digest.update(path.read_bytes())
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def write_json(path: Path, value: object) -> Path:
|
||||
path.write_text(json.dumps(value, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
return path
|
||||
|
||||
|
||||
def manifest_rows(database: str = "teleo_fixture") -> list[dict]:
|
||||
role = {
|
||||
"name": "kb_apply",
|
||||
"can_login": True,
|
||||
"superuser": False,
|
||||
"create_db": False,
|
||||
"create_role": False,
|
||||
"inherit": False,
|
||||
"replication": False,
|
||||
"bypass_rls": False,
|
||||
}
|
||||
rows = [
|
||||
{
|
||||
"kind": "identity",
|
||||
"database": database,
|
||||
"server_version_num": 160010,
|
||||
"transaction_read_only": "on",
|
||||
"server_address": None,
|
||||
"server_port": None,
|
||||
"ssl": False,
|
||||
"ssl_version": None,
|
||||
"database_collation": "C",
|
||||
"database_ctype": "C",
|
||||
},
|
||||
{"kind": "schemas", "items": ["kb_stage", "public"]},
|
||||
{"kind": "extensions", "items": []},
|
||||
{"kind": "application_roles", "items": [role]},
|
||||
]
|
||||
rows.extend(
|
||||
{"kind": kind, "items": []}
|
||||
for kind in (
|
||||
"columns",
|
||||
"constraints",
|
||||
"indexes",
|
||||
"sequences",
|
||||
"views",
|
||||
"functions",
|
||||
"triggers",
|
||||
"types",
|
||||
"policies",
|
||||
)
|
||||
)
|
||||
rows.extend(
|
||||
[
|
||||
{
|
||||
"kind": "table",
|
||||
"schema": "public",
|
||||
"table": "claims",
|
||||
"row_count": 2,
|
||||
"rowset_md5": "fixture",
|
||||
},
|
||||
{
|
||||
"kind": "performance",
|
||||
"query": "count_claims",
|
||||
"elapsed_ms": 1.0,
|
||||
"observed_rows": 2,
|
||||
},
|
||||
]
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def write_manifest(path: Path) -> Path:
|
||||
path.write_text("".join(json.dumps(row) + "\n" for row in manifest_rows()), encoding="utf-8")
|
||||
return path
|
||||
|
||||
|
||||
def proposal_rows() -> tuple[dict, dict, dict]:
|
||||
payload = {
|
||||
"apply_payload": {
|
||||
"from_claim": CLAIM_A,
|
||||
"to_claim": CLAIM_B,
|
||||
"edge_type": "supports",
|
||||
"weight": 0.8,
|
||||
},
|
||||
"private_title": PRIVATE_MARKER,
|
||||
}
|
||||
approved = {
|
||||
"id": PROPOSAL_ID,
|
||||
"proposal_type": "add_edge",
|
||||
"status": "approved",
|
||||
"proposed_by_handle": "fixture-agent",
|
||||
"proposed_by_agent_id": REVIEWER_ID,
|
||||
"channel": "reconstruction_fixture",
|
||||
"source_ref": "private://fixture/source",
|
||||
"rationale": PRIVATE_MARKER,
|
||||
"payload": payload,
|
||||
"reviewed_by_handle": "m3ta",
|
||||
"reviewed_by_agent_id": REVIEWER_ID,
|
||||
"reviewed_at": "2026-07-14T01:00:00+00:00",
|
||||
"review_note": "Reviewed exact strict fixture payload.",
|
||||
"applied_by_handle": None,
|
||||
"applied_by_agent_id": None,
|
||||
"applied_at": None,
|
||||
"created_at": "2026-07-14T00:59:00+00:00",
|
||||
"updated_at": "2026-07-14T01:00:00+00:00",
|
||||
}
|
||||
applied = {
|
||||
**approved,
|
||||
"status": "applied",
|
||||
"applied_by_handle": "kb-apply",
|
||||
"applied_by_agent_id": SERVICE_ID,
|
||||
"applied_at": "2026-07-14T01:01:00+00:00",
|
||||
"updated_at": "2026-07-14T01:01:00.000001+00:00",
|
||||
}
|
||||
approval = {
|
||||
"proposal_id": PROPOSAL_ID,
|
||||
"proposal_type": "add_edge",
|
||||
"payload": payload,
|
||||
"reviewed_by_handle": "m3ta",
|
||||
"reviewed_by_agent_id": REVIEWER_ID,
|
||||
"reviewed_by_db_role": "kb_review",
|
||||
"reviewed_at": "2026-07-14T01:00:00+00:00",
|
||||
"review_note": "Reviewed exact strict fixture payload.",
|
||||
}
|
||||
return approved, applied, approval
|
||||
|
||||
|
||||
def applied_envelope(applied: dict) -> dict:
|
||||
fields = (
|
||||
"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",
|
||||
)
|
||||
return {field: applied[field] for field in fields}
|
||||
|
||||
|
||||
def replay_material() -> dict:
|
||||
approved, applied, approval = proposal_rows()
|
||||
proposal = applied_envelope(applied)
|
||||
edge = {
|
||||
"id": EDGE_ID,
|
||||
"from_claim": CLAIM_A,
|
||||
"to_claim": CLAIM_B,
|
||||
"edge_type": "supports",
|
||||
"weight": 0.8,
|
||||
"created_by": None,
|
||||
"created_at": "2026-07-14T01:00:30+00:00",
|
||||
}
|
||||
receipt = rebuild.replay_receipt.build_replay_receipt(
|
||||
proposal,
|
||||
{"claim_edges": [edge]},
|
||||
apply_sql_sha256=rebuild.replay_receipt.sha256_text("fixture original guarded apply SQL"),
|
||||
apply_sql_source="exact_executed_sql",
|
||||
exported_at_utc="2026-07-14T01:02:00+00:00",
|
||||
)
|
||||
return {
|
||||
"artifact": rebuild.MATERIAL_ARTIFACT,
|
||||
"contract_version": rebuild.MATERIAL_CONTRACT_VERSION,
|
||||
"sequence": 1,
|
||||
"approved_proposal": approved,
|
||||
"approval_snapshot": approval,
|
||||
"applied_proposal": applied,
|
||||
"replay_receipt": receipt,
|
||||
}
|
||||
|
||||
|
||||
def write_bundle(tmp_path: Path, *, material: dict | None = None) -> argparse.Namespace:
|
||||
dump = tmp_path / "genesis.dump"
|
||||
dump.write_bytes(b"PGDMPfixture")
|
||||
genesis_manifest = write_manifest(tmp_path / "genesis-manifest.jsonl")
|
||||
final_manifest = write_manifest(tmp_path / "final-manifest.jsonl")
|
||||
material = copy.deepcopy(material or replay_material())
|
||||
material_path = write_json(tmp_path / "entry-0001.json", material)
|
||||
receipt_hash = material["replay_receipt"]["hashes"]["replay_material_sha256"]
|
||||
ledger = {
|
||||
"artifact": rebuild.LEDGER_ARTIFACT,
|
||||
"contract_version": rebuild.LEDGER_CONTRACT_VERSION,
|
||||
"engine": rebuild._engine_hashes(),
|
||||
"genesis": {
|
||||
"dump_sha256": sha256_file(dump),
|
||||
"parity_manifest_sha256": sha256_file(genesis_manifest),
|
||||
},
|
||||
"entries": [
|
||||
{
|
||||
"sequence": 1,
|
||||
"material": material_path.name,
|
||||
"sha256": sha256_file(material_path),
|
||||
"replay_material_sha256": receipt_hash,
|
||||
}
|
||||
],
|
||||
"final_parity": {
|
||||
"manifest": final_manifest.name,
|
||||
"sha256": sha256_file(final_manifest),
|
||||
},
|
||||
}
|
||||
ledger_path = write_json(tmp_path / "ledger.json", ledger)
|
||||
return argparse.Namespace(
|
||||
genesis_dump=dump,
|
||||
genesis_manifest=genesis_manifest,
|
||||
ledger=ledger_path,
|
||||
ledger_sha256=sha256_file(ledger_path),
|
||||
database="teleo",
|
||||
image=rebuild.canonical_rebuild.DEFAULT_IMAGE,
|
||||
container_prefix="teleo-genesis-ledger-test",
|
||||
tmpfs_mb=256,
|
||||
startup_timeout=30.0,
|
||||
restore_timeout=60.0,
|
||||
apply_timeout=60.0,
|
||||
manifest_timeout=60.0,
|
||||
command_timeout=20.0,
|
||||
poll_interval=0.05,
|
||||
max_target_query_ms=5000.0,
|
||||
max_target_source_ratio=100.0,
|
||||
docker_bin="docker",
|
||||
output=tmp_path / "receipt.json",
|
||||
)
|
||||
|
||||
|
||||
def rewrite_material_and_ledger(args: argparse.Namespace, material: dict) -> None:
|
||||
material_path = args.ledger.parent / "entry-0001.json"
|
||||
write_json(material_path, material)
|
||||
ledger = json.loads(args.ledger.read_text(encoding="utf-8"))
|
||||
ledger["entries"][0]["sha256"] = sha256_file(material_path)
|
||||
write_json(args.ledger, ledger)
|
||||
args.ledger_sha256 = sha256_file(args.ledger)
|
||||
|
||||
|
||||
def test_load_bundle_validates_every_pin_and_current_apply_engine(tmp_path: Path) -> None:
|
||||
args = write_bundle(tmp_path)
|
||||
|
||||
bundle = rebuild.load_bundle(args)
|
||||
|
||||
assert bundle.genesis_dump_sha256 == sha256_file(args.genesis_dump)
|
||||
assert bundle.genesis_manifest_sha256 == sha256_file(args.genesis_manifest)
|
||||
assert bundle.final_manifest_sha256 == sha256_file(tmp_path / "final-manifest.jsonl")
|
||||
assert bundle.engine_hashes == rebuild._engine_hashes()
|
||||
assert len(bundle.entries) == 1
|
||||
entry = bundle.entries[0]
|
||||
assert entry.applied_proposal["proposal_type"] == "add_edge"
|
||||
assert entry.current_apply_sql_sha256 == hashlib.sha256(entry.current_apply_sql.encode("utf-8")).hexdigest()
|
||||
assert "kb_stage.assert_approved_proposal" in entry.current_apply_sql
|
||||
|
||||
|
||||
def test_dump_hash_mismatch_fails_before_container_start(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
args = write_bundle(tmp_path)
|
||||
args.genesis_dump.write_bytes(b"PGDMPtampered")
|
||||
commands: list[list[str]] = []
|
||||
|
||||
def absent_docker(command: list[str], *, timeout: float):
|
||||
commands.append(command)
|
||||
if command[1:3] == ["rm", "--force"]:
|
||||
return subprocess.CompletedProcess(command, 1, "", "No such container")
|
||||
if command[1:3] == ["container", "inspect"]:
|
||||
return subprocess.CompletedProcess(command, 1, "", "No such object")
|
||||
raise AssertionError(f"unexpected command after failed preflight: {command}")
|
||||
|
||||
monkeypatch.setattr(rebuild.canonical_rebuild, "_run", absent_docker)
|
||||
|
||||
receipt = rebuild.run_reconstruction(args)
|
||||
|
||||
assert receipt["status"] == "fail"
|
||||
assert receipt["error"]["code"] == "hash_pin_mismatch"
|
||||
assert receipt["cleanup"]["container_absent"] is True
|
||||
assert not any(len(command) > 1 and command[1] == "run" for command in commands)
|
||||
|
||||
|
||||
def test_revise_strategy_receipt_fails_closed_before_receipt_rows_are_used(tmp_path: Path) -> None:
|
||||
material = replay_material()
|
||||
material["replay_receipt"]["proposal"]["proposal_type"] = "revise_strategy"
|
||||
material["applied_proposal"]["proposal_type"] = "revise_strategy"
|
||||
material["approved_proposal"]["proposal_type"] = "revise_strategy"
|
||||
material["approval_snapshot"]["proposal_type"] = "revise_strategy"
|
||||
args = write_bundle(tmp_path, material=material)
|
||||
|
||||
with pytest.raises(rebuild.ReconstructionError, match="prior strategy") as exc_info:
|
||||
rebuild.load_bundle(args)
|
||||
|
||||
assert exc_info.value.code == "unsupported_mutating_receipt"
|
||||
|
||||
|
||||
def test_legacy_freeform_receipt_fails_closed(tmp_path: Path) -> None:
|
||||
material = replay_material()
|
||||
material["replay_receipt"]["proposal"]["payload"] = {"legacy": PRIVATE_MARKER}
|
||||
material["approved_proposal"]["payload"] = {"legacy": PRIVATE_MARKER}
|
||||
material["applied_proposal"]["payload"] = {"legacy": PRIVATE_MARKER}
|
||||
material["approval_snapshot"]["payload"] = {"legacy": PRIVATE_MARKER}
|
||||
args = write_bundle(tmp_path, material=material)
|
||||
|
||||
with pytest.raises(rebuild.ReconstructionError) as exc_info:
|
||||
rebuild.load_bundle(args)
|
||||
|
||||
assert exc_info.value.code == "invalid_replay_receipt"
|
||||
assert PRIVATE_MARKER not in exc_info.value.safe_message
|
||||
|
||||
|
||||
def test_proposal_metadata_outside_apply_transition_is_rejected(tmp_path: Path) -> None:
|
||||
material = replay_material()
|
||||
material["applied_proposal"]["rationale"] = "silently changed after review"
|
||||
args = write_bundle(tmp_path)
|
||||
rewrite_material_and_ledger(args, material)
|
||||
|
||||
with pytest.raises(rebuild.ReconstructionError) as exc_info:
|
||||
rebuild.load_bundle(args)
|
||||
|
||||
assert exc_info.value.code == "material_mismatch"
|
||||
assert "rationale" in exc_info.value.safe_message
|
||||
|
||||
|
||||
def test_output_cannot_overwrite_a_fixed_guard_or_engine_file(tmp_path: Path) -> None:
|
||||
args = write_bundle(tmp_path)
|
||||
args.output = rebuild.GUARD_PREREQUISITES_PATH
|
||||
|
||||
with pytest.raises(rebuild.ReconstructionError) as exc_info:
|
||||
rebuild.load_bundle(args)
|
||||
|
||||
assert exc_info.value.code == "unsafe_output_path"
|
||||
|
||||
|
||||
def test_unknown_private_field_name_is_not_echoed_in_public_error(tmp_path: Path) -> None:
|
||||
material = replay_material()
|
||||
material[PRIVATE_MARKER] = "private value"
|
||||
args = write_bundle(tmp_path, material=material)
|
||||
|
||||
with pytest.raises(rebuild.ReconstructionError) as exc_info:
|
||||
rebuild.load_bundle(args)
|
||||
|
||||
assert exc_info.value.code == "invalid_contract"
|
||||
assert PRIVATE_MARKER not in exc_info.value.safe_message
|
||||
|
||||
|
||||
def test_seed_sql_preserves_exact_rows_and_uses_only_fixed_tables(tmp_path: Path) -> None:
|
||||
entry = rebuild.load_bundle(write_bundle(tmp_path)).entries[0]
|
||||
|
||||
sql = rebuild.build_seed_sql(entry)
|
||||
|
||||
assert "jsonb_populate_record" in sql
|
||||
assert "kb_stage.kb_proposals" in sql
|
||||
assert "kb_stage.kb_proposal_approvals" in sql
|
||||
assert "public.claim_edges" in sql
|
||||
assert "on conflict do nothing" in sql
|
||||
assert PRIVATE_MARKER in sql
|
||||
assert "public.strategies" not in sql
|
||||
|
||||
|
||||
def test_private_psql_failure_output_is_replaced_with_a_fingerprint(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
captured: dict[str, object] = {}
|
||||
|
||||
def fail(command: list[str], *, stdin: str, timeout: float):
|
||||
captured.update(command=command, stdin=stdin, timeout=timeout)
|
||||
return subprocess.CompletedProcess(command, 1, "", f"ERROR: {PRIVATE_MARKER}")
|
||||
|
||||
monkeypatch.setattr(rebuild, "_run_stdin", fail)
|
||||
args = argparse.Namespace(docker_bin="docker", database="teleo")
|
||||
|
||||
with pytest.raises(rebuild.ReconstructionError) as exc_info:
|
||||
rebuild._psql(
|
||||
args,
|
||||
"fixture-container",
|
||||
f"select '{PRIVATE_MARKER}';",
|
||||
role="postgres",
|
||||
timeout=3.0,
|
||||
code="fixture_failure",
|
||||
action="private fixture SQL",
|
||||
)
|
||||
|
||||
assert PRIVATE_MARKER not in exc_info.value.safe_message
|
||||
assert "diagnostic_sha256=" in exc_info.value.safe_message
|
||||
assert PRIVATE_MARKER not in " ".join(captured["command"])
|
||||
assert PRIVATE_MARKER in captured["stdin"]
|
||||
|
||||
|
||||
def test_entry_summary_is_secret_safe(tmp_path: Path) -> None:
|
||||
entry = rebuild.load_bundle(write_bundle(tmp_path)).entries[0]
|
||||
|
||||
encoded = json.dumps(rebuild._entry_summary(entry), sort_keys=True)
|
||||
|
||||
assert PRIVATE_MARKER not in encoded
|
||||
assert entry.applied_proposal["id"] in encoded
|
||||
assert entry.replay_material_sha256 in encoded
|
||||
|
||||
|
||||
BOOTSTRAP_SQL = f"""
|
||||
create role kb_apply login noinherit;
|
||||
create role kb_review login noinherit;
|
||||
create schema kb_stage;
|
||||
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
|
||||
);
|
||||
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,
|
||||
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 public.reasoning_tools (
|
||||
id uuid primary key,
|
||||
agent_id uuid references public.agents(id),
|
||||
name text not null,
|
||||
description text not null,
|
||||
category text,
|
||||
created_at timestamptz not null default now()
|
||||
);
|
||||
create table public.strategies (
|
||||
id uuid primary key,
|
||||
agent_id uuid not null references public.agents(id),
|
||||
diagnosis text,
|
||||
guiding_policy text,
|
||||
proximate_objectives jsonb,
|
||||
version integer not null default 1,
|
||||
active boolean not null default true,
|
||||
created_at timestamptz not null default now()
|
||||
);
|
||||
create table public.strategy_nodes (
|
||||
id uuid primary key,
|
||||
agent_id uuid references public.agents(id),
|
||||
node_type text,
|
||||
title text,
|
||||
body text,
|
||||
rank integer,
|
||||
status text not null default 'active',
|
||||
horizon text,
|
||||
measure text,
|
||||
source_ref text,
|
||||
metadata jsonb not null default '{{}}',
|
||||
created_at timestamptz not null default now(),
|
||||
updated_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()
|
||||
);
|
||||
|
||||
insert into public.agents (id, handle, kind) values
|
||||
('{REVIEWER_ID}', 'm3ta', 'human');
|
||||
insert into public.claims (id, type, text, status, confidence, tags, created_by) values
|
||||
('{CLAIM_A}', 'structural', 'Fixture claim A', 'open', 0.8, array['fixture'], '{REVIEWER_ID}'),
|
||||
('{CLAIM_B}', 'structural', 'Fixture claim B', 'open', 0.8, array['fixture'], '{REVIEWER_ID}');
|
||||
"""
|
||||
|
||||
|
||||
def docker_psql(
|
||||
container: str,
|
||||
database: str,
|
||||
sql: str,
|
||||
*,
|
||||
role: str = "postgres",
|
||||
check: bool = True,
|
||||
) -> subprocess.CompletedProcess[str]:
|
||||
completed = subprocess.run(
|
||||
[
|
||||
"docker",
|
||||
"exec",
|
||||
"-i",
|
||||
container,
|
||||
"psql",
|
||||
"-X",
|
||||
"-U",
|
||||
role,
|
||||
"-h",
|
||||
"127.0.0.1",
|
||||
"-d",
|
||||
database,
|
||||
"-Atq",
|
||||
"-v",
|
||||
"ON_ERROR_STOP=1",
|
||||
],
|
||||
input=sql,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
if check and completed.returncode != 0:
|
||||
pytest.fail(
|
||||
f"fixture psql failed ({completed.returncode})\nstdout:\n{completed.stdout}\nstderr:\n{completed.stderr}"
|
||||
)
|
||||
return completed
|
||||
|
||||
|
||||
def wait_for_postgres(container: str, database: str) -> None:
|
||||
for _ in range(120):
|
||||
completed = docker_psql(container, database, "select 1;", check=False)
|
||||
if completed.returncode == 0:
|
||||
return
|
||||
time.sleep(0.25)
|
||||
pytest.fail("fixture PostgreSQL did not become ready")
|
||||
|
||||
|
||||
def capture_manifest(container: str, database: str, destination: Path) -> None:
|
||||
copy_result = subprocess.run(
|
||||
["docker", "cp", str(rebuild.PARITY_SQL_PATH), f"{container}:/tmp/parity.sql"],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
if copy_result.returncode != 0:
|
||||
pytest.fail(f"could not copy parity SQL: {copy_result.stderr}")
|
||||
completed = subprocess.run(
|
||||
[
|
||||
"docker",
|
||||
"exec",
|
||||
container,
|
||||
"psql",
|
||||
"-X",
|
||||
"-U",
|
||||
"postgres",
|
||||
"-d",
|
||||
database,
|
||||
"-Atq",
|
||||
"-v",
|
||||
"ON_ERROR_STOP=1",
|
||||
"-f",
|
||||
"/tmp/parity.sql",
|
||||
],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
if completed.returncode != 0:
|
||||
pytest.fail(f"could not capture parity manifest: {completed.stderr}")
|
||||
destination.write_text(completed.stdout, encoding="utf-8")
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def source_postgres() -> Iterator[tuple[str, str]]:
|
||||
if shutil.which("docker") is None:
|
||||
pytest.skip("Docker is required for the genesis-plus-ledger lifecycle test")
|
||||
container = f"genesis-ledger-source-{uuid.uuid4().hex[:12]}"
|
||||
database = "teleo_fixture"
|
||||
started = subprocess.run(
|
||||
[
|
||||
"docker",
|
||||
"run",
|
||||
"--rm",
|
||||
"--detach",
|
||||
"--name",
|
||||
container,
|
||||
"--env",
|
||||
f"POSTGRES_DB={database}",
|
||||
"--env",
|
||||
"POSTGRES_HOST_AUTH_METHOD=trust",
|
||||
rebuild.canonical_rebuild.DEFAULT_IMAGE,
|
||||
],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
if started.returncode != 0:
|
||||
pytest.fail(f"could not start fixture PostgreSQL: {started.stderr}")
|
||||
try:
|
||||
wait_for_postgres(container, database)
|
||||
docker_psql(container, database, BOOTSTRAP_SQL)
|
||||
copy_result = subprocess.run(
|
||||
[
|
||||
"docker",
|
||||
"cp",
|
||||
str(rebuild.GUARD_PREREQUISITES_PATH),
|
||||
f"{container}:/tmp/kb-apply-prereqs.sql",
|
||||
],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
if copy_result.returncode != 0:
|
||||
pytest.fail(f"could not copy apply prerequisites: {copy_result.stderr}")
|
||||
migration = subprocess.run(
|
||||
[
|
||||
"docker",
|
||||
"exec",
|
||||
container,
|
||||
"psql",
|
||||
"-X",
|
||||
"-U",
|
||||
"postgres",
|
||||
"-d",
|
||||
database,
|
||||
"-Atq",
|
||||
"-v",
|
||||
"ON_ERROR_STOP=1",
|
||||
"-f",
|
||||
"/tmp/kb-apply-prereqs.sql",
|
||||
],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
if migration.returncode != 0:
|
||||
pytest.fail(f"could not apply guarded prerequisites: {migration.stderr}")
|
||||
yield container, database
|
||||
finally:
|
||||
subprocess.run(
|
||||
["docker", "rm", "--force", container],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
|
||||
def test_live_genesis_plus_ledger_command_rebuilds_exactly_and_proves_cleanup(
|
||||
tmp_path: Path,
|
||||
source_postgres: tuple[str, str],
|
||||
) -> None:
|
||||
source_container, database = source_postgres
|
||||
genesis_dump = tmp_path / "genesis.dump"
|
||||
dump_result = subprocess.run(
|
||||
[
|
||||
"docker",
|
||||
"exec",
|
||||
source_container,
|
||||
"pg_dump",
|
||||
"-U",
|
||||
"postgres",
|
||||
"-d",
|
||||
database,
|
||||
"--format=custom",
|
||||
"--file=/tmp/genesis.dump",
|
||||
],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
assert dump_result.returncode == 0, dump_result.stderr
|
||||
copy_result = subprocess.run(
|
||||
["docker", "cp", f"{source_container}:/tmp/genesis.dump", str(genesis_dump)],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
assert copy_result.returncode == 0, copy_result.stderr
|
||||
|
||||
genesis_manifest = tmp_path / "genesis-manifest.jsonl"
|
||||
capture_manifest(source_container, database, genesis_manifest)
|
||||
|
||||
material = replay_material()
|
||||
material_path = write_json(tmp_path / "entry-0001.json", material)
|
||||
entry = rebuild._validate_entry_material(
|
||||
material,
|
||||
expected_sequence=1,
|
||||
expected_replay_hash=material["replay_receipt"]["hashes"]["replay_material_sha256"],
|
||||
material_path=material_path,
|
||||
material_sha256=sha256_file(material_path),
|
||||
)
|
||||
docker_psql(source_container, database, rebuild.build_seed_sql(entry))
|
||||
docker_psql(
|
||||
source_container,
|
||||
database,
|
||||
entry.current_apply_sql,
|
||||
role="kb_apply",
|
||||
)
|
||||
docker_psql(
|
||||
source_container,
|
||||
database,
|
||||
rebuild.build_applied_timestamp_normalization_sql(entry),
|
||||
)
|
||||
|
||||
final_manifest = tmp_path / "final-manifest.jsonl"
|
||||
capture_manifest(source_container, database, final_manifest)
|
||||
ledger = {
|
||||
"artifact": rebuild.LEDGER_ARTIFACT,
|
||||
"contract_version": rebuild.LEDGER_CONTRACT_VERSION,
|
||||
"engine": rebuild._engine_hashes(),
|
||||
"genesis": {
|
||||
"dump_sha256": sha256_file(genesis_dump),
|
||||
"parity_manifest_sha256": sha256_file(genesis_manifest),
|
||||
},
|
||||
"entries": [
|
||||
{
|
||||
"sequence": 1,
|
||||
"material": material_path.name,
|
||||
"sha256": sha256_file(material_path),
|
||||
"replay_material_sha256": entry.replay_material_sha256,
|
||||
}
|
||||
],
|
||||
"final_parity": {
|
||||
"manifest": final_manifest.name,
|
||||
"sha256": sha256_file(final_manifest),
|
||||
},
|
||||
}
|
||||
ledger_path = write_json(tmp_path / "ledger.json", ledger)
|
||||
output = tmp_path / "reconstruction-receipt.json"
|
||||
command = [
|
||||
sys.executable,
|
||||
str(Path(rebuild.__file__).resolve()),
|
||||
"--genesis-dump",
|
||||
str(genesis_dump),
|
||||
"--genesis-manifest",
|
||||
str(genesis_manifest),
|
||||
"--ledger",
|
||||
str(ledger_path),
|
||||
"--ledger-sha256",
|
||||
sha256_file(ledger_path),
|
||||
"--database",
|
||||
database,
|
||||
"--container-prefix",
|
||||
"teleo-genesis-ledger-live-test",
|
||||
"--tmpfs-mb",
|
||||
"256",
|
||||
"--max-target-query-ms",
|
||||
"5000",
|
||||
"--max-target-source-ratio",
|
||||
"100",
|
||||
"--output",
|
||||
str(output),
|
||||
]
|
||||
completed = subprocess.run(
|
||||
command,
|
||||
cwd=rebuild.REPO_ROOT,
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
timeout=180,
|
||||
)
|
||||
|
||||
assert completed.returncode == 0, completed.stderr + completed.stdout
|
||||
receipt = json.loads(output.read_text(encoding="utf-8"))
|
||||
assert receipt["status"] == "pass"
|
||||
assert receipt["genesis_parity"]["status"] == "pass"
|
||||
assert receipt["ledger"]["entries"][0]["status"] == "pass"
|
||||
assert receipt["ledger"]["entries"][0]["guarded_apply_executed"] is True
|
||||
assert receipt["ledger"]["entries"][0]["canonical_rows_exact"] is True
|
||||
assert receipt["final_parity"]["status"] == "pass"
|
||||
assert receipt["cleanup"]["container_absent"] is True
|
||||
assert receipt["safety"]["network_access_available_to_container"] is False
|
||||
assert receipt["safety"]["private_replay_material_emitted"] is False
|
||||
assert PRIVATE_MARKER not in completed.stdout
|
||||
assert PRIVATE_MARKER not in output.read_text(encoding="utf-8")
|
||||
assert stat.S_IMODE(output.stat().st_mode) == 0o600
|
||||
|
||||
inspect = subprocess.run(
|
||||
["docker", "container", "inspect", receipt["container"]["name"]],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
assert inspect.returncode != 0
|
||||
Loading…
Reference in a new issue