From 5e0f0e5f0b01a8be9ba952b74e9afff508ddb7e0 Mon Sep 17 00:00:00 2001 From: Shay Date: Fri, 17 Jul 2026 10:49:44 -0700 Subject: [PATCH] feat(adr-0243): wire ADR-0240 PASS into integrate_biography (Phase 3 Lane C) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First live caller of integrate_biography (previously zero production call sites). New core/physics/biography_wiring.py: report-level PASS gate (report.wrong == 0 AND >=1 correct transfer; refusals don't block but all-refused validates nothing), I-01 closure asserts re-checked at the wiring seam (fail-closed typed BiographyIntegrationError), and a net-new append-only BiographyProvenanceRecord binding report evidence to the integrated trajectory hash (AuthorshipProposal-style closure proof in a TurnEvent-style audit role). Design ruling (Shay, 2026-07-17, brief open question): direct integration, structurally pinned — integrate_biography is a pure recompute (reconstruction-over-storage), so D-5/I-03 proposal gating does not attach; the ruling is enforced by a test pinning that the wiring imports no vault store and no evals package (report typed structurally, no core->evals import). Module registered in the never-serve lazy tier + _BANNED + third-door banned lists. cognitive_lifecycle.py untouched (Lane C touches zero Phase-2 code). --- core/physics/__init__.py | 15 ++ core/physics/biography_wiring.py | 219 ++++++++++++++++++++++ tests/test_adr_0243_biography_wiring.py | 207 ++++++++++++++++++++ tests/test_serve_quarantine_transitive.py | 1 + tests/test_third_door_cohesion.py | 2 + 5 files changed, 444 insertions(+) create mode 100644 core/physics/biography_wiring.py create mode 100644 tests/test_adr_0243_biography_wiring.py diff --git a/core/physics/__init__.py b/core/physics/__init__.py index 5cded114..52cbb4e6 100644 --- a/core/physics/__init__.py +++ b/core/physics/__init__.py @@ -144,6 +144,11 @@ _LAZY_EXPORTS: dict[str, str] = { "propositional_entails": "core.physics.cognitive_lifecycle", "relax_to_ground": "core.physics.cognitive_lifecycle", "uniform_assignment_state": "core.physics.cognitive_lifecycle", + # biography_wiring — ADR-0243 §2.5 validated-PASS → biography integration (never serve) + "BiographyIntegrationError": "core.physics.biography_wiring", + "BiographyProvenanceRecord": "core.physics.biography_wiring", + "biography_provenance_record": "core.physics.biography_wiring", + "integrate_validated_biography": "core.physics.biography_wiring", } from typing import TYPE_CHECKING @@ -206,6 +211,12 @@ if TYPE_CHECKING: # static-analysis only — never imported at runtime (serve q relax_to_ground, uniform_assignment_state, ) + from core.physics.biography_wiring import ( + BiographyIntegrationError, + BiographyProvenanceRecord, + biography_provenance_record, + integrate_validated_biography, + ) __all__ = [ "SalienceOperator", "SalienceMap", "FieldRegion", @@ -285,6 +296,10 @@ __all__ = [ "propositional_entails", "relax_to_ground", "uniform_assignment_state", + "BiographyIntegrationError", + "BiographyProvenanceRecord", + "biography_provenance_record", + "integrate_validated_biography", ] diff --git a/core/physics/biography_wiring.py b/core/physics/biography_wiring.py new file mode 100644 index 00000000..c9ca97f4 --- /dev/null +++ b/core/physics/biography_wiring.py @@ -0,0 +1,219 @@ +"""core.physics.biography_wiring — ADR-0240 PASS → biography integration (ADR-0243 §2.5). + +First live caller of :func:`core.physics.biography.integrate_biography`. +Gates on a validation report's report-level PASS signal (``report.wrong == 0`` +— the harness has no literal PASS type; ``wrong == 0`` is its acceptance +criterion) and re-asserts the I-01 closure invariant at this call site, +failing closed with a typed error. + +Ruling (Shay, 2026-07-17, Lane C open question): this wiring is a *direct* +integration, not a proposal-gated mutation — ``integrate_biography`` is a pure +recompute (reconstruction-over-storage; the blade is derived state, never +persisted), so D-5/I-03 proposal-only discipline does not attach here. The +ruling is structurally pinned: this module must import no vault store and no +``evals`` package (guarded by tests/test_adr_0243_biography_wiring.py). Any +future durable blade storage or trajectory auto-sealing breaks that pin and +reopens the mutation-vs-proposal question. + +Report-level granularity is deliberate: ADR-0243 §2.5 integrates a validated +*sequence*, and wrong=0 doctrine forbids cherry-picking correct transfers out +of a session that also produced wrongs. Refusals do not block (the harness's +own ``all_correct_or_refused`` semantics), but a session with zero correct +transfers has validated nothing and is refused — no confabulated wisdom. + +The report carries no versors (scalar evidence only) and the harness is +consumed as-is, so the caller supplies the lived trajectory alongside the +report; the provenance record binds the two by content hash. +""" + +from __future__ import annotations + +import hashlib +import json +from dataclasses import dataclass +from typing import Any, Mapping, Protocol, Sequence + +import numpy as np + +from algebra.backend import versor_condition +from core.physics.biography import BiographyHolonomyBlade, integrate_biography + +_CLOSURE = 1e-6 +_PROVENANCE_SCHEMA = "biography_provenance_v1" +_ADR_REFS = ("ADR-0240", "ADR-0243") + + +class BiographyIntegrationError(ValueError): + """Fail-closed refusal from :func:`integrate_validated_biography`. + + Subclasses ``ValueError`` so a caller that fail-closes on ``ValueError`` + records it as a refusal rather than crashing (same contract as + :class:`core.physics.surprise.SurpriseResidualError`). + """ + + def __init__(self, reason: str, **disclosure) -> None: + self.reason = reason + self.disclosure = dict(disclosure) + super().__init__(f"biography integration refused [{reason}]: {self.disclosure}") + + +class TransferCaseResult(Protocol): + """Per-case slice of the ADR-0240 report this wiring consumes.""" + + case_id: str + residual: float + correct: bool + refused: bool + reason: str + + +class ValidationReportLike(Protocol): + """Structural view of ``evals.analogical_transfer.harness.AnalogicalTransferReport``. + + Typed structurally so core.physics does not import ``evals`` (layering: + evals composes core, never the reverse). Any validated-experience source + exposing this shape can drive biography integration. + """ + + results: Sequence[TransferCaseResult] + counts: Mapping[str, int] + max_residual: float + wrong: int + + +@dataclass(frozen=True, slots=True) +class BiographyProvenanceRecord: + """Append-only audit record binding a PASS report to a biography integration. + + An evidence record, not a proposal — nothing awaits ratification (the blade + is derived state; see module docstring ruling). Closure-proof discipline + modeled on :class:`core.physics.self_authorship.AuthorshipProposal`; the + append-only audit role mirrors :class:`core.physics.identity.TurnEvent`. + """ + + record_id: str + schema_version: str + n_cases: int + counts: Mapping[str, int] + wrong: int + max_residual: float + case_outcomes: tuple[tuple[str, str], ...] # (case_id, reason), report order + trajectory_hash: str + n_steps: int + alpha: float + closure_proof: Mapping[str, float] + adr_refs: tuple[str, ...] + + def as_dict(self) -> dict[str, Any]: + return { + "record_id": self.record_id, + "schema_version": self.schema_version, + "n_cases": self.n_cases, + "counts": dict(self.counts), + "wrong": self.wrong, + "max_residual": self.max_residual, + "case_outcomes": [list(pair) for pair in self.case_outcomes], + "trajectory_hash": self.trajectory_hash, + "n_steps": self.n_steps, + "alpha": self.alpha, + "closure_proof": dict(self.closure_proof), + "adr_refs": list(self.adr_refs), + } + + +def _content_id(payload: Mapping[str, Any]) -> str: + raw = json.dumps(payload, sort_keys=True, separators=(",", ":"), default=str) + return hashlib.sha256(raw.encode("utf-8")).hexdigest()[:24] + + +def biography_provenance_record( + report: ValidationReportLike, + blade: BiographyHolonomyBlade, + *, + alpha: float, +) -> BiographyProvenanceRecord: + """Build the append-only provenance record for one validated integration.""" + counts = {k: int(v) for k, v in sorted(report.counts.items())} + case_outcomes = tuple((r.case_id, r.reason) for r in report.results) + closure_proof = { + "blade_closure": float(blade.closure), + "blade_versor_condition": float(versor_condition(blade.blade)), + "closure_tol": _CLOSURE, + } + body = { + "schema_version": _PROVENANCE_SCHEMA, + "n_cases": len(report.results), + "counts": counts, + "wrong": int(report.wrong), + "max_residual": float(report.max_residual), + "case_outcomes": [list(pair) for pair in case_outcomes], + "trajectory_hash": blade.trajectory_hash, + "n_steps": int(blade.n_steps), + "alpha": float(alpha), + "closure_proof": closure_proof, + "adr_refs": list(_ADR_REFS), + } + return BiographyProvenanceRecord( + record_id=f"bioprov-{_content_id(body)}", + schema_version=_PROVENANCE_SCHEMA, + n_cases=len(report.results), + counts=counts, + wrong=int(report.wrong), + max_residual=float(report.max_residual), + case_outcomes=case_outcomes, + trajectory_hash=blade.trajectory_hash, + n_steps=int(blade.n_steps), + alpha=float(alpha), + closure_proof=closure_proof, + adr_refs=_ADR_REFS, + ) + + +def integrate_validated_biography( + report: ValidationReportLike, + trajectory: Sequence[np.ndarray], + *, + alpha: float = 0.5, +) -> tuple[BiographyHolonomyBlade, BiographyProvenanceRecord]: + """Integrate a lived trajectory into biography, gated on report-level PASS. + + Refuses (typed, fail-closed) unless the report validated at least one + transfer with zero wrongs. Re-asserts I-01 closure on the returned blade at + this call site (modeled on tests/test_third_door_cohesion.py I-01) rather + than trusting the callee's internal checks. + """ + n_cases = len(report.results) + if n_cases == 0: + raise BiographyIntegrationError("empty_report") + if int(report.wrong) != 0: + raise BiographyIntegrationError( + "report_not_pass", + wrong=int(report.wrong), + counts={k: int(v) for k, v in sorted(report.counts.items())}, + ) + n_correct = sum(1 for r in report.results if r.correct) + if n_correct == 0: + # wrong == 0 with everything refused validates nothing (ADR-0243 §2.5 + # integrates a *validated* sequence) — refuse, no confabulated wisdom. + raise BiographyIntegrationError( + "no_validated_transfers", + n_cases=n_cases, + refused=sum(1 for r in report.results if r.refused), + ) + + try: + blade = integrate_biography(trajectory, alpha=alpha) + except ValueError as exc: + raise BiographyIntegrationError("trajectory_rejected", detail=str(exc)) from exc + + cond = float(versor_condition(blade.blade)) + if blade.closure >= _CLOSURE or cond >= _CLOSURE: + raise BiographyIntegrationError( + "i01_closure_violation", + closure=float(blade.closure), + versor_condition=cond, + closure_tol=_CLOSURE, + ) + + record = biography_provenance_record(report, blade, alpha=alpha) + return blade, record diff --git a/tests/test_adr_0243_biography_wiring.py b/tests/test_adr_0243_biography_wiring.py new file mode 100644 index 00000000..4a6563aa --- /dev/null +++ b/tests/test_adr_0243_biography_wiring.py @@ -0,0 +1,207 @@ +"""ADR-0243 §2.5 Lane C — ADR-0240 harness PASS → integrate_biography wiring. + +Covers: live-harness PASS drives a real integration with I-01 asserts and a +provenance record; non-PASS reports must not integrate (fail-closed typed +refusals); the direct-integration ruling is structurally pinned (wiring module +imports no vault store and no evals package). +""" + +from __future__ import annotations + +import dataclasses +import json +import subprocess +import sys +from pathlib import Path + +import numpy as np +import pytest + +from algebra.rotor import make_rotor_from_angle +from algebra.versor import versor_condition +from core.physics.biography import BiographyHolonomyBlade, integrate_biography +from core.physics.biography_wiring import ( + BiographyIntegrationError, + integrate_validated_biography, +) +from evals.analogical_transfer.harness import ( + AnalogicalTransferReport, + TransferResult, + make_fixture_pair, + run_analogical_transfer, +) + +_CLOSURE = 1e-6 +_ROOT = Path(__file__).resolve().parents[1] + + +def _trajectory() -> list[np.ndarray]: + return [make_rotor_from_angle(0.1 * (i + 1), bivector_idx=6 + (i % 3)) for i in range(4)] + + +def _result( + case_id: str = "c1", + *, + correct: bool = True, + refused: bool = False, + reason: str = "transfer_ok", + residual: float = 0.01, +) -> TransferResult: + return TransferResult( + case_id=case_id, + residual=residual, + goldtether_before=0.0, + goldtether_after=0.0, + correct=correct, + refused=refused, + reason=reason, + ) + + +def _report(results: list[TransferResult]) -> AnalogicalTransferReport: + counts = { + "correct": sum(1 for r in results if r.correct), + "wrong": sum(1 for r in results if not r.correct and not r.refused), + "refused": sum(1 for r in results if r.refused), + } + finite = [r.residual for r in results if np.isfinite(r.residual)] + return AnalogicalTransferReport( + results=tuple(results), + counts=counts, + max_residual=float(max(finite, default=0.0)), + wrong=counts["wrong"], + ) + + +# --- PASS path ---------------------------------------------------------------- + + +def test_live_harness_pass_drives_integration(): + """A real ADR-0240 harness PASS report drives a live integrate_biography call.""" + report = run_analogical_transfer([make_fixture_pair()]) + assert report.wrong == 0 and report.all_correct_or_refused + + traj = _trajectory() + blade, record = integrate_validated_biography(report, traj) + + # I-01 at the wiring seam (same asserts as test_third_door_cohesion I-01) + assert blade.closure < _CLOSURE + assert versor_condition(blade.blade) < _CLOSURE + + assert record.record_id.startswith("bioprov-") + assert record.schema_version == "biography_provenance_v1" + assert record.wrong == 0 + assert record.n_cases == 1 + assert record.counts["correct"] == 1 + assert record.case_outcomes == ( + ("fixture-nullcloud-similarity-transfer-v2", "transfer_ok"), + ) + # Record binds the report to the exact trajectory that was integrated + assert record.trajectory_hash == integrate_biography(traj).trajectory_hash + assert record.n_steps == blade.n_steps + assert record.adr_refs == ("ADR-0240", "ADR-0243") + assert record.closure_proof["blade_closure"] == blade.closure + + +def test_provenance_record_deterministic(): + report = run_analogical_transfer([make_fixture_pair()]) + traj = _trajectory() + _, rec1 = integrate_validated_biography(report, traj) + _, rec2 = integrate_validated_biography(report, traj) + assert rec1.record_id == rec2.record_id + assert rec1.as_dict() == rec2.as_dict() + assert json.dumps(rec1.as_dict(), sort_keys=True) # JSONL-serializable + + +# --- must-reject paths -------------------------------------------------------- + + +def test_wrong_report_refuses(): + report = _report([_result(), _result("c2", correct=False, reason="residual_above_threshold", residual=9.0)]) + assert report.wrong == 1 + with pytest.raises(BiographyIntegrationError) as exc_info: + integrate_validated_biography(report, _trajectory()) + assert exc_info.value.reason == "report_not_pass" + + +def test_live_harness_wrong_refuses(): + """A real harness run that produces a wrong must not integrate.""" + case = make_fixture_pair() + bad = dataclasses.replace( + case, expected_novel=make_rotor_from_angle(2.0, bivector_idx=8) + ) + report = run_analogical_transfer([bad]) + assert report.wrong >= 1 + with pytest.raises(BiographyIntegrationError) as exc_info: + integrate_validated_biography(report, _trajectory()) + assert exc_info.value.reason == "report_not_pass" + + +def test_all_refused_validates_nothing(): + """wrong == 0 with zero correct transfers validated nothing — refuse.""" + report = _report( + [ + _result("c1", correct=False, refused=True, reason="refused:x", residual=float("inf")), + _result("c2", correct=False, refused=True, reason="closure_failed", residual=0.5), + ] + ) + assert report.wrong == 0 and report.all_correct_or_refused + with pytest.raises(BiographyIntegrationError) as exc_info: + integrate_validated_biography(report, _trajectory()) + assert exc_info.value.reason == "no_validated_transfers" + + +def test_empty_report_refused(): + with pytest.raises(BiographyIntegrationError) as exc_info: + integrate_validated_biography(_report([]), _trajectory()) + assert exc_info.value.reason == "empty_report" + + +def test_bad_trajectory_rejected(): + report = _report([_result()]) + with pytest.raises(BiographyIntegrationError) as exc_info: + integrate_validated_biography(report, [np.zeros(32, dtype=np.float64)]) + assert exc_info.value.reason == "trajectory_rejected" + + +def test_i01_violation_fails_closed_at_call_site(monkeypatch): + """The wiring's own I-01 gate must trip even if the callee returns a bad blade.""" + open_blade = BiographyHolonomyBlade( + blade=make_rotor_from_angle(0.3), + n_steps=1, + trajectory_hash="deadbeef", + closure=1.0, + ) + monkeypatch.setattr( + "core.physics.biography_wiring.integrate_biography", + lambda trajectory, *, alpha: open_blade, + ) + with pytest.raises(BiographyIntegrationError) as exc_info: + integrate_validated_biography(_report([_result()]), _trajectory()) + assert exc_info.value.reason == "i01_closure_violation" + + +# --- structural pin of the direct-integration ruling -------------------------- + + +def test_wiring_imports_no_vault_store_and_no_evals(): + """Ruling pin: direct integration is legitimate only while the wiring makes + no durable write and stays layered below evals. If this test breaks, the + mutation-vs-proposal question reopens — do not weaken it.""" + probe = ( + "import importlib, sys, json;" + "importlib.import_module('core.physics.biography_wiring');" + "banned=['core.physics.holographic_vault','evals'];" + "leaked=sorted(m for m in sys.modules for b in banned if m==b or m.startswith(b+'.'));" + "print(json.dumps(leaked))" + ) + result = subprocess.run( + [sys.executable, "-c", probe], + cwd=str(_ROOT), + capture_output=True, + text=True, + env={"PYTHONPATH": str(_ROOT), "PATH": ""}, + ) + assert result.returncode == 0, f"probe failed: {result.stderr[-2000:]}" + leaked = json.loads(result.stdout.strip().splitlines()[-1]) + assert leaked == [], f"biography_wiring loaded banned modules: {leaked}" diff --git a/tests/test_serve_quarantine_transitive.py b/tests/test_serve_quarantine_transitive.py index be22cd22..57945496 100644 --- a/tests/test_serve_quarantine_transitive.py +++ b/tests/test_serve_quarantine_transitive.py @@ -29,6 +29,7 @@ _BANNED = ( "core.physics.multi_scale_energy", "core.physics.sensorium_wave_feed", "core.physics.cognitive_lifecycle", # ADR-0243 lifecycle — never serve + "core.physics.biography_wiring", # ADR-0243 §2.5 PASS→biography wiring — never serve # NOTE: core.physics.wave_manifold is intentionally excluded pending the # Joshua design ruling (goldtether delegates to it). Add it here if the # ruling is "quarantine wave_manifold for real". diff --git a/tests/test_third_door_cohesion.py b/tests/test_third_door_cohesion.py index a7a00ff4..53b444f9 100644 --- a/tests/test_third_door_cohesion.py +++ b/tests/test_third_door_cohesion.py @@ -71,6 +71,7 @@ def test_phase0_a04_serve_path_quarantines_wave_and_fibonacci(): "multi_scale_energy", # ADR-0242 V2 research multi-band E_n(t), never serve "sensorium_wave_feed", # D7 I-04 sensorium→ψ feed, never serve "cognitive_lifecycle", # ADR-0243 ingress→relaxation→egress, never serve + "biography_wiring", # ADR-0243 §2.5 PASS→biography wiring, never serve } banned_substrings = ( "holographic_vault", @@ -82,6 +83,7 @@ def test_phase0_a04_serve_path_quarantines_wave_and_fibonacci(): "multi_scale_energy", "sensorium_wave_feed", "cognitive_lifecycle", + "biography_wiring", ) for node in ast.walk(tree): if isinstance(node, ast.Import):