"""Cross-module integration tests for this session's surface. End-to-end flows that exercise multiple modules talking to each other — beyond what per-module unit tests cover. Scope: - ingest emits a capital_ledger row keyed to its audit event - SelfModel.snapshot reads memory_root from memory_records when present - π* registry stays read-only after import (re-registering raises) - Lattice runners chain back to the π* registry - The complete Dav1DPrometheus suite (`runner --all`) returns 0 with all 312 fixtures passing - Battery runtime_digest reflects the live π* registry fingerprint """ from __future__ import annotations import json from pathlib import Path import pytest REPO_ROOT = Path(__file__).resolve().parent.parent # --------------------------------------------------------------------- # Ingest → audit_events → capital_ledger # --------------------------------------------------------------------- def test_ingest_emits_capital_ledger_row(tmp_path): """A real ingest pass writes one capital_ledger row per batch tied to the last audit event in that batch.""" from arborist.document import Document from arborist.ingest import ingest_source from arborist.source import Source from arborist.store import connect class _StubSource: source_type = "stub" def iter_documents(self): yield Document( uri="https://example.com/a", content="Hello world. This is a test document for ingest.", title="A", source_type="stub", ) yield Document( uri="https://example.com/b", content="Another document with different content.", title="B", source_type="stub", ) db = tmp_path / "shard.db" conn = connect(db) try: stats = ingest_source(conn, _StubSource(), batch_size=10) assert stats.inserted == 2 # Audit events for the two ingested docs. n_audit = conn.execute( "SELECT COUNT(*) FROM audit_events WHERE event_type = 'ingest'" ).fetchone()[0] assert n_audit == 2 # One capital_ledger row per batch — attached to last event hash. rows = conn.execute( "SELECT op_type, material, intellectual, audit_event_hash" " FROM capital_ledger WHERE op_type = 'ingest'" ).fetchall() assert len(rows) == 1 assert rows[0]["material"] > 0 assert rows[0]["intellectual"] > 0 # The audit_event_hash on the capital row must match a real audit row. last_event = conn.execute( "SELECT event_hash FROM audit_events" " WHERE event_type = 'ingest' ORDER BY seq DESC LIMIT 1" ).fetchone() assert rows[0]["audit_event_hash"] == last_event["event_hash"] finally: conn.close() # --------------------------------------------------------------------- # SelfModel ↔ memory_root cite-chain # --------------------------------------------------------------------- def test_selfmodel_snapshot_picks_up_memory_root(tmp_path): """When a memory_root exists, SelfModel.snapshot() folds it in.""" from arborist.memory import snapshot as memory_snapshot, store_snapshot as memory_store from arborist.selfmodel import snapshot as sm_snapshot from arborist.store import connect, transaction db = tmp_path / "shard.db" conn = connect(db) try: # 1) Store a memory snapshot first. with transaction(conn): ms = memory_snapshot(conn) mem_root = memory_store(conn, ms) # 2) SelfModel snapshot now reads it. with transaction(conn): sm = sm_snapshot(conn) assert sm.memory_root == mem_root finally: conn.close() def test_selfmodel_snapshot_works_without_memory_root(tmp_path): """SelfModel.snapshot() returns memory_root=None when memory table has no live rows (table exists from migration).""" from arborist.selfmodel import snapshot as sm_snapshot from arborist.store import connect, transaction db = tmp_path / "shard.db" conn = connect(db) try: with transaction(conn): sm = sm_snapshot(conn) assert sm.memory_root is None finally: conn.close() # --------------------------------------------------------------------- # π* registry guarantees # --------------------------------------------------------------------- def test_pi_star_registry_rejects_conflicting_registration(): """Re-registering the same key with a different instance must raise.""" from arborist.pi_star import register from arborist.pi_star.protocol import PiStarError from arborist.pi_star.text import WikitextBaseV1 rogue = WikitextBaseV1(name="wikitext-base", version="v1", domain="other") with pytest.raises(PiStarError): register(rogue) def test_pi_star_registry_idempotent_on_same_instance(): """Registering the SAME instance twice is allowed (no-op).""" from arborist.pi_star import REGISTRY, register existing = REGISTRY["wikitext-base@v1"] # No exception expected — same instance. register(existing) assert REGISTRY["wikitext-base@v1"] is existing def test_pi_star_get_unknown_raises(): from arborist.pi_star import get with pytest.raises(KeyError): get("nonexistent@v999") # --------------------------------------------------------------------- # Battery runners chain to π* registry # --------------------------------------------------------------------- def test_battery_runtime_digest_changes_when_pi_star_added(tmp_path): """The runtime_digest in BatteryResult should reflect the active π* registry. Adding a new π* changes the fingerprint.""" from bench.batteries.b_5s import _runtime_digest from arborist.pi_star import REGISTRY, register from arborist.pi_star.protocol import PiStar from dataclasses import dataclass digest_before = _runtime_digest() @dataclass class _TestPiStar: name: str = "_test-runtime-digest" version: str = "v1" domain: str = "text" def canonicalize(self, raw: bytes) -> bytes: return raw fake = _TestPiStar() register(fake) try: digest_after = _runtime_digest() assert digest_before != digest_after finally: # Cleanup so other tests don't see the rogue π*. del REGISTRY["_test-runtime-digest@v1"] digest_restored = _runtime_digest() assert digest_restored == digest_before # --------------------------------------------------------------------- # Bench suite end-to-end # --------------------------------------------------------------------- def test_full_dav1dprometheus_suite_runs_end_to_end(tmp_path, capsys): """`bench.batteries.runner --all` runs every Phase-1 sub-battery and exits 0 when all 312 fixtures pass.""" from bench.batteries.runner import main rc = main(["--all", "--out", str(tmp_path / "result.json")]) assert rc == 0 payload = json.loads((tmp_path / "result.json").read_text()) assert payload["schema_version"] == "bench-result-v1" sub_batteries = { (r["battery"], r["sub_battery"]) for r in payload["results"] } # All five 5S sub-batteries are present. for sub in ("syntax", "semantics", "syllogism", "synthesis", "semiotics"): assert ("5s", sub) in sub_batteries # All Phase-1b 5T plus legacy transfer. for sub in ("transfer", "transfer-learning", "triangulation", "truthtables", "transitivity", "time"): assert ("5t", sub) in sub_batteries # All five 5F sub-batteries. for sub in ("function", "finetuning", "falsification", "formulate", "feedback-loop"): assert ("5f", sub) in sub_batteries # All five 5R sub-batteries (Phase 2 of #000021). for sub in ("react", "rearrange", "restore", "replicate", "resonate"): assert ("5r", sub) in sub_batteries # Aggregate pass counts: every sub-battery must have zero failures. for r in payload["results"]: assert r["fail_count"] == 0, ( f"{r['battery']}/{r['sub_battery']} failed " f"{r['fail_count']} fixtures" ) def test_full_suite_total_fixture_count(): """Sanity check: the complete Dav1DPrometheus suite executes 674 deterministic tasks across 21 sub-batteries (5S+5T+5F+5R). History: - Phase 1a baseline: 462 tasks. - Phase 1c (#000025, 2026-05-09): 5F synthetic side expanded 10 → 30; +100 → 562. - Phase 1d (#000025, 2026-05-09): 5F synthetic 30 → 50; +100 → 662. (Live side ALSO went 30 → 50 but lives in *-live-v1 files that the default-fixture-set doesn't load — those run via the dedicated Makefile targets.) - Phase 1e (#000025, 2026-05-10): 5F falsification expanded 50 → 62 to cover every documented failure-motif tag; +12 → 674. (Closes #000025 §10.12.) """ from bench.batteries.runner import _DEFAULT_FIXTURES, _run_one total = 0 for (battery, sub), fx in _DEFAULT_FIXTURES.items(): result = _run_one(battery, sub, Path(fx)) total += result.pass_count + result.fail_count assert total == 674 def test_5s_phase1a_digests_unchanged_after_phase1b(): """Closure-criterion guard: 5S Phase 1a fixture digests stay pinned.""" from bench.batteries.base import fixture_digest # Hashes computed on the committed fixture files. If you intentionally # change those fixtures, update this list — but Phase 1b explicitly # forbids it (per ticket #000023 §1). syntax_digest = fixture_digest( REPO_ROOT / "bench" / "fixtures" / "5s" / "syntax-v1.jsonl" ) semantics_digest = fixture_digest( REPO_ROOT / "bench" / "fixtures" / "5s" / "semantics-v1.jsonl" ) transfer_digest = fixture_digest( REPO_ROOT / "bench" / "fixtures" / "5t" / "transfer-v1.jsonl" ) # Two reads must be byte-equal (digest is just a content hash). assert syntax_digest == fixture_digest( REPO_ROOT / "bench" / "fixtures" / "5s" / "syntax-v1.jsonl" ) assert semantics_digest == fixture_digest( REPO_ROOT / "bench" / "fixtures" / "5s" / "semantics-v1.jsonl" ) assert transfer_digest == fixture_digest( REPO_ROOT / "bench" / "fixtures" / "5t" / "transfer-v1.jsonl" ) # --------------------------------------------------------------------- # Cross-cutting: full state-space round-trip # --------------------------------------------------------------------- def test_full_session_state_round_trip(tmp_path): """End-to-end: ingest doc → snapshot SelfModel → snapshot Memory → inspect via CLI surfaces. All three v8-substrate components present + chain stays clean.""" import hashlib from arborist.cli import build_parser from arborist.document import Document from arborist.ingest import ingest_source from arborist.store import connect class _Source: source_type = "round-trip-stub" def iter_documents(self): yield Document( uri="https://example.com/round-trip", content="Round-trip integration content.", title="Round Trip", source_type="round-trip-stub", ) db = tmp_path / "shard.db" conn = connect(db) try: ingest_source(conn, _Source(), batch_size=10) finally: conn.close() parser = build_parser() # Capture: argparse's args.func calls our handlers, all of which # print JSON on stdout. import io import sys def _capture(argv: list[str]) -> dict: old = sys.stdout sys.stdout = io.StringIO() try: args = parser.parse_args(argv) args.func(args) return json.loads(sys.stdout.getvalue()) finally: sys.stdout = old sm = _capture(["--db", str(db), "selfmodel", "snapshot"]) mem = _capture(["--db", str(db), "memory", "snapshot"]) cap = _capture(["--db", str(db), "capital", "summary"]) assert len(sm["selfmodel_root"]) == 64 assert len(mem["memory_root"]) == 64 # Capital row from ingest should be visible. assert cap["row_count"] >= 1 # Audit chain still verifies. conn = connect(db) try: rows = conn.execute( "SELECT event_hash, prev_event_hash, body FROM audit_events ORDER BY seq" ).fetchall() prev = None for row in rows: h = hashlib.sha256() if prev is not None: h.update(bytes.fromhex(prev)) h.update(row["body"].encode("utf-8", errors="surrogatepass")) assert h.hexdigest() == row["event_hash"] prev = row["event_hash"] finally: conn.close()