From aa86f1ae353ab0368526960d58c5addd57e4b73e Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 14 Jul 2026 18:34:41 -0700 Subject: [PATCH 1/8] =?UTF-8?q?feat(wave):=20P9=20Trace=20A=20contemplatio?= =?UTF-8?q?n=20=E2=86=92=20SPECULATIVE=20holographic=20seal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Land ADR-0241 cohesion package P9: - core/contemplation/wave_seam.py seals standing-wave modes only via HolographicVaultStore.seal_mode (SPECULATIVE); no seal_mode_reviewed, no direct VaultStore.store (INV-21). - FindingKind.RESONANT_MODE_CANDIDATE + ContemplationFinding for teaching review corridor. - reconstruct_as_hypothesis (full spectrum) vs reconstruct_as_evidence (min_status=COHERENT only; SPECULATIVE cannot masquerade). - HolographicVaultStore.resonant_reconstruct with status filter. - Serve quarantine extended to wave_seam; fidelity + ADR-0241 status. Lane: pytest tests/test_adr_0241_wave_contemplation_seam.py tests/test_adr_0241_holographic_vault.py tests/test_third_door_cohesion.py → 38 passed. --- core/contemplation/__init__.py | 15 ++ core/contemplation/schema.py | 2 + core/contemplation/wave_seam.py | 193 ++++++++++++++++++ core/physics/holographic_vault.py | 50 ++++- ...hyperbolic-atlas-and-resonant-cognition.md | 2 +- .../research/third-door-blueprint-fidelity.md | 4 +- .../test_adr_0241_wave_contemplation_seam.py | 164 +++++++++++++++ tests/test_third_door_cohesion.py | 13 +- 8 files changed, 430 insertions(+), 13 deletions(-) create mode 100644 core/contemplation/wave_seam.py create mode 100644 tests/test_adr_0241_wave_contemplation_seam.py diff --git a/core/contemplation/__init__.py b/core/contemplation/__init__.py index e7d439fb..435516bb 100644 --- a/core/contemplation/__init__.py +++ b/core/contemplation/__init__.py @@ -2,6 +2,9 @@ ADR-0080: contemplation can emit speculative findings about current substrate/report evidence, but it cannot ratify, promote, or mutate packs. + +ADR-0241 P9: Trace A wave seam may SPECULATIVE-seal standing-wave modes and +emit RESONANT_MODE_CANDIDATE findings — never COHERENT, never serve-wired. """ from .runner import contemplate_frontier_reports, run_contemplation @@ -12,6 +15,13 @@ from .schema import ( FindingKind, ) from .snapshot import ContemplationSubstrate +from .wave_seam import ( + WaveModeHypothesis, + WaveReconstructResult, + reconstruct_as_evidence, + reconstruct_as_hypothesis, + speculative_seal_from_contemplation, +) __all__ = [ "ContemplationEvidenceRef", @@ -19,6 +29,11 @@ __all__ = [ "ContemplationRun", "ContemplationSubstrate", "FindingKind", + "WaveModeHypothesis", + "WaveReconstructResult", "contemplate_frontier_reports", + "reconstruct_as_evidence", + "reconstruct_as_hypothesis", "run_contemplation", + "speculative_seal_from_contemplation", ] diff --git a/core/contemplation/schema.py b/core/contemplation/schema.py index fbfcc3a9..42efc2f2 100644 --- a/core/contemplation/schema.py +++ b/core/contemplation/schema.py @@ -25,6 +25,8 @@ class FindingKind(Enum): OOV_GAP = "oov_gap" PLANNER_GAP = "planner_gap" PACK_MUTATION_CANDIDATE = "pack_mutation_candidate" + # ADR-0241 P9 Trace A: speculative standing-wave mode sealed for review. + RESONANT_MODE_CANDIDATE = "resonant_mode_candidate" def _canonical_json(payload: dict[str, Any]) -> str: diff --git a/core/contemplation/wave_seam.py b/core/contemplation/wave_seam.py new file mode 100644 index 00000000..5c3ed147 --- /dev/null +++ b/core/contemplation/wave_seam.py @@ -0,0 +1,193 @@ +"""P9 Trace A seam — contemplation → SPECULATIVE holographic standing-wave seal. + +ADR-0241 cohesion package P9: + +1. Contemplation may **SPECULATIVE-seal** standing-wave modes via + :meth:`HolographicVaultStore.seal_mode` only. +2. Never writes COHERENT — teaching corridor / authorized + ``seal_mode_reviewed`` remains outside this module. +3. Resonant reconstruct is available as a **hypothesis** over the full + spectrum, or as **evidence** only when ``min_status=COHERENT``. +4. Serve path stays quarantined (no import from ``chat/runtime.py``). +5. No direct ``VaultStore.store`` — INV-21 writes stay in holographic_vault. + +This module is the living-system bridge for Trace A without collapsing +the teaching / serve containment boundary. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Literal + +import numpy as np + +from core.contemplation.schema import ( + ContemplationEvidenceRef, + ContemplationFinding, + FindingKind, +) +from core.physics.holographic_vault import ( + HolographicVaultError, + HolographicVaultStore, + SealedMode, +) +from teaching.epistemic import EpistemicStatus + + +@dataclass(frozen=True, slots=True) +class WaveModeHypothesis: + """SPECULATIVE seal + contemplation finding for teaching review.""" + + sealed: SealedMode + finding: ContemplationFinding + standing: Literal["hypothesis"] = "hypothesis" + + def as_dict(self) -> dict[str, Any]: + return { + "standing": self.standing, + "mode_id": self.sealed.mode_id, + "vault_index": self.sealed.vault_index, + "epistemic_status": self.sealed.epistemic_status.value, + "finding": self.finding.as_dict(), + } + + +@dataclass(frozen=True, slots=True) +class WaveReconstructResult: + """Reconstructed field with honest epistemic standing label.""" + + psi_hat: np.ndarray + coeffs: np.ndarray + energies: np.ndarray + spectrum: tuple[SealedMode, ...] + standing: Literal["hypothesis", "evidence"] + min_status: EpistemicStatus | None + + def as_dict(self) -> dict[str, Any]: + return { + "standing": self.standing, + "min_status": None if self.min_status is None else self.min_status.value, + "mode_ids": [s.mode_id for s in self.spectrum], + "coeff_count": int(self.coeffs.shape[0]), + } + + +def speculative_seal_from_contemplation( + store: HolographicVaultStore, + psi: np.ndarray, + *, + substrate_hash: str, + subject: str, + mode_id: str | None = None, + notes: str = "", + predicate: str = "propose_standing_wave_mode", +) -> WaveModeHypothesis: + """SPECULATIVE-seal a closed mode and emit a contemplation finding. + + Fail-closed on non-closed / high-drift ψ (delegates to holographic admit). + Does **not** accept an authorization flag — COHERENT promotion is not + available on this seam. + """ + if not str(substrate_hash).strip(): + raise ValueError("substrate_hash is required for Trace A provenance") + if not str(subject).strip(): + raise ValueError("subject is required") + + meta: dict[str, Any] = { + "source": "contemplation_trace_a", + "substrate_hash": substrate_hash, + "notes": notes, + "adr_refs": ["ADR-0241", "ADR-0080"], + } + sealed = store.seal_mode(psi, mode_id=mode_id, metadata=meta) + if sealed.epistemic_status is not EpistemicStatus.SPECULATIVE: + # Defensive: seal_mode contract is SPECULATIVE-only; never promote here. + raise RuntimeError( + "Trace A seam integrity breach: seal_mode returned non-SPECULATIVE" + ) + + mid = sealed.mode_id + finding = ContemplationFinding( + kind=FindingKind.RESONANT_MODE_CANDIDATE, + subject=subject, + predicate=predicate, + object=mid, + evidence_refs=( + ContemplationEvidenceRef( + source_type="holographic_vault", + source_id=mid, + pointer=f"vault_index:{sealed.vault_index}", + summary=( + "SPECULATIVE standing-wave mode sealed for teaching review; " + "not admissible as COHERENT evidence" + ), + ), + ), + proposed_action="review_standing_wave_mode", + substrate_hash=substrate_hash, + epistemic_status=EpistemicStatus.SPECULATIVE, + ) + return WaveModeHypothesis(sealed=sealed, finding=finding, standing="hypothesis") + + +def reconstruct_as_hypothesis( + store: HolographicVaultStore, + psi_query: np.ndarray, +) -> WaveReconstructResult: + """Superposition reconstruct over the full spectrum (incl. SPECULATIVE). + + Result standing is always ``hypothesis`` — never claim reviewed evidence. + """ + psi_hat, coeffs, energies, spectrum = store.resonant_reconstruct(psi_query) + return WaveReconstructResult( + psi_hat=psi_hat, + coeffs=coeffs, + energies=energies, + spectrum=spectrum, + standing="hypothesis", + min_status=None, + ) + + +def reconstruct_as_evidence( + store: HolographicVaultStore, + psi_query: np.ndarray, +) -> WaveReconstructResult: + """Superposition reconstruct over COHERENT modes only. + + SPECULATIVE modes are excluded. Empty COHERENT spectrum refuses so + unreviewed hypothesis mass cannot masquerade as evidence. + """ + try: + psi_hat, coeffs, energies, spectrum = store.resonant_reconstruct( + psi_query, + min_status=EpistemicStatus.COHERENT, + ) + except HolographicVaultError as exc: + if exc.reason == "empty_spectrum": + raise HolographicVaultError( + "empty_spectrum", + detail=( + "evidence reconstruct requires COHERENT standing-wave modes; " + "SPECULATIVE hypothesis mass is excluded" + ), + ) from exc + raise + return WaveReconstructResult( + psi_hat=psi_hat, + coeffs=coeffs, + energies=energies, + spectrum=spectrum, + standing="evidence", + min_status=EpistemicStatus.COHERENT, + ) + + +__all__ = [ + "WaveModeHypothesis", + "WaveReconstructResult", + "reconstruct_as_evidence", + "reconstruct_as_hypothesis", + "speculative_seal_from_contemplation", +] diff --git a/core/physics/holographic_vault.py b/core/physics/holographic_vault.py index 39c59588..261a7dd7 100644 --- a/core/physics/holographic_vault.py +++ b/core/physics/holographic_vault.py @@ -222,14 +222,7 @@ class HolographicVaultStore: Empty spectrum / cold start without modes → refuse (no confabulation). Uses algebraic reverse-product energy via WaveManifold. """ - if min_status is not None: - # Rebuild filtered view so COHERENT-tier evidence excludes SPECULATIVE. - spectrum = list(self.load_spectrum(min_status=min_status)) - else: - spectrum = list(self._sealed) - if not spectrum: - # Cold start: attempt unfiltered load once. - spectrum = list(self.load_spectrum()) + spectrum = self._spectrum_for_status(min_status) if not spectrum: raise HolographicVaultError( "empty_spectrum", @@ -240,6 +233,47 @@ class HolographicVaultStore: mode, energy, idx = self._manifold.resonant_recall(query, modes=modes) return mode, float(energy), int(idx), spectrum[int(idx)] + def resonant_reconstruct( + self, + psi_query: np.ndarray, + *, + min_status: EpistemicStatus | None = None, + ) -> tuple[np.ndarray, np.ndarray, np.ndarray, tuple[SealedMode, ...]]: + """Superposition reconstruct over the durable spectrum. + + ``min_status=COHERENT`` excludes SPECULATIVE modes so hypothesis + mass cannot masquerade as reviewed evidence (Trace A / INV-24). + Empty filtered spectrum refuses (no confabulation). + """ + spectrum = self._spectrum_for_status(min_status) + if not spectrum: + raise HolographicVaultError( + "empty_spectrum", + detail=( + "no standing-wave modes for resonant reconstruct " + f"(min_status={getattr(min_status, 'value', min_status)!r})" + ), + ) + query = _as_mv(psi_query, "ψ_query") + modes = [s.mode for s in spectrum] + psi_hat, coeffs, energies = self._manifold.resonant_reconstruct( + query, modes=modes + ) + return psi_hat, coeffs, energies, tuple(spectrum) + + def _spectrum_for_status( + self, + min_status: EpistemicStatus | None, + ) -> list[SealedMode]: + if min_status is not None: + # Rebuild filtered view so COHERENT-tier evidence excludes SPECULATIVE. + return list(self.load_spectrum(min_status=min_status)) + spectrum = list(self._sealed) + if not spectrum: + # Cold start: attempt unfiltered load once. + spectrum = list(self.load_spectrum()) + return spectrum + def spectrum_size(self) -> int: """Number of standing-wave modes currently in the reconstruction cache.""" return len(self._sealed) diff --git a/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md b/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md index ff3e622b..1f518531 100644 --- a/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md +++ b/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md @@ -1,6 +1,6 @@ # ADR-0241: Wave-Field Driven Hyperbolic Atlas and Resonant Algebraic Cognition -**Status**: Proposed — substrate + Slice-2/3 subsumption complete on branch (`wave_manifold`, operator delegates, multi-pair conjugacy thin wrap, resonant recall); acceptance path: Joshua review + merge +**Status**: Proposed — substrate + entity suite + ADR-0242 packing/Fibonacci + P9 Trace A wave_seam (SPECULATIVE holographic seal / hypothesis-vs-evidence reconstruct); remaining cohesion packages P10 energy + P12 governance; acceptance path: Joshua review **Date**: 2026-07-13 **Deciders**: Joshua Shay + multi-model R&D **Traceability**: Issue #14, parent #10 diff --git a/docs/research/third-door-blueprint-fidelity.md b/docs/research/third-door-blueprint-fidelity.md index e30faaa8..f83878a2 100644 --- a/docs/research/third-door-blueprint-fidelity.md +++ b/docs/research/third-door-blueprint-fidelity.md @@ -296,11 +296,12 @@ PY | Phase correlation \(\rho\) (I-04 algebra) | 🟢 `phase_correlation` (sensorium feed still open) | | Surprise / GoldTether / biography delegate to wave | 🟢 | | No teaching import in `wave_manifold`; no `core_ha` package | 🟢 | -| Serve path not wired to wave / Fibonacci (containment) | 🟢 (AST-pinned in cohesion suite) | +| Serve path not wired to wave / Fibonacci (containment) | 🟢 (AST-pinned in cohesion suite; includes `wave_seam`) | | Entity I-01…I-05 cohesion suite | 🟢 progressive pins in `test_third_door_cohesion.py` (I-02 float32-honest) | | Vault public `get_versor` ABI | 🟢 | | Golden-Angle atlas packing \(d_{\min}=0.12\) | 🟢 ADR-0242 (`atlas_packing`; CGA null-point \(d\)) | | Fibonacci κ search | 🟢 ADR-0242 (`fibonacci_search`) | +| Contemplation Trace A SPECULATIVE holographic seal (P9) | 🟢 `core/contemplation/wave_seam.py` (hypothesis vs COHERENT evidence) | ### Subsumption map (Slice 2–3) | Operator | Delegation | @@ -335,6 +336,7 @@ PY | `core_ha` deprecation — 🟢 no live tree + hygiene + Phase 0 grep | ADR-0241 / deprecation plan | | Durable holographic vault spectrum — 🟢 HolographicVaultStore | ADR-0241 | | Entity cohesion I-01…I-05 + Trace A/B | cohesion master plan | +| Contemplation Trace A SPECULATIVE seal (P9) — 🟢 wave_seam | ADR-0241 P9 | | Atlas packing + Fibonacci κ (ADR-0242) — 🟢 packing + search | PR #37 / ADR-0242 | Closing a gap = flip its `xfail` in `tests/test_third_door_blueprint_fidelity.py` (or the ADR-0241 / cohesion suite) to a passing behavioral test and delete the matching characterization lock. That is the definition of "done right" here. diff --git a/tests/test_adr_0241_wave_contemplation_seam.py b/tests/test_adr_0241_wave_contemplation_seam.py new file mode 100644 index 00000000..fd991e1c --- /dev/null +++ b/tests/test_adr_0241_wave_contemplation_seam.py @@ -0,0 +1,164 @@ +"""P9 Trace A — contemplation → SPECULATIVE holographic seal → teaching corridor. + +ADR-0241 cohesion plan package P9: + * Contemplation may SPECULATIVE-seal standing-wave modes (no COHERENT). + * Resonant reconstruct as *hypothesis* may use SPECULATIVE spectrum. + * Resonant reconstruct as *evidence* requires min_status=COHERENT. + * Serve path remains quarantined from this seam. + * Writes go only through HolographicVaultStore.seal_mode (INV-21). +""" + +from __future__ import annotations + +import ast +from pathlib import Path + +import numpy as np +import pytest + +from algebra.rotor import make_rotor_from_angle +from core.contemplation.schema import FindingKind +from core.physics.holographic_vault import HolographicVaultError, HolographicVaultStore +from teaching.epistemic import EpistemicStatus +from vault.store import VaultStore + +from core.contemplation.wave_seam import ( + WaveModeHypothesis, + WaveReconstructResult, + reconstruct_as_evidence, + reconstruct_as_hypothesis, + speculative_seal_from_contemplation, +) + +_ROOT = Path(__file__).resolve().parents[1] + + +def _closed(angle: float = 0.3, plane: int = 6) -> np.ndarray: + return make_rotor_from_angle(angle, bivector_idx=plane) + + +# --- SPECULATIVE seal from contemplation ------------------------------------ + + +def test_speculative_seal_writes_speculative_only(): + hv = HolographicVaultStore(VaultStore()) + hyp = speculative_seal_from_contemplation( + hv, + _closed(0.21), + substrate_hash="sub-abc", + subject="mode-from-contemplation", + mode_id="p9-m0", + ) + assert isinstance(hyp, WaveModeHypothesis) + assert hyp.sealed.epistemic_status is EpistemicStatus.SPECULATIVE + assert hyp.sealed.mode_id == "p9-m0" + assert hyp.finding.epistemic_status is EpistemicStatus.SPECULATIVE + assert hyp.finding.kind is FindingKind.RESONANT_MODE_CANDIDATE + assert hyp.finding.substrate_hash == "sub-abc" + assert hyp.standing == "hypothesis" + # Evidence ref points at vault mode, not pack mutation. + assert any( + e.source_type == "holographic_vault" and "p9-m0" in e.source_id + for e in hyp.finding.evidence_refs + ) + + +def test_speculative_seal_refuses_non_closed(): + hv = HolographicVaultStore(VaultStore()) + dirty = np.zeros(32, dtype=np.float64) + dirty[0] = 0.5 + dirty[1] = 0.5 + with pytest.raises((HolographicVaultError, ValueError)): + speculative_seal_from_contemplation( + hv, dirty, substrate_hash="sub", subject="bad" + ) + + +def test_seam_has_no_coherent_write_surface(): + """Contemplation seam must not *call* seal_mode_reviewed or VaultStore.store.""" + src = (_ROOT / "core/contemplation/wave_seam.py").read_text(encoding="utf-8") + assert "min_status" in src # evidence path filters COHERENT; does not write it + tree = ast.parse(src) + for node in ast.walk(tree): + if not isinstance(node, ast.Call): + continue + func = node.func + if isinstance(func, ast.Attribute) and func.attr in { + "store", + "seal_mode_reviewed", + }: + pytest.fail( + f"wave_seam must not call .{func.attr}(...) " + "(INV-21 / no COHERENT self-write)" + ) + if isinstance(func, ast.Name) and func.id in {"store", "seal_mode_reviewed"}: + pytest.fail(f"wave_seam must not call {func.id}(...)") + + +# --- Hypothesis vs evidence reconstruct ------------------------------------- + + +def test_hypothesis_reconstruct_uses_speculative_spectrum(): + hv = HolographicVaultStore(VaultStore()) + psi = _closed(0.33, plane=7) + speculative_seal_from_contemplation( + hv, psi, substrate_hash="s", subject="m", mode_id="only-spec" + ) + result = reconstruct_as_hypothesis(hv, psi) + assert isinstance(result, WaveReconstructResult) + assert result.standing == "hypothesis" + assert result.psi_hat.shape == (32,) + # Overlap with the sealed mode should be strong. + assert float(np.linalg.norm(result.psi_hat)) > 0.0 + + +def test_evidence_reconstruct_refuses_speculative_only_spectrum(): + """Evidence path requires COHERENT modes — SPECULATIVE must not masquerade.""" + hv = HolographicVaultStore(VaultStore()) + psi = _closed(0.18) + speculative_seal_from_contemplation( + hv, psi, substrate_hash="s", subject="m", mode_id="spec-only" + ) + with pytest.raises((HolographicVaultError, ValueError), match="empty|COHERENT|evidence|spectrum"): + reconstruct_as_evidence(hv, psi) + + +def test_evidence_reconstruct_accepts_coherent_modes(): + hv = HolographicVaultStore(VaultStore()) + psi = _closed(0.27, plane=8) + # Teaching corridor: authorized COHERENT seal (not via contemplation seam). + hv.seal_mode_reviewed(psi, authorized=True, mode_id="reviewed") + result = reconstruct_as_evidence(hv, psi) + assert result.standing == "evidence" + assert result.min_status is EpistemicStatus.COHERENT + assert float(np.linalg.norm(result.psi_hat)) > 0.0 + + +def test_mixed_spectrum_evidence_ignores_speculative(): + hv = HolographicVaultStore(VaultStore()) + psi_coh = _closed(0.4, plane=6) + psi_spec = _closed(0.9, plane=9) + speculative_seal_from_contemplation( + hv, psi_spec, substrate_hash="s", subject="spec", mode_id="spec" + ) + hv.seal_mode_reviewed(psi_coh, authorized=True, mode_id="coh") + # Evidence reconstruct should lock to COHERENT geometry, not SPECULATIVE. + result = reconstruct_as_evidence(hv, psi_coh) + assert result.standing == "evidence" + # Coefficient mass on the single COHERENT mode. + assert result.coeffs.shape[0] == 1 + + +# --- Serve quarantine ------------------------------------------------------- + + +def test_serve_runtime_does_not_import_wave_seam(): + runtime_path = _ROOT / "chat/runtime.py" + tree = ast.parse(runtime_path.read_text(encoding="utf-8")) + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom) and node.module: + assert "wave_seam" not in node.module + assert "wave_contemplation" not in node.module + if isinstance(node, ast.Import): + for alias in node.names: + assert "wave_seam" not in alias.name diff --git a/tests/test_third_door_cohesion.py b/tests/test_third_door_cohesion.py index 075c7aeb..8a818d18 100644 --- a/tests/test_third_door_cohesion.py +++ b/tests/test_third_door_cohesion.py @@ -61,15 +61,22 @@ def test_phase0_a04_serve_path_quarantines_wave_and_fibonacci(): "holographic_vault", "fibonacci_search", "atlas_packing", + "wave_seam", # P9 Trace A — contemplation only, never serve } + banned_substrings = ( + "wave_manifold", + "holographic_vault", + "fibonacci_search", + "atlas_packing", + "wave_seam", + ) for node in ast.walk(tree): if isinstance(node, ast.Import): for alias in node.names: leaf = alias.name.split(".")[-1] assert leaf not in banned_roots, f"banned import {alias.name}" - assert "wave_manifold" not in alias.name - assert "holographic_vault" not in alias.name - assert "fibonacci_search" not in alias.name + for ban in banned_substrings: + assert ban not in alias.name if isinstance(node, ast.ImportFrom) and node.module: mod = node.module for ban in banned_roots: From f123e0ea75b602e851e7d4bd11e877decc0b0567 Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 14 Jul 2026 18:39:01 -0700 Subject: [PATCH 2/8] =?UTF-8?q?feat(wave):=20P10=20Trace=20B=20energy=20bo?= =?UTF-8?q?undary=20+=20multi-scale=20Fibonacci=20=CF=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Land ADR-0241 cohesion package P10: - core/physics/wave_energy_boundary.py wires WaveManifold unitary residual into energy profiles and trajectory energy gates (no free-floating residual). - fibonacci_tau_schedule: τ_n = F_n · τ_0 constants table + recency bands. - crystallization_for_holographic_seal: E0–E1 + closed residual may SPECULATIVE-seal; dirty residual or hot classes refuse. - Public fibonacci_number; serve quarantine includes wave_energy_boundary. - Fidelity + ADR-0241 status updated. Lane: pytest tests/test_adr_0241_wave_energy_boundary.py tests/test_adr_0242_fibonacci.py tests/test_energy.py tests/test_third_door_cohesion.py → green. --- core/physics/__init__.py | 18 ++ core/physics/fibonacci_search.py | 9 +- core/physics/wave_energy_boundary.py | 219 ++++++++++++++++++ ...hyperbolic-atlas-and-resonant-cognition.md | 2 +- .../research/third-door-blueprint-fidelity.md | 2 + tests/test_adr_0241_wave_energy_boundary.py | 151 ++++++++++++ tests/test_third_door_cohesion.py | 2 + 7 files changed, 401 insertions(+), 2 deletions(-) create mode 100644 core/physics/wave_energy_boundary.py create mode 100644 tests/test_adr_0241_wave_energy_boundary.py diff --git a/core/physics/__init__.py b/core/physics/__init__.py index ae2d6c75..215b11bd 100644 --- a/core/physics/__init__.py +++ b/core/physics/__init__.py @@ -82,6 +82,16 @@ from core.physics.holographic_vault import ( HolographicVaultStore, SealedMode, ) +from core.physics.wave_energy_boundary import ( + CrystallizationDecision, + assess_wave_trajectory, + crystallization_for_holographic_seal, + energy_profile_from_wave, + fibonacci_tau_schedule, + recency_band_index, + wave_unitary_residual, +) +from core.physics.fibonacci_search import fibonacci_number __all__ = [ "SalienceOperator", "SalienceMap", "FieldRegion", @@ -116,4 +126,12 @@ __all__ = [ "assess_trajectory", "energy_boundary_ok", "relative_holonomy", "trajectory_divergence", "HolographicVaultError", "HolographicVaultStore", "SealedMode", + "CrystallizationDecision", + "assess_wave_trajectory", + "crystallization_for_holographic_seal", + "energy_profile_from_wave", + "fibonacci_tau_schedule", + "recency_band_index", + "wave_unitary_residual", + "fibonacci_number", ] diff --git a/core/physics/fibonacci_search.py b/core/physics/fibonacci_search.py index 2d4b3df3..37276900 100644 --- a/core/physics/fibonacci_search.py +++ b/core/physics/fibonacci_search.py @@ -41,7 +41,7 @@ class SearchTrace: certificate: dict = field(default_factory=dict) -def _fibonacci(n: int) -> int: +def fibonacci_number(n: int) -> int: """F_0=0, F_1=1, … standard Fibonacci. n may be 0.""" if n < 0: raise ValueError("fibonacci index must be non-negative") @@ -51,6 +51,11 @@ def _fibonacci(n: int) -> int: return a +def _fibonacci(n: int) -> int: + """Internal alias — prefer :func:`fibonacci_number` at call sites.""" + return fibonacci_number(n) + + def _assert_sampled_unimodality(eval_values: dict[float, float]) -> None: """Fail-closed if sorted samples are not unimodal about the observed min.""" sorted_points = sorted(eval_values.keys()) @@ -168,6 +173,8 @@ def fibonacci_section_search( __all__ = [ + "fibonacci_number", + "BoundedUnimodalObjective", "SearchTrace", "fibonacci_section_search", diff --git a/core/physics/wave_energy_boundary.py b/core/physics/wave_energy_boundary.py new file mode 100644 index 00000000..4474bdab --- /dev/null +++ b/core/physics/wave_energy_boundary.py @@ -0,0 +1,219 @@ +"""P10 Trace B — wave unitary residual → energy boundary + multi-scale τ. + +ADR-0241 cohesion package P10: + +1. **Wire wave residual into energy / trajectory gates** — coherence is not a + free-floating float; it is :meth:`WaveManifold.measure_unitary_residual`. +2. **Multi-scale recency** ``τ_n = F_n · τ_0`` as a constants table (not dogma). +3. **Crystallization E0–E1 → holographic seal policy** — only low-energy + classes with closed residual may SPECULATIVE-seal; COHERENT still requires + authorized teaching review outside this module. + +Serve path remains quarantined (no import from ``chat/runtime.py``). +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Sequence + +import numpy as np + +from core.physics.energy import EnergyClass, EnergyProfile, FieldEnergyOperator +from core.physics.fibonacci_search import fibonacci_number +from core.physics.trajectory_invariants import ( + TrajectoryAssessment, + assess_trajectory, +) +from core.physics.wave_manifold import WaveManifold + +_DEFAULT_EPSILON_DRIFT = 1e-6 +_DEFAULT_TAU0 = 1.0 + + +def wave_unitary_residual( + psi: np.ndarray, + *, + manifold: WaveManifold | None = None, + epsilon_drift: float = _DEFAULT_EPSILON_DRIFT, +) -> float: + """Unitary residual ‖ψ~ψ − 1‖_F dual-checked via WaveManifold.""" + m = manifold if manifold is not None else WaveManifold(epsilon_drift=epsilon_drift) + return float(m.measure_unitary_residual(psi)) + + +def energy_profile_from_wave( + psi: np.ndarray, + *, + operator: FieldEnergyOperator | None = None, + manifold: WaveManifold | None = None, + epsilon_drift: float = _DEFAULT_EPSILON_DRIFT, + **compute_kwargs: object, +) -> EnergyProfile: + """Build an EnergyProfile with coherence_residual from the wave field. + + Structural axes (convergence, activation, aspect) remain caller-supplied; + residual is never invented — it is measured on ψ. + """ + residual = wave_unitary_residual( + psi, manifold=manifold, epsilon_drift=epsilon_drift + ) + op = operator if operator is not None else FieldEnergyOperator() + # FieldEnergyOperator.compute takes coherence_residual as float kwarg. + kwargs = dict(compute_kwargs) + kwargs["coherence_residual"] = residual + return op.compute(**kwargs) # type: ignore[arg-type] + + +def assess_wave_trajectory( + versors: Sequence[np.ndarray], + *, + eps_trajectory: float = 1e-5, + kappa: float = 1.0, + dt: float = 1.0, + epsilon_drift: float = _DEFAULT_EPSILON_DRIFT, + manifold: WaveManifold | None = None, +) -> TrajectoryAssessment: + """Trajectory gate with exertion energy from max wave unitary residual. + + ``E_exertion`` = max residual along the path. + ``E_sensory`` = ``epsilon_drift`` (GoldTether-scale sensory budget). + Closed unit paths stay under the boundary when residual ≤ κ · ε. + """ + if not versors: + # Delegate fail-closed empty handling to trajectory_invariants. + return assess_trajectory( + versors, + E_exertion=0.0, + E_sensory=float(epsilon_drift), + eps_trajectory=eps_trajectory, + kappa=kappa, + dt=dt, + ) + residuals = [ + wave_unitary_residual(v, manifold=manifold, epsilon_drift=epsilon_drift) + for v in versors + ] + e_exertion = float(max(residuals)) + e_sensory = float(epsilon_drift) + return assess_trajectory( + versors, + E_exertion=e_exertion, + E_sensory=e_sensory, + eps_trajectory=eps_trajectory, + kappa=kappa, + dt=dt, + ) + + +def fibonacci_tau_schedule( + tau0: float = _DEFAULT_TAU0, + *, + levels: int = 8, +) -> tuple[float, ...]: + """Multi-scale recency hierarchy τ_n = F_n · τ_0 for n = 1..levels. + + Constants table only — not a runtime dogma. F_1=1, F_2=1, F_3=2, … + """ + t0 = float(tau0) + if not (t0 > 0.0) or t0 != t0: # NaN check via != + raise ValueError("tau0 must be a positive finite scalar") + n = int(levels) + if n < 1: + raise ValueError("levels must be >= 1") + return tuple(float(fibonacci_number(i) * t0) for i in range(1, n + 1)) + + +def recency_band_index(age: float, taus: Sequence[float]) -> int: + """Smallest band index with age ≤ τ_n, or ``len(taus)`` if beyond schedule.""" + a = float(age) + if a < 0.0: + raise ValueError("age must be non-negative") + for i, tau in enumerate(taus): + if a <= float(tau) + 1e-15: + return i + return len(taus) + + +@dataclass(frozen=True, slots=True) +class CrystallizationDecision: + """E0–E1 crystallization gate aligned with holographic SPECULATIVE seal policy.""" + + energy: EnergyProfile + unitary_residual: float + vault_candidate: bool + residual_closed: bool + may_speculative_seal: bool + reason: str + epsilon_drift: float + + def as_dict(self) -> dict[str, object]: + return { + "energy_class": self.energy.energy_class.value, + "unitary_residual": self.unitary_residual, + "vault_candidate": self.vault_candidate, + "residual_closed": self.residual_closed, + "may_speculative_seal": self.may_speculative_seal, + "reason": self.reason, + "epsilon_drift": self.epsilon_drift, + } + + +def crystallization_for_holographic_seal( + psi: np.ndarray, + *, + epsilon_drift: float = _DEFAULT_EPSILON_DRIFT, + manifold: WaveManifold | None = None, + operator: FieldEnergyOperator | None = None, + **energy_kwargs: object, +) -> CrystallizationDecision: + """Decide whether ψ may enter the SPECULATIVE holographic seal path. + + Policy (Trace B ↔ Trace A): + * residual must be closed (≤ epsilon_drift) — fail-closed, no repair + * energy class must be vault_candidate (E0/E1) + * both required for ``may_speculative_seal`` + + COHERENT promotion is never authorized here. + """ + residual = wave_unitary_residual( + psi, manifold=manifold, epsilon_drift=epsilon_drift + ) + energy = energy_profile_from_wave( + psi, + operator=operator, + manifold=manifold, + epsilon_drift=epsilon_drift, + **energy_kwargs, + ) + vault = bool(energy.energy_class.vault_candidate) + closed = bool(residual <= float(epsilon_drift)) + may_seal = vault and closed + if may_seal: + reason = "e0_e1_closed_residual_speculative_seal_ok" + elif not closed: + reason = "residual_not_closed" + elif not vault: + reason = "energy_class_not_vault_candidate" + else: + reason = "refused" + return CrystallizationDecision( + energy=energy, + unitary_residual=residual, + vault_candidate=vault, + residual_closed=closed, + may_speculative_seal=may_seal, + reason=reason, + epsilon_drift=float(epsilon_drift), + ) + + +__all__ = [ + "CrystallizationDecision", + "assess_wave_trajectory", + "crystallization_for_holographic_seal", + "energy_profile_from_wave", + "fibonacci_tau_schedule", + "recency_band_index", + "wave_unitary_residual", +] diff --git a/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md b/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md index 1f518531..49a37d03 100644 --- a/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md +++ b/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md @@ -1,6 +1,6 @@ # ADR-0241: Wave-Field Driven Hyperbolic Atlas and Resonant Algebraic Cognition -**Status**: Proposed — substrate + entity suite + ADR-0242 packing/Fibonacci + P9 Trace A wave_seam (SPECULATIVE holographic seal / hypothesis-vs-evidence reconstruct); remaining cohesion packages P10 energy + P12 governance; acceptance path: Joshua review +**Status**: Proposed — substrate + entity suite + ADR-0242 packing/Fibonacci + P9 Trace A wave_seam + P10 Trace B wave_energy_boundary (residual→energy/τ/crystallization); remaining cohesion package P12 governance (+ optional P11 Rust); acceptance path: Joshua review **Date**: 2026-07-13 **Deciders**: Joshua Shay + multi-model R&D **Traceability**: Issue #14, parent #10 diff --git a/docs/research/third-door-blueprint-fidelity.md b/docs/research/third-door-blueprint-fidelity.md index f83878a2..f5c6c848 100644 --- a/docs/research/third-door-blueprint-fidelity.md +++ b/docs/research/third-door-blueprint-fidelity.md @@ -302,6 +302,7 @@ PY | Golden-Angle atlas packing \(d_{\min}=0.12\) | 🟢 ADR-0242 (`atlas_packing`; CGA null-point \(d\)) | | Fibonacci κ search | 🟢 ADR-0242 (`fibonacci_search`) | | Contemplation Trace A SPECULATIVE holographic seal (P9) | 🟢 `core/contemplation/wave_seam.py` (hypothesis vs COHERENT evidence) | +| Energy boundary + multi-scale τ (P10 Trace B) | 🟢 `wave_energy_boundary` (wave residual → energy/trajectory; τ_n=F_n·τ_0; E0–E1 crystallization) | ### Subsumption map (Slice 2–3) | Operator | Delegation | @@ -337,6 +338,7 @@ PY | Durable holographic vault spectrum — 🟢 HolographicVaultStore | ADR-0241 | | Entity cohesion I-01…I-05 + Trace A/B | cohesion master plan | | Contemplation Trace A SPECULATIVE seal (P9) — 🟢 wave_seam | ADR-0241 P9 | +| Energy boundary + multi-scale τ (P10) — 🟢 wave_energy_boundary | ADR-0241 P10 | | Atlas packing + Fibonacci κ (ADR-0242) — 🟢 packing + search | PR #37 / ADR-0242 | Closing a gap = flip its `xfail` in `tests/test_third_door_blueprint_fidelity.py` (or the ADR-0241 / cohesion suite) to a passing behavioral test and delete the matching characterization lock. That is the definition of "done right" here. diff --git a/tests/test_adr_0241_wave_energy_boundary.py b/tests/test_adr_0241_wave_energy_boundary.py new file mode 100644 index 00000000..9f09cd70 --- /dev/null +++ b/tests/test_adr_0241_wave_energy_boundary.py @@ -0,0 +1,151 @@ +"""P10 Trace B — wave residual → energy boundary + Fibonacci τ + crystallization. + +ADR-0241 cohesion package P10: + * Unitary residual from WaveManifold feeds energy / trajectory gates. + * Multi-scale recency τ_n = F_n · τ_0 is a constants table (not dogma). + * E0–E1 crystallization + closed residual may SPECULATIVE-seal; else refuse. + * Serve remains quarantined from this module. +""" + +from __future__ import annotations + +import ast +from pathlib import Path + +import numpy as np +import pytest + +from algebra.rotor import make_rotor_from_angle +from core.physics.energy import EnergyClass +from core.physics.wave_energy_boundary import ( + CrystallizationDecision, + assess_wave_trajectory, + crystallization_for_holographic_seal, + energy_profile_from_wave, + fibonacci_tau_schedule, + recency_band_index, + wave_unitary_residual, +) +from core.physics.trajectory_invariants import TrajectoryInvariantError + +_ROOT = Path(__file__).resolve().parents[1] + + +def _closed(angle: float = 0.3, plane: int = 6) -> np.ndarray: + return make_rotor_from_angle(angle, bivector_idx=plane) + + +def _dirty() -> np.ndarray: + v = np.zeros(32, dtype=np.float64) + v[0] = 0.5 + v[1] = 0.5 + return v + + +# --- Wave residual wiring --------------------------------------------------- + + +def test_wave_unitary_residual_closed_under_eps(): + r = wave_unitary_residual(_closed(0.25)) + assert r < 1e-6 + + +def test_wave_unitary_residual_dirty_above_eps(): + r = wave_unitary_residual(_dirty()) + assert r > 1e-6 + + +def test_energy_profile_from_wave_uses_residual(): + profile = energy_profile_from_wave(_closed(0.1)) + assert profile.coherence_residual < 1e-6 + # Low residual + defaults → crystallizable class (E0/E1). + assert profile.energy_class in {EnergyClass.E0, EnergyClass.E1} + + +def test_energy_profile_from_wave_high_residual_feeds_raw(): + profile = energy_profile_from_wave(_dirty()) + # Operator clamps residual into [0,1] for the scalar mix. + assert profile.coherence_residual > 0.0 + + +def test_assess_wave_trajectory_closed_path_energy_ok(): + steps = [_closed(0.1 * i, plane=6) for i in range(1, 5)] + assessment = assess_wave_trajectory(steps) + assert assessment.energy_ok is True + assert assessment.n_steps == 4 + assert assessment.within_replay_bound is True + + +def test_assess_wave_trajectory_refuses_empty(): + with pytest.raises(TrajectoryInvariantError): + assess_wave_trajectory([]) + + +# --- Fibonacci multi-scale τ ------------------------------------------------ + + +def test_fibonacci_tau_schedule_matches_F_n_tau0(): + # F_1=1, F_2=1, F_3=2, F_4=3, F_5=5 + taus = fibonacci_tau_schedule(tau0=0.5, levels=5) + assert taus == (0.5, 0.5, 1.0, 1.5, 2.5) + + +def test_fibonacci_tau_schedule_rejects_bad_inputs(): + with pytest.raises(ValueError): + fibonacci_tau_schedule(tau0=0.0, levels=3) + with pytest.raises(ValueError): + fibonacci_tau_schedule(tau0=1.0, levels=0) + + +def test_recency_band_index_progressive(): + taus = fibonacci_tau_schedule(tau0=1.0, levels=5) # 1,1,2,3,5 + assert recency_band_index(0.5, taus) == 0 + assert recency_band_index(1.0, taus) == 0 + assert recency_band_index(1.5, taus) == 2 + assert recency_band_index(10.0, taus) == len(taus) # beyond schedule + + +# --- Crystallization ↔ holographic seal policy ------------------------------ + + +def test_crystallization_closed_e0_may_speculative_seal(): + decision = crystallization_for_holographic_seal(_closed(0.12)) + assert isinstance(decision, CrystallizationDecision) + assert decision.residual_closed is True + assert decision.vault_candidate is True + assert decision.may_speculative_seal is True + assert decision.energy.energy_class.vault_candidate is True + + +def test_crystallization_dirty_refuses_seal(): + decision = crystallization_for_holographic_seal(_dirty()) + assert decision.residual_closed is False + assert decision.may_speculative_seal is False + + +def test_crystallization_high_activity_not_vault_candidate(): + # Force E3/E4 via structural inputs while residual stays closed. + decision = crystallization_for_holographic_seal( + _closed(0.05), + convergence_density=20, + activation_count=20, + current_cycle=1, + last_activation_cycle=1, + anchor_adjacent=True, + ) + assert decision.residual_closed is True + assert decision.vault_candidate is False + assert decision.may_speculative_seal is False + + +# --- Serve quarantine ------------------------------------------------------- + + +def test_serve_runtime_does_not_import_wave_energy_boundary(): + tree = ast.parse((_ROOT / "chat/runtime.py").read_text(encoding="utf-8")) + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom) and node.module: + assert "wave_energy_boundary" not in node.module + if isinstance(node, ast.Import): + for alias in node.names: + assert "wave_energy_boundary" not in alias.name diff --git a/tests/test_third_door_cohesion.py b/tests/test_third_door_cohesion.py index 8a818d18..2b109bec 100644 --- a/tests/test_third_door_cohesion.py +++ b/tests/test_third_door_cohesion.py @@ -62,6 +62,7 @@ def test_phase0_a04_serve_path_quarantines_wave_and_fibonacci(): "fibonacci_search", "atlas_packing", "wave_seam", # P9 Trace A — contemplation only, never serve + "wave_energy_boundary", # P10 Trace B — energy/τ gate, never serve } banned_substrings = ( "wave_manifold", @@ -69,6 +70,7 @@ def test_phase0_a04_serve_path_quarantines_wave_and_fibonacci(): "fibonacci_search", "atlas_packing", "wave_seam", + "wave_energy_boundary", ) for node in ast.walk(tree): if isinstance(node, ast.Import): From 9d543f6a9c5008f4fec137ac24b55a866ad16bdc Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 14 Jul 2026 18:41:24 -0700 Subject: [PATCH 3/8] =?UTF-8?q?docs(governance):=20P12=20cohesion=20close?= =?UTF-8?q?=20=E2=80=94=20contracts,=20checklist,=20ready-for-accept?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADR-0241 cohesion package P12 (governance): - runtime_contracts: off-serve quarantine, holographic SPECULATIVE/COHERENT, hypothesis vs evidence reconstruct, crystallization, entity suite pins. - docs/audit/adr_0241_cohesion_acceptance_checklist.md maps C0–C8 → tests. - ADRs remain Proposed + ready for Joshua acceptance (no self-Accept). - Fidelity §12 honesty: P0–P10 green; human Accepted is the gate. - tests/test_adr_0241_governance_p12.py pins inventory + status honesty. CLAIMS.md Tier-2 lanes unchanged (I-01…I-05 are suite pins, not lane SHAs). --- ...hyperbolic-atlas-and-resonant-cognition.md | 2 +- .../ADR-0242-atlas-packing-and-fibonacci.md | 2 +- .../adr_0241_cohesion_acceptance_checklist.md | 61 ++++++++++ .../research/third-door-blueprint-fidelity.md | 21 ++-- docs/specs/runtime_contracts.md | 66 +++++++++++ tests/test_adr_0241_governance_p12.py | 105 ++++++++++++++++++ 6 files changed, 245 insertions(+), 12 deletions(-) create mode 100644 docs/audit/adr_0241_cohesion_acceptance_checklist.md create mode 100644 tests/test_adr_0241_governance_p12.py diff --git a/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md b/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md index 49a37d03..9f359eec 100644 --- a/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md +++ b/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md @@ -1,6 +1,6 @@ # ADR-0241: Wave-Field Driven Hyperbolic Atlas and Resonant Algebraic Cognition -**Status**: Proposed — substrate + entity suite + ADR-0242 packing/Fibonacci + P9 Trace A wave_seam + P10 Trace B wave_energy_boundary (residual→energy/τ/crystallization); remaining cohesion package P12 governance (+ optional P11 Rust); acceptance path: Joshua review +**Status**: Proposed — **implementation complete (P0–P10)**; ready for Joshua acceptance review (do not self-Accept). Optional P11 Rust deferred. Checklist: `docs/audit/adr_0241_cohesion_acceptance_checklist.md`. **Date**: 2026-07-13 **Deciders**: Joshua Shay + multi-model R&D **Traceability**: Issue #14, parent #10 diff --git a/docs/adr/ADR-0242-atlas-packing-and-fibonacci.md b/docs/adr/ADR-0242-atlas-packing-and-fibonacci.md index d75d0118..ee866445 100644 --- a/docs/adr/ADR-0242-atlas-packing-and-fibonacci.md +++ b/docs/adr/ADR-0242-atlas-packing-and-fibonacci.md @@ -1,6 +1,6 @@ # ADR-0242: Hyperbolic Atlas Golden-Angle Packing and Fibonacci Search -**Status**: Proposed — packing + Fibonacci search green on branch; acceptance path: Joshua review + merge +**Status**: Proposed — packing + Fibonacci search + multi-scale τ schedule green; **ready for Joshua acceptance review** (do not self-Accept). Checklist: `docs/audit/adr_0241_cohesion_acceptance_checklist.md`. **Date**: 2026-07-14 **Deciders**: Joshua Shay + multi-model R&D (Gemini implementation pass) **Traceability**: PR #37, parent ADR-0241 / cohesion master plan diff --git a/docs/audit/adr_0241_cohesion_acceptance_checklist.md b/docs/audit/adr_0241_cohesion_acceptance_checklist.md new file mode 100644 index 00000000..2033e1d7 --- /dev/null +++ b/docs/audit/adr_0241_cohesion_acceptance_checklist.md @@ -0,0 +1,61 @@ +# ADR-0241 / ADR-0242 Cohesion Acceptance Checklist + +**Status:** Implementation packages P0–P10 green; human **Accepted** flip is Joshua review. +**Plan authority:** session plan P0–P12 (`feat/adr-0241-0242-implementation` lineage). +**This document:** maps cohesion success criteria to **tests**, not prose alone. + +--- + +## Cohesion-complete criteria (plan C0–C8) + +| ID | Criterion | Proof (tests / modules) | +|----|-----------|-------------------------| +| C0 | Cohesion master plan in-repo; Phase 0 A-01…A-04 | `docs/analysis/core_cohesion_master_plan.md`; `tests/test_third_door_cohesion.py` (`test_phase0_*`, deprecation grep) | +| C1 | I-01…I-05 suite under honest tolerances | `test_i01_*` … `test_i05_*` in `tests/test_third_door_cohesion.py` (I-02 float32-honest) | +| C2 | Vault public ABI; no private `_versors` | `test_holographic_vault_does_not_touch_private_versors`; `VaultStore.get_versor` | +| C3 | Serve + Fibonacci + packing + seam quarantine | `test_phase0_a04_serve_path_quarantines_wave_and_fibonacci` | +| C4 | Superposition reconstruct | `test_resonant_reconstruct_*`; `WaveManifold.resonant_reconstruct` | +| C5 | Multimodal ρ (I-04 algebra) | `test_i04_phase_correlation_*` (sensorium feed still open — not blocking algebra pin) | +| C6 | Contemplation SPECULATIVE holographic seam (P9) | `tests/test_adr_0241_wave_contemplation_seam.py` | +| C7 | Pre-deprecation grep CI-green | `test_pre_deprecation_grep_*`, `test_core_ha_package_absent` | +| C8 | runtime_contracts + ADR acceptance path | this checklist + `docs/specs/runtime_contracts.md` § Wave-field cohesion; ADRs **Proposed — ready for Joshua acceptance** | + +## Absolute-mastery add-ons (landed) + +| Package | Proof | +|---------|--------| +| P4 Golden-Angle packing | `tests/test_adr_0242_atlas_packing.py` | +| P5 Fibonacci search | `tests/test_adr_0242_fibonacci.py` | +| P7 polar honesty | `tests/test_adr_0241_wave_manifold.py` (conjugacy authority; multi-grade analytic retired) | +| P8 non-vacuous chiral | chiral suite in `test_adr_0241_wave_manifold.py` | +| P10 energy + τ | `tests/test_adr_0241_wave_energy_boundary.py` | + +## Explicit non-goals (do not block acceptance) + +- Serve-path wiring of wave / Fibonacci +- Resurrecting `core_ha` +- Cosine/ANN multimodal matching +- Hot-path silent unitize / nearest-versor repair +- Continuous \(\psi(X,t)\) continuum solver +- P11 Rust/MLX (optional mechanical sympathy) +- Sensorium compiler feed into ρ (algebra green; feed open) + +## Human gate + +Joshua review may flip: + +- `docs/adr/ADR-0241-…md` → **Accepted** +- `docs/adr/ADR-0242-…md` → **Accepted** + +Agents must **not** self-accept. Implementation complete ≠ Accepted. + +## Validation lane + +```bash +python3 -m pytest \ + tests/test_third_door_cohesion.py \ + tests/test_adr_0241_*.py \ + tests/test_adr_0242_*.py \ + tests/test_adr_0241_governance_p12.py \ + -q +``` diff --git a/docs/research/third-door-blueprint-fidelity.md b/docs/research/third-door-blueprint-fidelity.md index f5c6c848..6c8d9b35 100644 --- a/docs/research/third-door-blueprint-fidelity.md +++ b/docs/research/third-door-blueprint-fidelity.md @@ -266,14 +266,14 @@ PY --- -## 12. Wave-field substrate (ADR-0241) — 🟢 local operators / 🟡 entity mastery +## 12. Wave-field substrate (ADR-0241) — 🟢 local + cohesion packages P0–P10 -> **Status (2026-07-14, honesty pass):** Local Slice 1–3 + holographic vault -> behavioral suites are **GREEN**. Entity cohesion (I-01…I-05, Trace A/B, -> Golden-Angle packing, true \(\mathcal{C}_{AB}\) polar, non-vacuous chiral, -> multimodal \(\rho\)) is the remaining mastery surface — see -> `docs/analysis/core_cohesion_master_plan.md` and -> `tests/test_third_door_cohesion.py`. +> **Status (2026-07-15, P12 honesty pass):** Local operators **and** entity +> cohesion packages P0–P10 are **GREEN** (I-01…I-05 suite, Trace A/B seams, +> packing, Fibonacci, polar honesty, chiral, ρ algebra). Human **Accepted** +> flip is Joshua review only — see +> `docs/audit/adr_0241_cohesion_acceptance_checklist.md`. Still open by design: +> sensorium feed into ρ, optional P11 Rust/MLX, serve remains quarantined. ### Spec (ADR-0241) — contract - Continuous multivector wave-field \(\psi \in Cl(4,1)\) (32-coeff) under Cartan/Procrustes, Surprise, GoldTether, Biography. @@ -333,12 +333,13 @@ PY | Grade-5 pseudoscalar preservation gate — ⚪ RETIRED (vacuous; see §5) | #19 (closed) | | Surprise: metric projection + productivity polarity + DiscoveryCandidate wiring — 🟢 done | #20 (math #26; wiring #31) | | Trajectory invariants + ADR-DAG embedding — 🟢 Python surfaces | #21 | -| Wave-field local operators + subsumption (W1–W6) — 🟢 local / 🟡 entity mastery | ADR-0241 | +| Wave-field local operators + subsumption (W1–W6) — 🟢 | ADR-0241 | +| Entity cohesion I-01…I-05 + Trace A/B + P9/P10 — 🟢 | ADR-0241 / cohesion plan | | `core_ha` deprecation — 🟢 no live tree + hygiene + Phase 0 grep | ADR-0241 / deprecation plan | | Durable holographic vault spectrum — 🟢 HolographicVaultStore | ADR-0241 | -| Entity cohesion I-01…I-05 + Trace A/B | cohesion master plan | | Contemplation Trace A SPECULATIVE seal (P9) — 🟢 wave_seam | ADR-0241 P9 | | Energy boundary + multi-scale τ (P10) — 🟢 wave_energy_boundary | ADR-0241 P10 | -| Atlas packing + Fibonacci κ (ADR-0242) — 🟢 packing + search | PR #37 / ADR-0242 | +| Atlas packing + Fibonacci κ (ADR-0242) — 🟢 packing + search | ADR-0242 | +| Governance close (P12) — 🟢 contracts + checklist + ready-for-accept | ADR-0241 P12 | Closing a gap = flip its `xfail` in `tests/test_third_door_blueprint_fidelity.py` (or the ADR-0241 / cohesion suite) to a passing behavioral test and delete the matching characterization lock. That is the definition of "done right" here. diff --git a/docs/specs/runtime_contracts.md b/docs/specs/runtime_contracts.md index 495b9e5a..e89bcc42 100644 --- a/docs/specs/runtime_contracts.md +++ b/docs/specs/runtime_contracts.md @@ -849,3 +849,69 @@ migration PR with verifier parity evidence. - Frontier baseline: `evals/gsm8k_math/baselines/` - Sealed holdout: `evals/gsm8k_math/holdouts/v1/cases.jsonl.age` - ADR chain: ADR-0119, ADR-0119.1 through ADR-0119.8 + +## Wave-field cohesion substrate (ADR-0241 + ADR-0242) + +This section freezes the **off-serve** wave / holographic / packing contracts +landed under the ADR-0241 cohesion plan (packages P0–P10). It prevents drift +between physics modules, contemplation Trace A, energy Trace B, and serve +containment. + +### Off-serve quarantine (hard) + +The following modules **must not** be imported by `chat/runtime.py` or any +wrong=0 serve entry path (AST-pinned in `tests/test_third_door_cohesion.py`): + +| Module | Role | +|--------|------| +| `core.physics.wave_manifold` | Cl(4,1) wave field \(\psi\), leakage, polar conjugacy, chiral, \(\rho\) | +| `core.physics.holographic_vault` | Durable standing-wave spectrum via `VaultStore` | +| `core.physics.atlas_packing` | Golden-Angle mode packing (ADR-0242) | +| `core.physics.fibonacci_search` | Fixed-budget unimodal section search (ADR-0242) | +| `core.contemplation.wave_seam` | P9 Trace A SPECULATIVE seal + hypothesis/evidence reconstruct | +| `core.physics.wave_energy_boundary` | P10 Trace B residual→energy / \(\tau_n\) / crystallization | + +Wiring any of these into serve requires an explicit ADR amendment and a +failing-to-green containment test change in the same PR. + +### Holographic standing-wave epistemic standing + +- Default seal path: `HolographicVaultStore.seal_mode` → **SPECULATIVE** only. +- COHERENT seal: `seal_mode_reviewed(..., authorized=True)` only — never from + contemplation / self-authorship / wave_seam. +- Writes use `VaultStore.store` exclusively (INV-21 allowlist includes + `core/physics/holographic_vault.py`). No parallel memory path. +- Restart reconstruct uses public `VaultStore.get_versor` / `iter_metadata` + (no private `_versors`). + +### Hypothesis vs evidence reconstruct (Trace A) + +| API | Spectrum filter | Standing label | +|-----|-----------------|----------------| +| `reconstruct_as_hypothesis` | full (incl. SPECULATIVE) | `hypothesis` | +| `reconstruct_as_evidence` | `min_status=COHERENT` only | `evidence` | + +SPECULATIVE modes **must not** masquerade as reviewed evidence. Empty COHERENT +spectrum on the evidence path refuses (no confabulation). + +### Energy crystallization (Trace B) + +- Unitary residual for energy / trajectory gates is measured by + `WaveManifold.measure_unitary_residual` (via `wave_energy_boundary`), not a + free-floating float. +- Multi-scale recency table \(\tau_n = F_n \tau_0\) is a **constants schedule**, + not a serve-path optimizer. +- `crystallization_for_holographic_seal`: E0/E1 **and** residual ≤ ε may + SPECULATIVE-seal; otherwise refuse. Never self-authorizes COHERENT. + +### Entity invariants (suite-pinned, not Tier-2 lane SHAs) + +I-01…I-05 and Phase 0 audits are behavioral pins in +`tests/test_third_door_cohesion.py` (and related ADR-0241 tests). They are **not** +Tier-2 `CLAIMS.md` lane-SHA rows (those remain eval-lane report digests only). +Acceptance inventory: `docs/audit/adr_0241_cohesion_acceptance_checklist.md`. + +### Field invariant (unchanged) + +Every wave transition still obeys `versor_condition(F) < 1e-6`. Residual breach +is **fail-closed** — no hot-path nearest-versor drift repair. diff --git a/tests/test_adr_0241_governance_p12.py b/tests/test_adr_0241_governance_p12.py new file mode 100644 index 00000000..a295282b --- /dev/null +++ b/tests/test_adr_0241_governance_p12.py @@ -0,0 +1,105 @@ +"""P12 governance close — contracts, checklist, and module inventory pins. + +Does not re-execute the full physics suite. Asserts the load-bearing +governance surfaces for ADR-0241/0242 cohesion remain present and honest: + * runtime_contracts documents off-serve quarantine + epistemic standing + * acceptance checklist maps C0–C8 to tests + * ADRs stay Proposed (ready for Joshua) — not self-Accepted + * cohesion suite still names I-01…I-05 pins +""" + +from __future__ import annotations + +from pathlib import Path + +_ROOT = Path(__file__).resolve().parents[1] + +_QUARANTINE_NAMES = ( + "wave_manifold", + "holographic_vault", + "atlas_packing", + "fibonacci_search", + "wave_seam", + "wave_energy_boundary", +) + +_REQUIRED_MODULES = ( + "core/physics/wave_manifold.py", + "core/physics/holographic_vault.py", + "core/physics/atlas_packing.py", + "core/physics/fibonacci_search.py", + "core/contemplation/wave_seam.py", + "core/physics/wave_energy_boundary.py", + "docs/analysis/core_cohesion_master_plan.md", + "docs/audit/adr_0241_cohesion_acceptance_checklist.md", + "docs/specs/runtime_contracts.md", + "docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md", + "docs/adr/ADR-0242-atlas-packing-and-fibonacci.md", + "tests/test_third_door_cohesion.py", +) + + +def test_required_cohesion_modules_exist(): + missing = [p for p in _REQUIRED_MODULES if not (_ROOT / p).is_file()] + assert not missing, f"missing governance surfaces: {missing}" + + +def test_runtime_contracts_documents_wave_quarantine(): + text = (_ROOT / "docs/specs/runtime_contracts.md").read_text(encoding="utf-8") + assert "Wave-field cohesion substrate" in text + assert "Off-serve quarantine" in text + for name in _QUARANTINE_NAMES: + assert name in text, f"runtime_contracts missing quarantine name {name}" + assert "SPECULATIVE" in text + assert "seal_mode_reviewed" in text or "authorized=True" in text + assert "reconstruct_as_evidence" in text + assert "crystallization_for_holographic_seal" in text + + +def test_acceptance_checklist_maps_c0_c8_to_tests(): + text = ( + _ROOT / "docs/audit/adr_0241_cohesion_acceptance_checklist.md" + ).read_text(encoding="utf-8") + for cid in ("C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8"): + assert cid in text + assert "test_third_door_cohesion.py" in text + assert "Joshua" in text + assert "must **not** self-accept" in text or "must not self-accept" in text.lower() + + +def test_cohesion_suite_names_entity_invariants(): + text = (_ROOT / "tests/test_third_door_cohesion.py").read_text(encoding="utf-8") + for inv in ("i01", "i02", "i03", "i04", "i05"): + assert f"test_{inv}_" in text or f"test_i0" in text + # Explicit per-invariant function names (progressive suite). + for name in ( + "test_i01_biography_holonomy_closed_and_modes_reloadable", + "test_i02_holographic_round_trip_float32_honest", + "test_i03_self_authorship_proposals_are_speculative_only", + "test_i04_phase_correlation_symmetric_algebraic", + "test_i05_unitary_propagator_amplitude_conservation", + ): + assert name in text, f"missing entity pin {name}" + + +def test_adrs_ready_for_acceptance_not_self_accepted(): + """P12: implementation complete → Proposed + ready; Joshua alone Accepts.""" + for rel in ( + "docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md", + "docs/adr/ADR-0242-atlas-packing-and-fibonacci.md", + ): + text = (_ROOT / rel).read_text(encoding="utf-8") + # First status line must remain Proposed until human Accept. + status_line = next( + (ln for ln in text.splitlines() if ln.startswith("**Status**")), + "", + ) + assert "Proposed" in status_line, f"{rel} lost Proposed status" + assert "Accepted" not in status_line, f"{rel} must not self-Accept" + assert "Joshua" in status_line or "ready" in status_line.lower() + + +def test_serve_quarantine_list_matches_cohesion_ast_pin(): + cohesion = (_ROOT / "tests/test_third_door_cohesion.py").read_text(encoding="utf-8") + for name in _QUARANTINE_NAMES: + assert name in cohesion, f"cohesion AST pin missing {name}" From bbd3b6678fe4901e7aef1cc95642de0ef57ea178 Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 14 Jul 2026 19:53:43 -0700 Subject: [PATCH 4/8] feat(adr-0242): Drive V1 cert discipline + doc align five vectors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Close the gap between cohesion packing/search and Drive ADR-0242: D0 — Expand ADR-0242 to five-vector + sovereignty thesis (title matches Drive). D8 — Land docs/analysis/fibonacci_applications_in_core_substrate.md. D1 — FibonacciSearchCertificate | OptimizationFailure (never bare float); content-addressed cert_id; dual-run stable digest. D2 — propose_kappa_from_search / goldtether.propose_kappa_line_search; failure → baseline κ=1.0 (no state mutation). D4 — ALLOCATOR_VERSION golden_angle_v1 + layout descriptor. Fidelity §12 honest: V1/V3 green; V2 table-only; V4/V5 staged. --- core/physics/__init__.py | 18 +- core/physics/atlas_packing.py | 27 ++ core/physics/fibonacci_search.py | 296 +++++++++++++++--- core/physics/goldtether.py | 37 +++ .../ADR-0242-atlas-packing-and-fibonacci.md | 129 +++++--- ...ibonacci_applications_in_core_substrate.md | 60 ++++ .../research/third-door-blueprint-fidelity.md | 12 +- tests/test_adr_0242_fibonacci.py | 103 +++++- tests/test_third_door_cohesion.py | 36 ++- 9 files changed, 599 insertions(+), 119 deletions(-) create mode 100644 docs/analysis/fibonacci_applications_in_core_substrate.md diff --git a/core/physics/__init__.py b/core/physics/__init__.py index 215b11bd..a38601c5 100644 --- a/core/physics/__init__.py +++ b/core/physics/__init__.py @@ -33,6 +33,7 @@ from core.physics.goldtether import ( GoldTetherMonitor, OperatingMode, coherence_residual, + propose_kappa_line_search, ) from core.physics.dynamic_manifold import ( AxisClassification, @@ -91,7 +92,15 @@ from core.physics.wave_energy_boundary import ( recency_band_index, wave_unitary_residual, ) -from core.physics.fibonacci_search import fibonacci_number +from core.physics.fibonacci_search import ( + BASELINE_KAPPA, + BoundedUnimodalObjective, + FibonacciSearchCertificate, + OptimizationFailure, + fibonacci_number, + fibonacci_section_search, + propose_kappa_from_search, +) __all__ = [ "SalienceOperator", "SalienceMap", "FieldRegion", @@ -134,4 +143,11 @@ __all__ = [ "recency_band_index", "wave_unitary_residual", "fibonacci_number", + "BASELINE_KAPPA", + "BoundedUnimodalObjective", + "FibonacciSearchCertificate", + "OptimizationFailure", + "fibonacci_section_search", + "propose_kappa_from_search", + "propose_kappa_line_search", ] diff --git a/core/physics/atlas_packing.py b/core/physics/atlas_packing.py index 867f3f46..e2f590d9 100644 --- a/core/physics/atlas_packing.py +++ b/core/physics/atlas_packing.py @@ -27,6 +27,9 @@ from core.physics.wave_manifold import WaveManifold PHI = (1.0 + math.sqrt(5.0)) / 2.0 DEFAULT_MIN_D = 0.12 +# Reconstruction-over-storage: layout regenerates from identity + ordinal k. +ALLOCATOR_IDENTITY = "golden_angle" +ALLOCATOR_VERSION = "golden_angle_v1" class AtlasPackingError(ValueError): @@ -54,6 +57,9 @@ def golden_angle_pack( (x,y) = (r cos θ, r sin θ) → embed_point → null 32-vector Rejects with :class:`AtlasPackingError` if any pairwise separation < min_d. + + Layout is reconstructible from :data:`ALLOCATOR_VERSION` and ordinals + ``0..n-1`` (no opaque mutable coordinate table as truth). """ if n < 1: raise AtlasPackingError("n must be >= 1") @@ -83,6 +89,24 @@ def golden_angle_pack( return modes +def allocator_layout_descriptor( + n: int, + alpha: float, + *, + min_d: float = DEFAULT_MIN_D, +) -> dict[str, object]: + """Content-free reconstruction metadata for packing (no coordinate leak).""" + return { + "allocator_identity": ALLOCATOR_IDENTITY, + "allocator_version": ALLOCATOR_VERSION, + "n": int(n), + "alpha": float(alpha), + "min_d": float(min_d), + "metric": "cga_null_point_euclidean_d", + "note": "not_full_H2_geodesic", + } + + def register_packed_modes( modes: Sequence[np.ndarray], manifold: WaveManifold, @@ -101,8 +125,11 @@ def register_packed_modes( __all__ = [ "PHI", "DEFAULT_MIN_D", + "ALLOCATOR_IDENTITY", + "ALLOCATOR_VERSION", "AtlasPackingError", "null_point_separation", "golden_angle_pack", + "allocator_layout_descriptor", "register_packed_modes", ] diff --git a/core/physics/fibonacci_search.py b/core/physics/fibonacci_search.py index 37276900..2c0356f6 100644 --- a/core/physics/fibonacci_search.py +++ b/core/physics/fibonacci_search.py @@ -1,8 +1,11 @@ -"""core.physics.fibonacci_search — fixed-budget Fibonacci section search (ADR-0242). +"""core.physics.fibonacci_search — evidence-gated Fibonacci section search (ADR-0242 V1). Deterministic 1D unimodal minimization for construction / calibration / GoldTether κ-style scalar brackets. Not a serve-path operator (A-04 quarantine). +Public result is always a typed ``FibonacciSearchCertificate`` or +``OptimizationFailure`` — never a bare float (Drive evidence discipline). + Fail-closed on: * nonfinite objective values * invalid bounds / budget @@ -12,9 +15,11 @@ Fail-closed on: from __future__ import annotations +import hashlib +import json import math from dataclasses import dataclass, field -from typing import Callable +from typing import Callable, Union @dataclass(frozen=True, slots=True) @@ -32,8 +37,65 @@ class BoundedUnimodalObjective: raise ValueError("upper bound must be strictly greater than lower bound") if not math.isfinite(self.lower) or not math.isfinite(self.upper): raise ValueError("bounds must be finite") + if not str(self.objective_id).strip(): + raise ValueError("objective_id is required") + if not str(self.objective_version).strip(): + raise ValueError("objective_version is required") +@dataclass(frozen=True, slots=True) +class FibonacciSearchCertificate: + """Cryptographically content-addressed, replayable optimization result.""" + + minimizer: float + final_interval: tuple[float, float] + evaluations: int + ordered_points: tuple[float, ...] + ordered_values: tuple[float, ...] + objective_id: str + objective_version: str + cert_id: str = field(default="") + + def __post_init__(self) -> None: + if not self.cert_id: + object.__setattr__(self, "cert_id", _cert_digest(self)) + + def as_dict(self) -> dict[str, object]: + return { + "kind": "FibonacciSearchCertificate", + "cert_id": self.cert_id, + "minimizer": self.minimizer, + "final_interval": list(self.final_interval), + "evaluations": self.evaluations, + "ordered_points": list(self.ordered_points), + "ordered_values": list(self.ordered_values), + "objective_id": self.objective_id, + "objective_version": self.objective_version, + } + + +@dataclass(frozen=True, slots=True) +class OptimizationFailure: + """Typed failure — never silently accept a candidate minimizer.""" + + reason: str + final_interval: tuple[float, float] + evaluations: int + objective_id: str + objective_version: str + + def as_dict(self) -> dict[str, object]: + return { + "kind": "OptimizationFailure", + "reason": self.reason, + "final_interval": list(self.final_interval), + "evaluations": self.evaluations, + "objective_id": self.objective_id, + "objective_version": self.objective_version, + } + + +# Backward-compat view (cohesion sketches). Prefer Certificate | Failure. @dataclass(slots=True) class SearchTrace: best_observed_point: float @@ -41,6 +103,11 @@ class SearchTrace: certificate: dict = field(default_factory=dict) +SearchResult = Union[FibonacciSearchCertificate, OptimizationFailure] + +BASELINE_KAPPA: float = 1.0 + + def fibonacci_number(n: int) -> int: """F_0=0, F_1=1, … standard Fibonacci. n may be 0.""" if n < 0: @@ -52,13 +119,27 @@ def fibonacci_number(n: int) -> int: def _fibonacci(n: int) -> int: - """Internal alias — prefer :func:`fibonacci_number` at call sites.""" return fibonacci_number(n) -def _assert_sampled_unimodality(eval_values: dict[float, float]) -> None: - """Fail-closed if sorted samples are not unimodal about the observed min.""" +def _cert_digest(cert: FibonacciSearchCertificate) -> str: + payload = { + "minimizer": cert.minimizer, + "final_interval": list(cert.final_interval), + "evaluations": cert.evaluations, + "ordered_points": list(cert.ordered_points), + "ordered_values": list(cert.ordered_values), + "objective_id": cert.objective_id, + "objective_version": cert.objective_version, + } + raw = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8") + return hashlib.sha256(raw).hexdigest() + + +def _unimodality_ok(eval_values: dict[float, float]) -> bool: sorted_points = sorted(eval_values.keys()) + if len(sorted_points) < 2: + return True min_idx = 0 min_val = float("inf") for i, x in enumerate(sorted_points): @@ -66,60 +147,119 @@ def _assert_sampled_unimodality(eval_values: dict[float, float]) -> None: if v < min_val: min_val = v min_idx = i - - # Strictly non-increasing toward min (allow float ties). for i in range(min_idx): left = eval_values[sorted_points[i]] right = eval_values[sorted_points[i + 1]] if left < right - 1e-9: - raise ValueError( - "unimodality violation detected (multiple extrema): " - "values not decreasing before minimum." - ) - - # Strictly non-decreasing after min (allow float ties). + return False for i in range(min_idx, len(sorted_points) - 1): left = eval_values[sorted_points[i]] right = eval_values[sorted_points[i + 1]] if left > right + 1e-9: - raise ValueError( - "unimodality violation detected (multiple extrema): " - "values not increasing after minimum." - ) + return False + return True + + +def _failure( + objective: BoundedUnimodalObjective, + *, + reason: str, + a: float, + b: float, + evaluations: int, +) -> OptimizationFailure: + return OptimizationFailure( + reason=reason, + final_interval=(float(a), float(b)), + evaluations=int(evaluations), + objective_id=objective.objective_id, + objective_version=objective.objective_version, + ) def fibonacci_section_search( objective: BoundedUnimodalObjective, func: Callable[[float], float], -) -> SearchTrace: - """Fibonacci section search: exactly ``evaluation_budget`` function evals. +) -> SearchResult: + """Fibonacci section search with evidence-gated result. - Returns :class:`SearchTrace` with ``best_observed_point``, ``eval_sequence``, - and a small certificate dict (budget, ids, bounds). + Returns :class:`FibonacciSearchCertificate` on success or + :class:`OptimizationFailure` on any fail-closed condition. + Never returns a bare float. """ n = int(objective.evaluation_budget) - a = float(objective.lower) - b = float(objective.upper) + a0 = float(objective.lower) + b0 = float(objective.upper) + a, b = a0, b0 + + if n < 2: + return _failure( + objective, + reason="budget_too_low_for_unimodal_search", + a=a0, + b=b0, + evaluations=0, + ) f_n_plus_1 = _fibonacci(n + 1) f_n_minus_1 = _fibonacci(n - 1) f_n = _fibonacci(n) + if f_n_plus_1 == 0: + return _failure( + objective, + reason="degenerate_fibonacci_schedule", + a=a0, + b=b0, + evaluations=0, + ) c = a + (f_n_minus_1 / f_n_plus_1) * (b - a) d = a + (f_n / f_n_plus_1) * (b - a) - def _eval(x: float) -> float: + points: list[float] = [] + values: list[float] = [] + eval_values: dict[float, float] = {} + + def _eval(x: float) -> float | OptimizationFailure: if x < objective.lower - 1e-12 or x > objective.upper + 1e-12: - raise ValueError(f"bounds violation: evaluated {x} outside [{objective.lower}, {objective.upper}]") - y = float(func(x)) + return _failure( + objective, + reason=f"bounds_violation: evaluated {x} outside [{objective.lower}, {objective.upper}]", + a=a, + b=b, + evaluations=len(points), + ) + try: + y = float(func(x)) + except Exception as exc: # noqa: BLE001 — typed failure surface + return _failure( + objective, + reason=f"evaluation_error: {type(exc).__name__}: {exc}", + a=a, + b=b, + evaluations=len(points), + ) if not math.isfinite(y): - raise ValueError(f"Objective function returned nonfinite value {y} at {x}") + return _failure( + objective, + reason=f"nonfinite_objective_value_at_{x}", + a=a, + b=b, + evaluations=len(points), + ) return y - fc = _eval(c) - fd = _eval(d) - eval_sequence = [c, d] - eval_values: dict[float, float] = {c: fc, d: fd} + r_c = _eval(c) + if isinstance(r_c, OptimizationFailure): + return r_c + r_d = _eval(d) + if isinstance(r_d, OptimizationFailure): + return r_d + fc, fd = r_c, r_d + points.extend([c, d]) + values.extend([fc, fd]) + eval_values[c] = fc + eval_values[d] = fd best_x = c if fc < fd else d best_f = min(fc, fd) @@ -133,8 +273,12 @@ def fibonacci_section_search( f_n_minus_k_minus_1 = _fibonacci(n - k - 1) f_n_minus_k_plus_1 = _fibonacci(n - k + 1) c = a + (f_n_minus_k_minus_1 / f_n_minus_k_plus_1) * (b - a) - fc = _eval(c) - eval_sequence.append(c) + r_c = _eval(c) + if isinstance(r_c, OptimizationFailure): + return r_c + fc = r_c + points.append(c) + values.append(fc) eval_values[c] = fc if fc < best_f: best_f = fc @@ -146,36 +290,88 @@ def fibonacci_section_search( f_n_minus_k = _fibonacci(n - k) f_n_minus_k_plus_1 = _fibonacci(n - k + 1) d = a + (f_n_minus_k / f_n_minus_k_plus_1) * (b - a) - fd = _eval(d) - eval_sequence.append(d) + r_d = _eval(d) + if isinstance(r_d, OptimizationFailure): + return r_d + fd = r_d + points.append(d) + values.append(fd) eval_values[d] = fd if fd < best_f: best_f = fd best_x = d k += 1 - _assert_sampled_unimodality(eval_values) + if not _unimodality_ok(eval_values): + return _failure( + objective, + reason="unimodality_violation_multiple_extrema_detected", + a=a, + b=b, + evaluations=len(points), + ) - certificate = { - "budget": objective.evaluation_budget, - "objective_id": objective.objective_id, - "objective_version": objective.objective_version, - "lower_bound": objective.lower, - "upper_bound": objective.upper, - "best_value": best_f, - "n_evals": len(eval_sequence), - } + # Drive cert uses midpoint of final bracket; track also retains best sample. + minimizer = 0.5 * (a + b) + # Prefer best sample if it lies inside final bracket (more accurate under noise). + if a - 1e-15 <= best_x <= b + 1e-15: + minimizer = float(best_x) + + return FibonacciSearchCertificate( + minimizer=float(minimizer), + final_interval=(float(a), float(b)), + evaluations=len(points), + ordered_points=tuple(float(p) for p in points), + ordered_values=tuple(float(v) for v in values), + objective_id=objective.objective_id, + objective_version=objective.objective_version, + ) + + +def propose_kappa_from_search( + result: SearchResult, + *, + baseline: float = BASELINE_KAPPA, +) -> tuple[float, SearchResult]: + """Evidence-gated κ: cert → minimizer; failure → baseline (default 1.0). + + Never promotes COHERENT standing or mutates identity — caller telemetry only. + """ + if isinstance(result, FibonacciSearchCertificate): + return float(result.minimizer), result + return float(baseline), result + + +def search_trace_from_result(result: SearchResult) -> SearchTrace: + """Legacy adapter for sketches that expect SearchTrace (raises on failure).""" + if isinstance(result, OptimizationFailure): + raise ValueError(result.reason) return SearchTrace( - best_observed_point=float(best_x), - eval_sequence=list(eval_sequence), - certificate=certificate, + best_observed_point=result.minimizer, + eval_sequence=list(result.ordered_points), + certificate={ + "budget": result.evaluations, + "objective_id": result.objective_id, + "objective_version": result.objective_version, + "lower_bound": result.final_interval[0], + "upper_bound": result.final_interval[1], + "best_value": None, + "n_evals": result.evaluations, + "cert_id": result.cert_id, + "kind": "FibonacciSearchCertificate", + }, ) __all__ = [ - "fibonacci_number", - + "BASELINE_KAPPA", "BoundedUnimodalObjective", + "FibonacciSearchCertificate", + "OptimizationFailure", + "SearchResult", "SearchTrace", + "fibonacci_number", "fibonacci_section_search", + "propose_kappa_from_search", + "search_trace_from_result", ] diff --git a/core/physics/goldtether.py b/core/physics/goldtether.py index 78ffe2ae..c6cbc708 100644 --- a/core/physics/goldtether.py +++ b/core/physics/goldtether.py @@ -521,3 +521,40 @@ class GoldTetherMonitor: for h in self.history[-16:] ], } + + +# --------------------------------------------------------------------------- +# ADR-0242 V1 — evidence-gated κ line search (optional, off-serve) +# --------------------------------------------------------------------------- + + +def propose_kappa_line_search( + residual_fn, + *, + lower: float = 0.1, + upper: float = 2.0, + evaluation_budget: int = 16, + objective_id: str = "goldtether_kappa", + objective_version: str = "v1", +) -> tuple[float, object]: + """Optional κ search via Fibonacci section (ADR-0242 Phase 1 seam). + + Returns ``(kappa, cert_or_failure)``. On failure, kappa is baseline 1.0. + Does **not** mutate GoldTetherMonitor state, COHERENT standing, or serve + autonomy — caller may record the result as telemetry only. + """ + from core.physics.fibonacci_search import ( + BoundedUnimodalObjective, + fibonacci_section_search, + propose_kappa_from_search, + ) + + objective = BoundedUnimodalObjective( + lower=float(lower), + upper=float(upper), + evaluation_budget=int(evaluation_budget), + objective_id=str(objective_id), + objective_version=str(objective_version), + ) + result = fibonacci_section_search(objective, residual_fn) + return propose_kappa_from_search(result) diff --git a/docs/adr/ADR-0242-atlas-packing-and-fibonacci.md b/docs/adr/ADR-0242-atlas-packing-and-fibonacci.md index ee866445..78f27254 100644 --- a/docs/adr/ADR-0242-atlas-packing-and-fibonacci.md +++ b/docs/adr/ADR-0242-atlas-packing-and-fibonacci.md @@ -1,75 +1,124 @@ -# ADR-0242: Hyperbolic Atlas Golden-Angle Packing and Fibonacci Search +# ADR-0242: Deterministic Fibonacci Operators and Evidence-Gated Optimization -**Status**: Proposed — packing + Fibonacci search + multi-scale τ schedule green; **ready for Joshua acceptance review** (do not self-Accept). Checklist: `docs/audit/adr_0241_cohesion_acceptance_checklist.md`. -**Date**: 2026-07-14 -**Deciders**: Joshua Shay + multi-model R&D (Gemini implementation pass) -**Traceability**: PR #37, parent ADR-0241 / cohesion master plan -**Related**: ADR-0003, ADR-0238, ADR-0241, `docs/analysis/core_cohesion_master_plan.md`, `docs/briefs/ADR-0242-atlas-packing-and-fibonacci-brief.md` -**Canonical path**: `docs/adr/` +**Status**: Proposed — V1 cert discipline + V3 packing landed; V2/V4/V5 staged; **not** self-Accepted (Joshua review). +**Date**: 2026-07-13 (Drive authority); in-repo expansion 2026-07-15 +**Deciders**: Joshua Shay + multi-model R&D +**Traceability**: Drive ADR-0242 (`15_NECCPy-tEWGfYi_BNqawm8GytUTMkz1DsOqGVMXhI`), PR #37/#38, cohesion plan +**Related**: ADR-0003, ADR-0238, ADR-0239, ADR-0240, ADR-0241, `docs/analysis/fibonacci_applications_in_core_substrate.md`, `docs/analysis/core_cohesion_master_plan.md` +**Canonical path**: `docs/adr/` +**Filename note**: file keeps historical path `ADR-0242-atlas-packing-and-fibonacci.md`; **title/scope match Drive**. --- ## Context -ADR-0241 established `WaveManifold` and `HolographicVaultStore`. Entity cohesion still needed: +ADR-0241 establishes continuous wave-field \(\psi\). Optimization, scheduling, and multi-scale allocation still need deterministic, reconstructible operators that **earn their place** under CORE’s evidence discipline — not sacred-geometry dogma. -1. **Uniform resonant-mode packing** without resurrecting pointwise `core_ha` node IDs or Poincaré as runtime memory truth (ADR-0003). -2. **Fixed-budget unimodal scalar search** for construction/calibration (e.g. GoldTether κ brackets) without scipy-as-truth or stochastic optimizers. +Drive ADR-0242 defines **five Fibonacci vectors**. An earlier in-repo draft understated that thesis as packing + section search only. This document restores the full scope and records honest landing status. -## Decision +--- -### 1. Golden-Angle packing (`core/physics/atlas_packing.py`) +## Sovereignty invariant (absolute) -For \(k = 0 \ldots n-1\): +**Fibonacci operators may optimize search parameters, set observation scale, or schedule background checks; they must NEVER dictate proposition truth, safety policy, identity, or authorize autonomous COHERENT promotion.** + +Active reasoning, vault standing, and serve remain governed by versor closure, CRDT exactness, and human-gated review. + +--- + +## Decision — five vectors + +### Vector 1 — Bounded Fibonacci-section search (production Phase 1) 🟢 + +Module: `core/physics/fibonacci_search.py` + +- `BoundedUnimodalObjective` +- `fibonacci_section_search(objective, func) -> FibonacciSearchCertificate | OptimizationFailure` +- **Never** returns a bare float +- Certificate is content-addressed (`cert_id` = SHA-256 of ordered trace + ids) +- Fail-closed: nonfinite, bounds, unimodality multi-extrema → `OptimizationFailure` +- κ seam: `propose_kappa_from_search` / `goldtether.propose_kappa_line_search` + - success → proposed κ = minimizer (telemetry; no auto state mutation) + - failure → **baseline κ = 1.0** + +### Vector 2 — Multi-scale temporal basis (research) 🟡 + +Drive: \[ -\theta_k = 2\pi k / \varphi,\qquad r_k = \tanh(\alpha\sqrt{k}) +E_n(t) = E_n(t_0)\,\exp\bigl(-(t-t_0)/(F_n\tau_0)\bigr) \] -Lift \((r\cos\theta, r\sin\theta, 0)\) via `algebra.cga.embed_point` to Cl(4,1) **null points**. +Landed progressive form: `fibonacci_tau_schedule` / `recency_band_index` in `wave_energy_boundary.py` (constants table). +**Not** yet production default inside `FieldEnergyOperator`. Promotion requires comparative benchmark vs dyadic \(2^n\tau_0\) (Drive comparative hypothesis). -**Separation pin:** CGA null-point distance from `cga_inner` contract \(\langle P,Q\rangle = -d^2/2\): +### Vector 3 — Golden-Angle mode allocator 🟢 -\[ -d = \sqrt{-2\langle P,Q\rangle} -\] +Module: `core/physics/atlas_packing.py` -Fail-closed (`AtlasPackingError`) if any pair has \(d < d_{\min}\) (default \(0.12\)). +- Golden-Angle polar lift via `embed_point` → null 32-vectors +- Fail-closed if pairwise CGA null-point \(d < d_{\min}\) (default 0.12) +- Honest metric: Euclidean null-cone readout, **not** full \(H^2\) geodesic +- Reconstruction-over-storage: `ALLOCATOR_VERSION = golden_angle_v1` + `allocator_layout_descriptor` +- Not holographic seals (null points ≠ closed unit versors) -**Honest scope:** this \(d\) is the Euclidean distance of the embedded \(\mathbb{R}^3\) points (null-cone isometric readout), not a full hyperbolic \(H^2\) geodesic solver. Sufficient for the cohesion packing density gate. +### Vector 4 — Fibonacci-word observability choreography 🔴 staged -**No attribute leaks:** returned modes are pure `float64` 32-vectors. No stored θ/r. +Drive: \(W_0=B, W_1=A, W_{n+1}=W_n W_{n-1}\) for telemetry / sealed-holdout sampling. +**Outside cognitive truth path.** Not yet implemented (plan D5). -**Not holographic seals:** packed null points are session mode-registry geometry; `HolographicVaultStore.seal_mode` still requires closed unit versors. +### Vector 5 — Topological anyon / braid holonomy 🔴 research quarantine -### 2. Fibonacci section search (`core/physics/fibonacci_search.py`) +Drive: isolated `algebra/topological_reasoning/` study; blocked from production. +Not implemented (plan D6). Must not enter serve/FFI until proofs exist. -- `BoundedUnimodalObjective(lower, upper, evaluation_budget, objective_id, objective_version)` -- `fibonacci_section_search(objective, func) -> SearchTrace` -- Exactly `evaluation_budget` evaluations -- Fail-closed on nonfinite, bounds violation, sampled unimodality violation -- Certificate carries budget, ids, bounds, best value, n_evals +--- -### 3. Serve quarantine (A-04) +## Phase order (Drive §5) -Neither module may be imported from `chat/runtime.py`. Pinned in `tests/test_third_door_cohesion.py`. +| Phase | Vector | Status | +|-------|--------|--------| +| 1 | V1 search + κ cert gate | 🟢 | +| 2 | V2 multi-scale energy study | 🟡 table only | +| 3 | V3 packing | 🟢 | +| 4 | V4 word scheduler | 🔴 | +| 5 | V5 anyons | 🔴 quarantine | + +--- + +## Serve quarantine (A-04) + +`fibonacci_search`, `atlas_packing`, `wave_energy_boundary` must not be imported from `chat/runtime.py` (AST pin in `tests/test_third_door_cohesion.py`). + +--- ## Consequences ### Benefits -- Deterministic atlas packing for standing-wave mode placement -- Algebra-native fixed-budget scalar search for κ / residual brackets -- Continues `core_ha` deprecation (no node IDs / Poincaré runtime store) +- Evidence-gated optimizers (typed cert/failure) +- Deterministic packing without `core_ha` node IDs +- Clear multi-vector roadmap without dogma ### Trade-offs -- Separation is CGA null-point Euclidean distance, not full hyperbolic geodesic -- Unimodality check is sample-based (only evaluated points), not a global oracle -- Packing modes are null points, not unit versors — durable vault seal path remains separate +- Sample-based unimodality (not global oracle) +- Packing separation not full hyperbolic geodesic +- V2 production promotion deferred pending benchmarks + +--- ## Validation -- `tests/test_adr_0242_atlas_packing.py` -- `tests/test_adr_0242_fibonacci.py` -- `tests/test_third_door_cohesion.py` (serve quarantine + κ integration) +- `tests/test_adr_0242_fibonacci.py` — cert/failure + dual-run digest + κ fallback +- `tests/test_adr_0242_atlas_packing.py` +- `tests/test_third_door_cohesion.py` — serve quarantine + κ integration +- `tests/test_adr_0241_wave_energy_boundary.py` — \(\tau_n\) table + +--- + +## Acceptance path + +Joshua review may Accept after Phase 1 (V1+V3) is verified in merge. +V2/V4/V5 need not block Phase 1 Accept if status rows remain honest RESEARCH/staged. +Agents **must not** self-Accept. diff --git a/docs/analysis/fibonacci_applications_in_core_substrate.md b/docs/analysis/fibonacci_applications_in_core_substrate.md new file mode 100644 index 00000000..fcc84eb4 --- /dev/null +++ b/docs/analysis/fibonacci_applications_in_core_substrate.md @@ -0,0 +1,60 @@ +# R&D Memorandum: Non-Forced Applications of Fibonacci and Golden Ratio Dynamics in the CORE Substrate + +**Status**: Proposed (Exploratory R&D / Theoretical Blueprint) +**Date**: 2026-07-13 +**Authors**: Multi-model R&D + Joshua Shay +**Traceability**: Drive memo `1wcuxwfxk6AW6du4SgKe4AuRxMaE5tipxG2VbrXeWM6c` +**Related**: ADR-0003, ADR-0006, ADR-0238, ADR-0239, ADR-0241, ADR-0242, `core/physics/energy.py`, `core/physics/fibonacci_search.py`, `core/physics/atlas_packing.py` +**Canonical path**: `docs/analysis/fibonacci_applications_in_core_substrate.md` + +--- + +## 1. Introduction + +In natural systems, the Fibonacci sequence \(F_n = F_{n-1}+F_{n-2}\) and Golden Ratio \(\varphi = (1+\sqrt{5})/2\) appear in optimal packing and multi-scale structure. CORE does **not** force sacred geometry. Operators land only where they provide deterministic, reconstructible, evidence-gated advantage (ADR-0242 sovereignty invariant). + +## 2. Four integration vectors (memo) ↔ ADR-0242 five vectors + +| Memo § | Topic | ADR-0242 vector | Landing status | +|--------|-------|-----------------|----------------| +| 2.1 | Hyperbolic golden-spiral mode packing | V3 | 🟢 `atlas_packing.py` | +| 2.2 | Fibonacci anyons / braid holonomy | V5 | 🔴 research only | +| 2.3 | Fibonacci-section search | V1 | 🟢 cert-gated `fibonacci_search.py` | +| §4 | Multi-scale \(\tau_n = F_n\tau_0\) energy | V2 | 🟡 table in `wave_energy_boundary`; not production default | +| (Drive add) | Fibonacci-word observability schedule | V4 | 🔴 staged | + +### 2.1 Optimal spectral mode packing (V3) + +Place mode centroids via Golden Angle / phyllotaxis and lift to Cl(4,1) null points. Separation pin \(d_{\min}\) uses CGA null-point distance (honest Euclidean readout). See ADR-0242 V3. + +### 2.2 Fibonacci anyons (V5 — research) + +Fusion \(\tau\otimes\tau = \mathbf{1}\oplus\tau\) as a topological composition research program. **Blocked from production** until algebraic + numerical proofs exist. Do not wire into serve, vault COHERENT, or FFI. + +### 2.3 Fibonacci-section search (V1) + +Fixed-budget unimodal search for κ / residual brackets. Public API returns `FibonacciSearchCertificate | OptimizationFailure` only. κ failure → baseline 1.0. + +### 2.4 Multi-scale temporal windows (V2) + +\[ +\tau_n = F_n\cdot\tau_0 +\quad\Rightarrow\quad +\{1,1,2,3,5,8,13,\ldots\}\tau_0 +\] + +Progressive landing: constants schedule + band index. Production `FieldEnergyOperator` multi-band \(E_n(t)\) requires comparative evidence vs dyadic bases (ADR-0242 Phase 2). + +## 3. Engineering guidelines + +- **No force-fitting** — elegance is not acceptance. +- **Evidence gate** — certificates / failures, not silent floats. +- **Off-serve** — fibonacci / packing / energy-boundary modules quarantined from `chat/runtime.py`. +- **Reconstruction-over-storage** for packing layout identity. + +## 4. Cross-links + +- ADR-0242 (authoritative five-vector decision record) +- ADR-0241 wave-field substrate +- Cohesion master plan entity traces +- Fidelity ledger §12 diff --git a/docs/research/third-door-blueprint-fidelity.md b/docs/research/third-door-blueprint-fidelity.md index 6c8d9b35..a3d44174 100644 --- a/docs/research/third-door-blueprint-fidelity.md +++ b/docs/research/third-door-blueprint-fidelity.md @@ -299,8 +299,12 @@ PY | Serve path not wired to wave / Fibonacci (containment) | 🟢 (AST-pinned in cohesion suite; includes `wave_seam`) | | Entity I-01…I-05 cohesion suite | 🟢 progressive pins in `test_third_door_cohesion.py` (I-02 float32-honest) | | Vault public `get_versor` ABI | 🟢 | -| Golden-Angle atlas packing \(d_{\min}=0.12\) | 🟢 ADR-0242 (`atlas_packing`; CGA null-point \(d\)) | -| Fibonacci κ search | 🟢 ADR-0242 (`fibonacci_search`) | +| Golden-Angle atlas packing \(d_{\min}=0.12\) (V3) | 🟢 ADR-0242 (`atlas_packing`; CGA null-point \(d\); `golden_angle_v1`) | +| Fibonacci section search cert/failure (V1) | 🟢 ADR-0242 (`FibonacciSearchCertificate` \| `OptimizationFailure`; dual-run digest) | +| κ cert gate fail → baseline 1.0 (V1b) | 🟢 `propose_kappa_from_search` / `goldtether.propose_kappa_line_search` | +| Multi-scale \(\tau_n=F_n\tau_0\) (V2) | 🟡 table only; production multi-band \(E_n(t)\) not default | +| Fibonacci-word scheduler (V4) | 🔴 staged | +| Fibonacci anyons (V5) | 🔴 research quarantine | | Contemplation Trace A SPECULATIVE holographic seal (P9) | 🟢 `core/contemplation/wave_seam.py` (hypothesis vs COHERENT evidence) | | Energy boundary + multi-scale τ (P10 Trace B) | 🟢 `wave_energy_boundary` (wave residual → energy/trajectory; τ_n=F_n·τ_0; E0–E1 crystallization) | @@ -339,7 +343,7 @@ PY | Durable holographic vault spectrum — 🟢 HolographicVaultStore | ADR-0241 | | Contemplation Trace A SPECULATIVE seal (P9) — 🟢 wave_seam | ADR-0241 P9 | | Energy boundary + multi-scale τ (P10) — 🟢 wave_energy_boundary | ADR-0241 P10 | -| Atlas packing + Fibonacci κ (ADR-0242) — 🟢 packing + search | ADR-0242 | -| Governance close (P12) — 🟢 contracts + checklist + ready-for-accept | ADR-0241 P12 | +| Atlas packing + Fibonacci V1 cert (ADR-0242) — 🟢 V1/V3; V2–V5 staged | ADR-0242 Drive five-vector | +| Governance close (P12) — 🟢 contracts + checklist | ADR-0241 P12 | Closing a gap = flip its `xfail` in `tests/test_third_door_blueprint_fidelity.py` (or the ADR-0241 / cohesion suite) to a passing behavioral test and delete the matching characterization lock. That is the definition of "done right" here. diff --git a/tests/test_adr_0242_fibonacci.py b/tests/test_adr_0242_fibonacci.py index aad2182c..54f379e5 100644 --- a/tests/test_adr_0242_fibonacci.py +++ b/tests/test_adr_0242_fibonacci.py @@ -1,16 +1,18 @@ -"""ADR-0242 — Fibonacci section search behavioral pins.""" +"""ADR-0242 V1 — evidence-gated Fibonacci section search.""" from __future__ import annotations -import pytest - from core.physics.fibonacci_search import ( + BASELINE_KAPPA, BoundedUnimodalObjective, + FibonacciSearchCertificate, + OptimizationFailure, fibonacci_section_search, + propose_kappa_from_search, ) -def test_fibonacci_search_hits_known_unimodal_min_within_1e_3(): +def test_fibonacci_search_returns_certificate_near_known_min(): objective = BoundedUnimodalObjective( lower=0.1, upper=2.0, @@ -22,9 +24,34 @@ def test_fibonacci_search_hits_known_unimodal_min_within_1e_3(): def func(x: float) -> float: return (x - 0.789) ** 2 - trace = fibonacci_section_search(objective, func) - assert abs(trace.best_observed_point - 0.789) < 1e-3 - assert len(trace.eval_sequence) == 20 + result = fibonacci_section_search(objective, func) + assert isinstance(result, FibonacciSearchCertificate) + assert abs(result.minimizer - 0.789) < 1e-3 + assert result.evaluations == 20 + assert len(result.ordered_points) == 20 + assert len(result.ordered_values) == 20 + assert result.cert_id + assert len(result.cert_id) == 64 + + +def test_certificate_digest_stable_dual_run(): + objective = BoundedUnimodalObjective( + lower=-5.0, + upper=5.0, + evaluation_budget=15, + objective_id="stable", + objective_version="v1", + ) + + def func(x: float) -> float: + return x**2 + + a = fibonacci_section_search(objective, func) + b = fibonacci_section_search(objective, func) + assert isinstance(a, FibonacciSearchCertificate) + assert isinstance(b, FibonacciSearchCertificate) + assert a.cert_id == b.cert_id + assert a.as_dict() == b.as_dict() def test_fibonacci_search_eval_count_equals_budget(): @@ -39,12 +66,12 @@ def test_fibonacci_search_eval_count_equals_budget(): def func(x: float) -> float: return x**2 - trace = fibonacci_section_search(objective, func) - assert len(trace.eval_sequence) == 15 - assert trace.certificate["n_evals"] == 15 + result = fibonacci_section_search(objective, func) + assert isinstance(result, FibonacciSearchCertificate) + assert result.evaluations == 15 -def test_fibonacci_search_rejects_nan_objective(): +def test_fibonacci_search_nonfinite_returns_failure(): objective = BoundedUnimodalObjective( lower=-1.0, upper=1.0, @@ -56,11 +83,12 @@ def test_fibonacci_search_rejects_nan_objective(): def func(x: float) -> float: return float("nan") - with pytest.raises(ValueError, match="nonfinite"): - fibonacci_section_search(objective, func) + result = fibonacci_section_search(objective, func) + assert isinstance(result, OptimizationFailure) + assert "nonfinite" in result.reason -def test_fibonacci_search_unimodality_violation_fail_closed(): +def test_fibonacci_search_unimodality_returns_failure(): objective = BoundedUnimodalObjective( lower=-2.0, upper=2.0, @@ -70,8 +98,49 @@ def test_fibonacci_search_unimodality_violation_fail_closed(): ) def func(x: float) -> float: - # Multiple extrema: x^4 - x^2 return x**4 - x**2 - with pytest.raises(ValueError, match="unimodality"): - fibonacci_section_search(objective, func) + result = fibonacci_section_search(objective, func) + assert isinstance(result, OptimizationFailure) + assert "unimodality" in result.reason + + +def test_never_returns_bare_float(): + objective = BoundedUnimodalObjective( + lower=0.0, + upper=1.0, + evaluation_budget=8, + objective_id="type", + objective_version="v1", + ) + result = fibonacci_section_search(objective, lambda x: (x - 0.3) ** 2) + assert not isinstance(result, float) + assert isinstance(result, (FibonacciSearchCertificate, OptimizationFailure)) + + +def test_propose_kappa_cert_uses_minimizer(): + objective = BoundedUnimodalObjective( + lower=0.1, + upper=2.0, + evaluation_budget=16, + objective_id="kappa", + objective_version="v1", + ) + result = fibonacci_section_search(objective, lambda x: (x - 0.5) ** 2) + kappa, outcome = propose_kappa_from_search(result) + assert isinstance(outcome, FibonacciSearchCertificate) + assert abs(kappa - 0.5) < 1e-2 + + +def test_propose_kappa_failure_falls_back_to_baseline(): + objective = BoundedUnimodalObjective( + lower=-2.0, + upper=2.0, + evaluation_budget=10, + objective_id="kappa_fail", + objective_version="v1", + ) + result = fibonacci_section_search(objective, lambda x: x**4 - x**2) + kappa, outcome = propose_kappa_from_search(result) + assert isinstance(outcome, OptimizationFailure) + assert kappa == BASELINE_KAPPA diff --git a/tests/test_third_door_cohesion.py b/tests/test_third_door_cohesion.py index 2b109bec..dcba364e 100644 --- a/tests/test_third_door_cohesion.py +++ b/tests/test_third_door_cohesion.py @@ -275,12 +275,19 @@ def test_resonant_reconstruct_empty_refused(): M.resonant_reconstruct(_closed(0.1)) -# --- ADR-0242 placeholder (Fibonacci not yet landed) -------------------------- +# --- ADR-0242 V1 evidence-gated Fibonacci + κ fallback ------------------------ def test_fibonacci_search_goldtether_integration(): - """Asserts Fibonacci search can optimize kappa and return a valid certificate.""" - from core.physics.fibonacci_search import BoundedUnimodalObjective, fibonacci_section_search + """Fibonacci search optimizes κ; cert-gated propose never silent-fails.""" + from core.physics.fibonacci_search import ( + BASELINE_KAPPA, + BoundedUnimodalObjective, + FibonacciSearchCertificate, + OptimizationFailure, + fibonacci_section_search, + propose_kappa_from_search, + ) objective = BoundedUnimodalObjective( lower=0.1, @@ -293,7 +300,22 @@ def test_fibonacci_search_goldtether_integration(): def synthetic_objective(kappa: float) -> float: return (kappa - 0.789) ** 2 # unimodal minimum at 0.789 - trace = fibonacci_section_search(objective, synthetic_objective) - assert abs(trace.best_observed_point - 0.789) < 1e-3 - assert len(trace.eval_sequence) == 20 - assert trace.certificate.get("budget") == 20 + result = fibonacci_section_search(objective, synthetic_objective) + assert isinstance(result, FibonacciSearchCertificate) + kappa, outcome = propose_kappa_from_search(result) + assert isinstance(outcome, FibonacciSearchCertificate) + assert abs(kappa - 0.789) < 1e-3 + assert outcome.evaluations == 20 + + # Failure path: multi-extrema → baseline κ=1.0 (Drive Phase 1). + fail_obj = BoundedUnimodalObjective( + lower=-2.0, + upper=2.0, + evaluation_budget=10, + objective_id="kappa_fail", + objective_version="v1.0", + ) + fail = fibonacci_section_search(fail_obj, lambda x: x**4 - x**2) + k_fail, out_fail = propose_kappa_from_search(fail) + assert isinstance(out_fail, OptimizationFailure) + assert k_fail == BASELINE_KAPPA From db6430ed4eedb9aa694d830ccadfec6a5c10ebd8 Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 14 Jul 2026 20:01:18 -0700 Subject: [PATCH 5/8] =?UTF-8?q?feat(adr-0242):=20macro-phase=20V2=E2=80=93?= =?UTF-8?q?V5=20+=20sensorium=20feed=20(Drive=20gap=20close)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parallel implementation of remaining Drive ADR-0242 vectors on PR #38: D3 V2 — multi_scale_energy: E_n(t)=E0·exp(-age/(F_n·τ0)), dyadic baseline, comparative helpers; not production FieldEnergyOperator default. D5 V4 — fibonacci_word_schedule: W0=B,W1=A,W_{n+1}=W_n W_{n-1}; telemetry only. D6 V5 — algebra/topological_reasoning quarantine + production AST pin. D7 — sensorium_wave_feed: packet→ψ, superpose, ρ via WaveManifold only. Serve quarantine extended for all new modules. Fidelity + ADR-0242 + runtime_contracts honesty pass. Suite: 118 related tests green. Single macro-phase commit per plan policy (no micro-PRs). --- algebra/topological_reasoning/README.md | 82 +++++++ algebra/topological_reasoning/__init__.py | 21 ++ core/physics/__init__.py | 12 + core/physics/fibonacci_word_schedule.py | 106 +++++++++ core/physics/multi_scale_energy.py | 178 ++++++++++++++ core/physics/sensorium_wave_feed.py | 141 +++++++++++ .../ADR-0242-atlas-packing-and-fibonacci.md | 28 ++- .../research/third-door-blueprint-fidelity.md | 7 +- docs/specs/runtime_contracts.md | 8 +- tests/test_adr_0241_sensorium_wave_feed.py | 222 ++++++++++++++++++ tests/test_adr_0242_fibonacci_word.py | 136 +++++++++++ tests/test_adr_0242_multi_scale_energy.py | 167 +++++++++++++ tests/test_adr_0242_topological_quarantine.py | 124 ++++++++++ tests/test_third_door_cohesion.py | 6 + 14 files changed, 1221 insertions(+), 17 deletions(-) create mode 100644 algebra/topological_reasoning/README.md create mode 100644 algebra/topological_reasoning/__init__.py create mode 100644 core/physics/fibonacci_word_schedule.py create mode 100644 core/physics/multi_scale_energy.py create mode 100644 core/physics/sensorium_wave_feed.py create mode 100644 tests/test_adr_0241_sensorium_wave_feed.py create mode 100644 tests/test_adr_0242_fibonacci_word.py create mode 100644 tests/test_adr_0242_multi_scale_energy.py create mode 100644 tests/test_adr_0242_topological_quarantine.py diff --git a/algebra/topological_reasoning/README.md b/algebra/topological_reasoning/README.md new file mode 100644 index 00000000..c9507e2c --- /dev/null +++ b/algebra/topological_reasoning/README.md @@ -0,0 +1,82 @@ +# Topological Reasoning — ADR-0242 Vector 5 (D6) Research Quarantine + +**Status**: 🔴 RESEARCH ONLY — blocked from production +**Authority**: ADR-0242 Vector 5 (topological anyon / braid holonomy) +**Related**: `docs/adr/ADR-0242-atlas-packing-and-fibonacci.md`, +`docs/analysis/fibonacci_applications_in_core_substrate.md` §2.2 + +--- + +## Purpose + +Isolated study surface for Fibonacci anyon fusion and braid holonomy as a +**topological composition research program**. The canonical fusion rule under +study is: + +\[ +\tau \otimes \tau = \mathbf{1} \oplus \tau +\] + +This package exists so research stubs, notes, and future proof-carrying +algebra can land **without** contaminating the live cognitive path: + +```text +listen → comprehend → recall → think → articulate → learn → replay +``` + +## Hard quarantine (do not violate) + +Until algebraic **and** numerical proofs exist, and until an explicit ADR +promotion gate is Accepted by human review, this package is **BLOCKED** from: + +| Surface | Rule | +|---------|------| +| Production runtime | No imports from serve / hot path | +| `chat/` | Not for import by chat or `chat/runtime.py` | +| Serve / FFI | Must not enter serve or FFI bindings | +| `core/physics/` | Not a production physics operator | +| `generate/` | Not for articulation / planner / cognitive turns | +| `vault/` | Not for vault standing, seal, or COHERENT promotion | +| `teaching/` | Not for reviewed teaching or pack mutation | +| GoldTether / κ paths | Not for production residual / κ optimization | +| `algebra` public surface | Not re-exported from `algebra/__init__.py` | + +Architectural pin: `tests/test_adr_0242_topological_quarantine.py` scans +production packages for any import of `topological_reasoning` and must find +none. + +## Sovereignty (ADR-0242) + +Fibonacci / topological operators may **never** dictate proposition truth, +safety policy, identity, or authorize autonomous COHERENT promotion. Active +reasoning remains governed by versor closure, exact CRDT recall, and +human-gated review. + +## What is allowed here + +- Research constants and docstring contracts (e.g. fusion rule labels) +- Future proof sketches, numerical experiments under tests/evals only when + explicitly gated +- Documentation of open questions and proof obligations + +## What is not allowed here + +- Production logic wired into cognition, serve, or vault truth +- Stochastic / approximate substitutes for exact CGA recall +- Hidden normalization or drift repair outside owned algebra boundaries +- Silent promotion of research results into COHERENT standing + +## API note + +The package may expose minimal docstring / constant stubs (e.g. `FUSION_RULE`). +No production operators, no side effects, no I/O. + +## Promotion path + +1. Algebraic + numerical proofs land and are reviewable. +2. ADR update records evidence and remaining risks. +3. Human Accept of a production promotion gate. +4. Only then may a **separate**, reviewed integration surface be designed — + still subject to serve quarantine and sovereignty invariant. + +Until then: this directory is a quarantine box, not a feature. diff --git a/algebra/topological_reasoning/__init__.py b/algebra/topological_reasoning/__init__.py new file mode 100644 index 00000000..7b5e36fe --- /dev/null +++ b/algebra/topological_reasoning/__init__.py @@ -0,0 +1,21 @@ +"""ADR-0242 V5 (D6) — topological anyon / braid holonomy research quarantine. + +Fibonacci anyon fusion research surface. BLOCKED from production, serve, FFI, +chat/runtime, vault COHERENT, teaching mutation, and GoldTether production +paths until algebraic and numerical proofs exist (see package README). + +This module intentionally re-exports nothing into ``algebra``'s public API +and must not be imported by production packages. +""" + +from __future__ import annotations + +# Canonical Fibonacci anyon fusion rule under study (research label only). +# τ ⊗ τ = 1 ⊕ τ — not a production operator; no evaluation semantics. +FUSION_RULE: str = "tau_otimes_tau_eq_1_oplus_tau" +"""Research label for the Fibonacci anyon fusion rule τ⊗τ = 1⊕τ. + +Docstring / constant only. Does not implement fusion, braiding, or holonomy. +""" + +__all__ = ["FUSION_RULE"] diff --git a/core/physics/__init__.py b/core/physics/__init__.py index a38601c5..70546831 100644 --- a/core/physics/__init__.py +++ b/core/physics/__init__.py @@ -101,6 +101,13 @@ from core.physics.fibonacci_search import ( fibonacci_section_search, propose_kappa_from_search, ) +from core.physics.multi_scale_energy import ( + comparative_residual_separation, + dyadic_tau_schedule, + multi_scale_energy_for_schedule, + multi_scale_energy_vector, + schedule_mid_span_fraction, +) __all__ = [ "SalienceOperator", "SalienceMap", "FieldRegion", @@ -150,4 +157,9 @@ __all__ = [ "fibonacci_section_search", "propose_kappa_from_search", "propose_kappa_line_search", + "comparative_residual_separation", + "dyadic_tau_schedule", + "multi_scale_energy_for_schedule", + "multi_scale_energy_vector", + "schedule_mid_span_fraction", ] diff --git a/core/physics/fibonacci_word_schedule.py b/core/physics/fibonacci_word_schedule.py new file mode 100644 index 00000000..a721d117 --- /dev/null +++ b/core/physics/fibonacci_word_schedule.py @@ -0,0 +1,106 @@ +"""core.physics.fibonacci_word_schedule — ADR-0242 V4 (D5) observability choreography. + +Fibonacci-word scheduler for telemetry / sealed-holdout sampling only. + +Drive recurrence +---------------- + W_0 = B + W_1 = A + W_{n+1} = W_n W_{n-1} (string concatenation) + +where: + * A — low-cost local measurement + * B — high-cost cross-band check + +Length formula +-------------- +With the standard Fibonacci sequence F_0 = 0, F_1 = 1, F_2 = 1, F_3 = 2, …: + + |W_n| = F_{n+1} for n >= 0 + (equivalently |W_0|=1, |W_1|=1, |W_2|=2, |W_3|=3, |W_4|=5, …) + +Sovereignty (ADR-0242 absolute invariant) +---------------------------------------- +This module is **outside the cognitive truth path**. It schedules +observability / telemetry actions only. It MUST NOT: + + * mutate vault standing or call VaultStore.store + * mutate field state or authorize COHERENT promotion + * dictate proposition truth, safety policy, or identity + * be imported from the serve hot path (A-04 quarantine) + +Pure and deterministic: no I/O, no randomness, no field/vault side effects. +""" + +from __future__ import annotations + +from enum import Enum +from typing import Iterator + + +class Action(str, Enum): + """Observability action labels (telemetry only; not cognitive truth).""" + + A = "A" # low-cost local measurement + B = "B" # high-cost cross-band check + + +def _require_nonneg_int(n: int, *, name: str = "n") -> int: + if not isinstance(n, int) or isinstance(n, bool): + raise TypeError(f"{name} must be an int, got {type(n).__name__}") + if n < 0: + raise ValueError(f"{name} must be non-negative, got {n}") + return n + + +def fibonacci_word(n: int) -> str: + """Return the Fibonacci word W_n as a string of 'A'/'B' characters. + + W_0 = \"B\", W_1 = \"A\", W_{k+1} = W_k + W_{k-1}. + Length |W_n| = F_{n+1} with F_0=0, F_1=1. + """ + n = _require_nonneg_int(n) + if n == 0: + return Action.B.value + if n == 1: + return Action.A.value + # Iterative doubling: O(n) concatenations, O(F_{n+1}) total chars. + prev = Action.B.value # W_0 + curr = Action.A.value # W_1 + for _ in range(2, n + 1): + prev, curr = curr, curr + prev + return curr + + +def schedule_actions(n: int) -> tuple[str, ...]: + """Return the action sequence for W_n as an immutable tuple of \"A\"/\"B\". + + Same recurrence and length as :func:`fibonacci_word`; form convenient for + iteration without splitting a string. + """ + word = fibonacci_word(n) + return tuple(word) + + +def iter_schedule_actions(n: int) -> Iterator[str]: + """Iterate actions of W_n without building an intermediate tuple.""" + yield from fibonacci_word(n) + + +def word_length(n: int) -> int: + """Return |W_n| = F_{n+1} (F_0=0, F_1=1) without building the word.""" + n = _require_nonneg_int(n) + # F_{n+1}: a,b walk F_0=0, F_1=1 for (n+1) steps → a = F_{n+1} + a, b = 0, 1 + for _ in range(n + 1): + a, b = b, a + b + return a + + +__all__ = [ + "Action", + "fibonacci_word", + "schedule_actions", + "iter_schedule_actions", + "word_length", +] diff --git a/core/physics/multi_scale_energy.py b/core/physics/multi_scale_energy.py new file mode 100644 index 00000000..2efd7c24 --- /dev/null +++ b/core/physics/multi_scale_energy.py @@ -0,0 +1,178 @@ +"""ADR-0242 V2 — multi-scale temporal energy basis (research prototype). + +Drive formula: + + E_n(t) = E_n(t_0) * exp(-(t - t_0) / (F_n * τ_0)) + +with Fibonacci scale factors F_n (n ≥ 1) and base time constant τ_0. + +This module is **research-only**: +- pure helpers for comparative study vs a dyadic (2^{n-1} τ_0) baseline +- **not** a production default inside ``FieldEnergyOperator.compute`` +- serve-quarantined (A-04): must not be imported from ``chat/runtime.py`` + +Reuses ``fibonacci_number`` / ``fibonacci_tau_schedule`` — no parallel Fibonacci. +""" + +from __future__ import annotations + +from math import exp, isfinite +from typing import Sequence + +from core.physics.fibonacci_search import fibonacci_number +from core.physics.wave_energy_boundary import fibonacci_tau_schedule + +_DEFAULT_TAU0 = 1.0 + + +def _validate_tau0(tau0: float) -> float: + t0 = float(tau0) + if not (t0 > 0.0) or not isfinite(t0): + raise ValueError("tau0 must be a positive finite scalar") + return t0 + + +def _validate_levels(levels: int) -> int: + n = int(levels) + if n < 1: + raise ValueError("levels must be >= 1") + return n + + +def _validate_age(age: float) -> float: + a = float(age) + if a < 0.0 or not isfinite(a): + raise ValueError("age must be a non-negative finite scalar") + return a + + +def _validate_e0(e0: float) -> float: + e = float(e0) + if not isfinite(e): + raise ValueError("e0 must be a finite scalar") + return e + + +def dyadic_tau_schedule( + tau0: float = _DEFAULT_TAU0, + *, + levels: int = 8, +) -> tuple[float, ...]: + """Dyadic comparison baseline τ_n = 2^{n-1} · τ_0 for n = 1..levels. + + ADR-0242 Phase 2 comparative hypothesis baseline (not a production default). + """ + t0 = _validate_tau0(tau0) + n = _validate_levels(levels) + return tuple(float(t0 * (2 ** (i - 1))) for i in range(1, n + 1)) + + +def multi_scale_energy_for_schedule( + e0: float, + age: float, + taus: Sequence[float], +) -> tuple[float, ...]: + """Apply E = e0 · exp(-age / τ) for each positive finite τ in ``taus``. + + ``age`` is (t − t_0). ``e0`` is the shared E_n(t_0) research default. + """ + e = _validate_e0(e0) + a = _validate_age(age) + if not taus: + raise ValueError("taus must be non-empty") + out: list[float] = [] + for raw in taus: + tau = float(raw) + if not (tau > 0.0) or not isfinite(tau): + raise ValueError("each tau must be a positive finite scalar") + out.append(float(e * exp(-a / tau))) + return tuple(out) + + +def multi_scale_energy_vector( + e0: float, + age: float, + *, + tau0: float = _DEFAULT_TAU0, + levels: int = 8, +) -> tuple[float, ...]: + """Fibonacci multi-scale energies E_n for n = 1..levels. + + Drive form with shared E_n(t_0) = e0: + + E_n = e0 * exp(-age / (F_n * tau0)) + + Equivalent to ``multi_scale_energy_for_schedule`` over + ``fibonacci_tau_schedule(tau0, levels=levels)``. + """ + t0 = _validate_tau0(tau0) + n = _validate_levels(levels) + # Explicit F_n path keeps the Drive formula visible at the callsite layer. + e = _validate_e0(e0) + a = _validate_age(age) + return tuple( + float(e * exp(-a / float(fibonacci_number(i) * t0))) + for i in range(1, n + 1) + ) + + +def comparative_residual_separation( + e0: float, + age: float, + *, + tau0: float = _DEFAULT_TAU0, + levels: int = 8, +) -> dict[str, object]: + """Deterministic Fibonacci vs dyadic multi-scale energy comparison. + + Pure research helper — no I/O. Returns both schedules, both energy + vectors, and per-index energy gaps (fib − dyadic). Promotion of + Fibonacci multi-band energy into production requires evidence from + this (or richer) comparative surface. + """ + t0 = _validate_tau0(tau0) + n = _validate_levels(levels) + fib_taus = fibonacci_tau_schedule(t0, levels=n) + dyad_taus = dyadic_tau_schedule(t0, levels=n) + fib_e = multi_scale_energy_for_schedule(e0, age, fib_taus) + dyad_e = multi_scale_energy_for_schedule(e0, age, dyad_taus) + gaps = tuple(float(f - d) for f, d in zip(fib_e, dyad_e, strict=True)) + return { + "tau0": t0, + "levels": n, + "age": float(age), + "e0": float(e0), + "fibonacci_taus": fib_taus, + "dyadic_taus": dyad_taus, + "fibonacci_energies": fib_e, + "dyadic_energies": dyad_e, + "energy_gap_fib_minus_dyadic": gaps, + } + + +def schedule_mid_span_fraction(taus: Sequence[float], *, index: int | None = None) -> float: + """Fraction of max(τ) occupied by τ at mid (or given) index. + + Used by comparative pins: Fibonacci mid-scale bands sit further along + the normalized span than pure dyadic 2^{n-1} (slower φ-growth). + """ + if not taus: + raise ValueError("taus must be non-empty") + vals = tuple(float(t) for t in taus) + for t in vals: + if not (t > 0.0) or not isfinite(t): + raise ValueError("each tau must be a positive finite scalar") + peak = max(vals) + i = len(vals) // 2 if index is None else int(index) + if i < 0 or i >= len(vals): + raise ValueError("index out of range for taus") + return float(vals[i] / peak) + + +__all__ = [ + "comparative_residual_separation", + "dyadic_tau_schedule", + "multi_scale_energy_for_schedule", + "multi_scale_energy_vector", + "schedule_mid_span_fraction", +] diff --git a/core/physics/sensorium_wave_feed.py b/core/physics/sensorium_wave_feed.py new file mode 100644 index 00000000..f4ae5f26 --- /dev/null +++ b/core/physics/sensorium_wave_feed.py @@ -0,0 +1,141 @@ +"""D7 sensorium → ψ feed (I-04 boundary). + +Thin construction-boundary adapter: modality surface packets become Cl(4,1) +wave fields for algebraic multimodal resonance. + +Real modality compilers remain in ``sensorium/*``. This module only standardizes +the feed into the wave substrate: + + * :class:`ModalityPacket` (or dict) — modality id + 32-float coefficients + * :func:`compile_packet_to_psi` — validate / lift to shape ``(32,)`` + * :func:`superpose_packets` — ``ψ_total = Σ ψ_i`` + * :func:`phase_correlate` — delegates **only** to + :meth:`WaveManifold.phase_correlation` (metric-exact ρ; no cosine / ANN) + +Honest test fixtures via :func:`fake_deterministic_packet` use closed rotors +from :func:`algebra.rotor.make_rotor_from_angle` when live compilers are not +under test. That is a fixture, not a claim that audio/vision compilers ran. + +Off-serve: must not be imported by ``chat/runtime.py`` (A-04 quarantine). +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Mapping, Sequence, Union + +import numpy as np + +from algebra.cl41 import N_COMPONENTS +from algebra.rotor import make_rotor_from_angle +from core.physics.wave_manifold import WaveManifold + +PacketLike = Union["ModalityPacket", Mapping[str, Any]] + + +@dataclass(frozen=True, slots=True) +class ModalityPacket: + """Construction-boundary packet: modality tag + 32 Cl(4,1) coefficients. + + After compile, the field has no modality concept (Logos recovery). + ``modality_id`` is provenance only. + """ + + modality_id: str + coefficients: np.ndarray # shape (N_COMPONENTS,) + + def __post_init__(self) -> None: + mid = str(self.modality_id).strip() + if not mid: + raise ValueError("modality_id must be non-empty") + arr = np.asarray(self.coefficients, dtype=np.float64).reshape(-1) + if arr.shape != (N_COMPONENTS,): + raise ValueError( + f"coefficients must have shape ({N_COMPONENTS},); got {arr.shape}" + ) + object.__setattr__(self, "modality_id", mid) + object.__setattr__(self, "coefficients", arr.copy()) + + +def _coerce_packet(packet: PacketLike) -> ModalityPacket: + if isinstance(packet, ModalityPacket): + return packet + if isinstance(packet, Mapping): + mid = packet.get("modality_id", packet.get("modality")) + coeffs = packet.get("coefficients") + if coeffs is None: + coeffs = packet.get("coeffs") + if coeffs is None: + coeffs = packet.get("psi") + if mid is None or coeffs is None: + raise ValueError( + "packet mapping requires modality_id (or modality) and " + "coefficients (or coeffs / psi)" + ) + return ModalityPacket(modality_id=str(mid), coefficients=np.asarray(coeffs)) + raise TypeError( + f"packet must be ModalityPacket or mapping; got {type(packet).__name__}" + ) + + +def compile_packet_to_psi(packet: PacketLike) -> np.ndarray: + """Lift a modality packet to a wave field ψ of shape ``(32,)``. + + Construction-boundary only: validates shape/dtype and returns a fresh + float64 copy. Does not repair non-unit packets (no hidden unitize). + """ + p = _coerce_packet(packet) + return p.coefficients.astype(np.float64, copy=True) + + +def superpose_packets(packets: Sequence[PacketLike]) -> np.ndarray: + """Linear superposition ``ψ_total = Σ_i compile_packet_to_psi(packet_i)``. + + Empty input refuses (no confabulated zero field as resonance truth). + """ + if not packets: + raise ValueError("superpose_packets: empty packet list") + total = np.zeros(N_COMPONENTS, dtype=np.float64) + for packet in packets: + total = total + compile_packet_to_psi(packet) + return total + + +def phase_correlate( + psi_a: np.ndarray, + psi_b: np.ndarray, + *, + manifold: WaveManifold | None = None, +) -> float: + """Algebraic multimodal resonance ρ(A,B) for I-04. + + Delegates solely to :meth:`WaveManifold.phase_correlation`. + Forbidden: cosine similarity, ANN, sklearn neighbors, embedding ranking. + """ + m = manifold if manifold is not None else WaveManifold() + return float(m.phase_correlation(psi_a, psi_b)) + + +def fake_deterministic_packet( + modality_id: str, + *, + angle: float = 0.3, + plane: int = 6, +) -> ModalityPacket: + """Honest deterministic fixture when real modality compilers are absent. + + Builds a closed unit rotor via :func:`make_rotor_from_angle`. This is a + test/construction fixture — not a live audio/vision compile path. + """ + coeffs = make_rotor_from_angle(float(angle), bivector_idx=int(plane)) + return ModalityPacket(modality_id=modality_id, coefficients=coeffs) + + +__all__ = [ + "ModalityPacket", + "PacketLike", + "compile_packet_to_psi", + "superpose_packets", + "phase_correlate", + "fake_deterministic_packet", +] diff --git a/docs/adr/ADR-0242-atlas-packing-and-fibonacci.md b/docs/adr/ADR-0242-atlas-packing-and-fibonacci.md index 78f27254..f547c182 100644 --- a/docs/adr/ADR-0242-atlas-packing-and-fibonacci.md +++ b/docs/adr/ADR-0242-atlas-packing-and-fibonacci.md @@ -1,6 +1,6 @@ # ADR-0242: Deterministic Fibonacci Operators and Evidence-Gated Optimization -**Status**: Proposed — V1 cert discipline + V3 packing landed; V2/V4/V5 staged; **not** self-Accepted (Joshua review). +**Status**: Proposed — V1 cert + V3 packing + V2 research helpers + V4 word schedule + V5 quarantine + sensorium feed landed; ready for Joshua review (do not self-accept). **Date**: 2026-07-13 (Drive authority); in-repo expansion 2026-07-15 **Deciders**: Joshua Shay + multi-model R&D **Traceability**: Drive ADR-0242 (`15_NECCPy-tEWGfYi_BNqawm8GytUTMkz1DsOqGVMXhI`), PR #37/#38, cohesion plan @@ -41,7 +41,7 @@ Module: `core/physics/fibonacci_search.py` - success → proposed κ = minimizer (telemetry; no auto state mutation) - failure → **baseline κ = 1.0** -### Vector 2 — Multi-scale temporal basis (research) 🟡 +### Vector 2 — Multi-scale temporal basis (research helpers) 🟢 research / 🟡 production Drive: @@ -49,8 +49,9 @@ Drive: E_n(t) = E_n(t_0)\,\exp\bigl(-(t-t_0)/(F_n\tau_0)\bigr) \] -Landed progressive form: `fibonacci_tau_schedule` / `recency_band_index` in `wave_energy_boundary.py` (constants table). -**Not** yet production default inside `FieldEnergyOperator`. Promotion requires comparative benchmark vs dyadic \(2^n\tau_0\) (Drive comparative hypothesis). +- `wave_energy_boundary.fibonacci_tau_schedule` / `recency_band_index` (constants table) +- `multi_scale_energy.py`: `multi_scale_energy_vector`, `dyadic_tau_schedule`, `comparative_residual_separation` +**Not** production default inside `FieldEnergyOperator`. Flip requires comparative benchmark + Joshua gate. ### Vector 3 — Golden-Angle mode allocator 🟢 @@ -62,15 +63,18 @@ Module: `core/physics/atlas_packing.py` - Reconstruction-over-storage: `ALLOCATOR_VERSION = golden_angle_v1` + `allocator_layout_descriptor` - Not holographic seals (null points ≠ closed unit versors) -### Vector 4 — Fibonacci-word observability choreography 🔴 staged +### Vector 4 — Fibonacci-word observability choreography 🟢 + +Module: `core/physics/fibonacci_word_schedule.py` Drive: \(W_0=B, W_1=A, W_{n+1}=W_n W_{n-1}\) for telemetry / sealed-holdout sampling. -**Outside cognitive truth path.** Not yet implemented (plan D5). +**Outside cognitive truth path** — pure schedule only; no vault/field mutation. -### Vector 5 — Topological anyon / braid holonomy 🔴 research quarantine +### Vector 5 — Topological anyon / braid holonomy 🟢 quarantine box -Drive: isolated `algebra/topological_reasoning/` study; blocked from production. -Not implemented (plan D6). Must not enter serve/FFI until proofs exist. +Package: `algebra/topological_reasoning/` (README + `FUSION_RULE` stub). +Production AST pin: `chat/`, `core/physics/`, `generate/`, `vault/`, `teaching/` must not import it. +No fusion/braid logic until proofs + human Accept. --- @@ -79,10 +83,10 @@ Not implemented (plan D6). Must not enter serve/FFI until proofs exist. | Phase | Vector | Status | |-------|--------|--------| | 1 | V1 search + κ cert gate | 🟢 | -| 2 | V2 multi-scale energy study | 🟡 table only | +| 2 | V2 multi-scale energy study | 🟢 helpers; 🟡 not production default | | 3 | V3 packing | 🟢 | -| 4 | V4 word scheduler | 🔴 | -| 5 | V5 anyons | 🔴 quarantine | +| 4 | V4 word scheduler | 🟢 | +| 5 | V5 anyons | 🟢 quarantine only (no logic) | --- diff --git a/docs/research/third-door-blueprint-fidelity.md b/docs/research/third-door-blueprint-fidelity.md index a3d44174..72e21fb6 100644 --- a/docs/research/third-door-blueprint-fidelity.md +++ b/docs/research/third-door-blueprint-fidelity.md @@ -302,9 +302,10 @@ PY | Golden-Angle atlas packing \(d_{\min}=0.12\) (V3) | 🟢 ADR-0242 (`atlas_packing`; CGA null-point \(d\); `golden_angle_v1`) | | Fibonacci section search cert/failure (V1) | 🟢 ADR-0242 (`FibonacciSearchCertificate` \| `OptimizationFailure`; dual-run digest) | | κ cert gate fail → baseline 1.0 (V1b) | 🟢 `propose_kappa_from_search` / `goldtether.propose_kappa_line_search` | -| Multi-scale \(\tau_n=F_n\tau_0\) (V2) | 🟡 table only; production multi-band \(E_n(t)\) not default | -| Fibonacci-word scheduler (V4) | 🔴 staged | -| Fibonacci anyons (V5) | 🔴 research quarantine | +| Multi-scale \(\tau_n=F_n\tau_0\) + \(E_n(t)\) helpers (V2) | 🟢 research API (`multi_scale_energy`); production energy default unchanged | +| Fibonacci-word scheduler (V4) | 🟢 `fibonacci_word_schedule` (telemetry only) | +| Fibonacci anyons (V5) | 🟢 quarantine package only; zero production imports | +| Sensorium → ψ feed (I-04) | 🟢 `sensorium_wave_feed` (fake packets + real \(\rho\)) | | Contemplation Trace A SPECULATIVE holographic seal (P9) | 🟢 `core/contemplation/wave_seam.py` (hypothesis vs COHERENT evidence) | | Energy boundary + multi-scale τ (P10 Trace B) | 🟢 `wave_energy_boundary` (wave residual → energy/trajectory; τ_n=F_n·τ_0; E0–E1 crystallization) | diff --git a/docs/specs/runtime_contracts.md b/docs/specs/runtime_contracts.md index e89bcc42..5f5464a5 100644 --- a/docs/specs/runtime_contracts.md +++ b/docs/specs/runtime_contracts.md @@ -866,10 +866,14 @@ wrong=0 serve entry path (AST-pinned in `tests/test_third_door_cohesion.py`): |--------|------| | `core.physics.wave_manifold` | Cl(4,1) wave field \(\psi\), leakage, polar conjugacy, chiral, \(\rho\) | | `core.physics.holographic_vault` | Durable standing-wave spectrum via `VaultStore` | -| `core.physics.atlas_packing` | Golden-Angle mode packing (ADR-0242) | -| `core.physics.fibonacci_search` | Fixed-budget unimodal section search (ADR-0242) | +| `core.physics.atlas_packing` | Golden-Angle mode packing (ADR-0242 V3) | +| `core.physics.fibonacci_search` | Cert-gated Fibonacci section search (ADR-0242 V1) | +| `core.physics.fibonacci_word_schedule` | Fibonacci-word observability choreography (ADR-0242 V4; telemetry only) | +| `core.physics.multi_scale_energy` | Multi-band \(E_n(t)\) research helpers (ADR-0242 V2; not serve) | +| `core.physics.sensorium_wave_feed` | Sensorium → \(\psi\) construction feed (I-04) | | `core.contemplation.wave_seam` | P9 Trace A SPECULATIVE seal + hypothesis/evidence reconstruct | | `core.physics.wave_energy_boundary` | P10 Trace B residual→energy / \(\tau_n\) / crystallization | +| `algebra.topological_reasoning` | ADR-0242 V5 research quarantine only — never serve/production | Wiring any of these into serve requires an explicit ADR amendment and a failing-to-green containment test change in the same PR. diff --git a/tests/test_adr_0241_sensorium_wave_feed.py b/tests/test_adr_0241_sensorium_wave_feed.py new file mode 100644 index 00000000..59ae7a85 --- /dev/null +++ b/tests/test_adr_0241_sensorium_wave_feed.py @@ -0,0 +1,222 @@ +"""D7 sensorium → ψ feed (I-04 boundary) pins. + +ADR-0241 cohesion: modality packets compile to Cl(4,1) wave fields; +multimodal resonance uses WaveManifold.phase_correlation only. + +Honest fake packets when real compilers are not under test. +No cosine / ANN / sklearn neighbors. +""" + +from __future__ import annotations + +import ast +from pathlib import Path + +import numpy as np +import pytest + +from algebra.cl41 import N_COMPONENTS +from algebra.rotor import make_rotor_from_angle +from algebra.versor import versor_condition +from core.physics.sensorium_wave_feed import ( + ModalityPacket, + compile_packet_to_psi, + fake_deterministic_packet, + phase_correlate, + superpose_packets, +) +from core.physics.wave_manifold import WaveManifold + +_ROOT = Path(__file__).resolve().parents[1] +_MODULE = _ROOT / "core/physics/sensorium_wave_feed.py" +_CLOSURE = 1e-6 + + +def _closed(angle: float = 0.3, plane: int = 6) -> np.ndarray: + return make_rotor_from_angle(angle, bivector_idx=plane) + + +# --- compile / packet -------------------------------------------------------- + + +def test_compile_packet_to_psi_from_modality_packet(): + coeffs = _closed(0.41, plane=7) + packet = ModalityPacket(modality_id="vision", coefficients=coeffs) + psi = compile_packet_to_psi(packet) + assert psi.shape == (N_COMPONENTS,) + assert psi.dtype == np.float64 + assert float(np.linalg.norm(psi - coeffs)) < 1e-15 + # Fresh copy — not the same buffer + assert psi is not packet.coefficients + + +def test_compile_packet_to_psi_from_dict(): + coeffs = _closed(0.22, plane=8) + psi = compile_packet_to_psi( + {"modality_id": "audio", "coefficients": coeffs.tolist()} + ) + assert psi.shape == (32,) + assert float(np.linalg.norm(psi - coeffs)) < 1e-12 + + +def test_compile_packet_accepts_modality_and_psi_keys(): + coeffs = _closed(0.15, plane=6) + psi = compile_packet_to_psi({"modality": "text", "psi": coeffs}) + assert float(np.linalg.norm(psi - coeffs)) < 1e-15 + + +def test_compile_packet_rejects_wrong_shape(): + with pytest.raises(ValueError, match="shape"): + ModalityPacket(modality_id="x", coefficients=np.zeros(16)) + + +def test_compile_packet_rejects_empty_modality_id(): + with pytest.raises(ValueError, match="modality_id"): + ModalityPacket(modality_id=" ", coefficients=_closed()) + + +def test_compile_packet_rejects_incomplete_dict(): + with pytest.raises(ValueError, match="modality_id"): + compile_packet_to_psi({"coefficients": _closed()}) + + +# --- superpose --------------------------------------------------------------- + + +def test_superpose_packets_is_sum_of_compiled(): + a = fake_deterministic_packet("audio", angle=0.2, plane=6) + b = fake_deterministic_packet("vision", angle=0.55, plane=8) + total = superpose_packets([a, b]) + expected = compile_packet_to_psi(a) + compile_packet_to_psi(b) + assert float(np.linalg.norm(total - expected)) < 1e-15 + + +def test_superpose_packets_empty_refused(): + with pytest.raises(ValueError, match="empty"): + superpose_packets([]) + + +# --- phase correlate (I-04) -------------------------------------------------- + + +def test_phase_correlate_delegates_to_wave_manifold(): + a = _closed(0.2, plane=6) + b = _closed(0.55, plane=8) + M = WaveManifold() + rho_direct = M.phase_correlation(a, b) + rho_feed = phase_correlate(a, b, manifold=M) + assert abs(rho_feed - rho_direct) < 1e-15 + + +def test_phase_correlate_symmetric(): + a = compile_packet_to_psi(fake_deterministic_packet("text", angle=0.3, plane=6)) + b = compile_packet_to_psi(fake_deterministic_packet("audio", angle=0.7, plane=9)) + assert abs(phase_correlate(a, b) - phase_correlate(b, a)) < 1e-12 + assert phase_correlate(a, a) > 0.5 + + +def test_cross_modal_fake_packets_phase_correlate(): + """I-04 feed path: two modalities → ψ → algebraic ρ (not cosine).""" + audio = fake_deterministic_packet("audio", angle=0.25, plane=6) + vision = fake_deterministic_packet("vision", angle=0.25, plane=6) + # Same closed rotor → strong self-like correlation across modality tags + rho_same = phase_correlate( + compile_packet_to_psi(audio), + compile_packet_to_psi(vision), + ) + assert rho_same > 0.5 + + other = fake_deterministic_packet("vision", angle=1.1, plane=10) + rho_diff = phase_correlate( + compile_packet_to_psi(audio), + compile_packet_to_psi(other), + ) + # Distinct planes/angles are not required to be lower, but path must run. + assert isinstance(rho_diff, float) + + +# --- fake deterministic fixtures --------------------------------------------- + + +def test_fake_deterministic_packet_closed_and_stable(): + p1 = fake_deterministic_packet("sensorimotor", angle=0.4, plane=7) + p2 = fake_deterministic_packet("sensorimotor", angle=0.4, plane=7) + assert p1.modality_id == "sensorimotor" + assert float(np.linalg.norm(p1.coefficients - p2.coefficients)) == 0.0 + assert versor_condition(p1.coefficients) < _CLOSURE + + +def test_fake_deterministic_matches_make_rotor(): + angle, plane = 0.37, 11 + packet = fake_deterministic_packet("motor", angle=angle, plane=plane) + expected = make_rotor_from_angle(angle, bivector_idx=plane) + assert float(np.linalg.norm(packet.coefficients - expected)) < 1e-15 + + +# --- hygiene: no approx neighbors / cosine ----------------------------------- + + +def test_module_forbids_approx_neighbor_and_cosine_imports(): + """I-04: no faiss / hnsw / annoy / sklearn / cosine_similarity stack.""" + src = _MODULE.read_text(encoding="utf-8") + tree = ast.parse(src) + banned_roots = { + "faiss", + "hnswlib", + "annoy", + "sklearn", + "scipy", + "sklearn.neighbors", + } + banned_names = { + "cosine_similarity", + "NearestNeighbors", + "cosine", + "cdist", + } + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + root = alias.name.split(".")[0] + assert root not in banned_roots, f"banned import {alias.name}" + assert alias.name not in banned_roots + if isinstance(node, ast.ImportFrom) and node.module: + root = node.module.split(".")[0] + assert root not in banned_roots, f"banned from {node.module}" + assert node.module not in banned_roots + for alias in node.names: + assert alias.name not in banned_names + if isinstance(node, ast.Attribute): + assert node.attr not in banned_names + if isinstance(node, ast.Name): + assert node.id not in banned_names + # Source-level ban on cosine similarity wording as implementation path + assert "cosine_similarity" not in src + assert "NearestNeighbors" not in src + + +def test_phase_correlate_source_only_calls_phase_correlation(): + """phase_correlate body must call WaveManifold.phase_correlation only.""" + src = _MODULE.read_text(encoding="utf-8") + tree = ast.parse(src) + func = None + for node in ast.walk(tree): + if isinstance(node, ast.FunctionDef) and node.name == "phase_correlate": + func = node + break + assert func is not None, "phase_correlate not found" + call_attrs: list[str] = [] + for node in ast.walk(func): + if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute): + call_attrs.append(node.func.attr) + assert "phase_correlation" in call_attrs + # No alternate resonance / similarity calls inside the function + forbidden = { + "cosine_similarity", + "resonant_recall", + "resonant_reconstruct", + "dot", + "norm", + } + for attr in call_attrs: + assert attr not in forbidden, f"phase_correlate must not call .{attr}" diff --git a/tests/test_adr_0242_fibonacci_word.py b/tests/test_adr_0242_fibonacci_word.py new file mode 100644 index 00000000..908de980 --- /dev/null +++ b/tests/test_adr_0242_fibonacci_word.py @@ -0,0 +1,136 @@ +"""ADR-0242 V4 (D5) — Fibonacci-word observability scheduler. + +Telemetry-only; dual-run deterministic; cannot mutate cognitive truth. +""" + +from __future__ import annotations + +import pytest + +from core.physics.fibonacci_word_schedule import ( + Action, + fibonacci_word, + iter_schedule_actions, + schedule_actions, + word_length, +) + + +# Expected words from Drive: W0=B, W1=A, W_{n+1}=W_n W_{n-1} +_EXPECTED = { + 0: "B", + 1: "A", + 2: "AB", + 3: "ABA", + 4: "ABAAB", + 5: "ABAABABA", + 6: "ABAABABAABAAB", +} + + +def test_w0_is_b(): + assert fibonacci_word(0) == "B" + assert schedule_actions(0) == ("B",) + + +def test_w1_is_a(): + assert fibonacci_word(1) == "A" + assert schedule_actions(1) == ("A",) + + +@pytest.mark.parametrize("n,expected", sorted(_EXPECTED.items())) +def test_fibonacci_word_table(n: int, expected: str): + assert fibonacci_word(n) == expected + + +@pytest.mark.parametrize("n,expected", sorted(_EXPECTED.items())) +def test_schedule_actions_matches_word(n: int, expected: str): + actions = schedule_actions(n) + assert actions == tuple(expected) + assert all(a in (Action.A.value, Action.B.value) for a in actions) + + +def test_w2_ab_w3_aba_w4_abaab(): + assert fibonacci_word(2) == "AB" + assert fibonacci_word(3) == "ABA" + assert fibonacci_word(4) == "ABAAB" + + +def test_recurrence_w_n_concat(): + """W_{n+1} == W_n + W_{n-1} for several n.""" + for n in range(1, 10): + assert fibonacci_word(n + 1) == fibonacci_word(n) + fibonacci_word(n - 1) + + +def test_length_equals_fib_n_plus_1(): + """|W_n| = F_{n+1} with F_0=0, F_1=1, F_2=1, F_3=2, F_4=3, F_5=5, …""" + # F_{n+1} table for n=0..8 → 1,1,2,3,5,8,13,21,34 + fib_np1 = [1, 1, 2, 3, 5, 8, 13, 21, 34] + for n, expected_len in enumerate(fib_np1): + word = fibonacci_word(n) + assert len(word) == expected_len + assert word_length(n) == expected_len + assert len(schedule_actions(n)) == expected_len + + +def test_dual_run_identical(): + """Deterministic: two independent evaluations produce byte-identical results.""" + for n in range(0, 12): + a = fibonacci_word(n) + b = fibonacci_word(n) + assert a == b + assert schedule_actions(n) == schedule_actions(n) + assert tuple(iter_schedule_actions(n)) == schedule_actions(n) + + +def test_action_enum_values(): + assert Action.A.value == "A" + assert Action.B.value == "B" + assert Action.A == "A" + assert Action.B == "B" + + +def test_rejects_negative_n(): + with pytest.raises(ValueError, match="non-negative"): + fibonacci_word(-1) + with pytest.raises(ValueError, match="non-negative"): + schedule_actions(-1) + with pytest.raises(ValueError, match="non-negative"): + word_length(-1) + + +def test_rejects_non_int_n(): + with pytest.raises(TypeError): + fibonacci_word(1.5) # type: ignore[arg-type] + with pytest.raises(TypeError): + fibonacci_word(True) # type: ignore[arg-type] + + +def test_only_ab_alphabet(): + for n in range(0, 14): + word = fibonacci_word(n) + assert set(word) <= {"A", "B"} + if n == 0: + assert set(word) == {"B"} + elif n == 1: + assert set(word) == {"A"} + else: + assert set(word) == {"A", "B"} + + +def test_module_is_pure_no_side_effect_imports(): + """Sovereignty pin: module must not pull vault/field mutation surfaces.""" + import ast + from pathlib import Path + + src = Path("core/physics/fibonacci_word_schedule.py").read_text() + tree = ast.parse(src) + forbidden = {"vault", "field", "store", "VaultStore", "generate", "chat"} + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + root = alias.name.split(".")[0] + assert root not in forbidden, alias.name + if isinstance(node, ast.ImportFrom) and node.module: + root = node.module.split(".")[0] + assert root not in forbidden, node.module diff --git a/tests/test_adr_0242_multi_scale_energy.py b/tests/test_adr_0242_multi_scale_energy.py new file mode 100644 index 00000000..229b1cc8 --- /dev/null +++ b/tests/test_adr_0242_multi_scale_energy.py @@ -0,0 +1,167 @@ +"""ADR-0242 V2 — multi-scale temporal energy basis (research prototype). + +Pins Drive form E_n = E0 · exp(-age / (F_n · τ_0)), dyadic baseline comparison, +determinism, and serve quarantine. Does **not** change FieldEnergyOperator defaults. +""" + +from __future__ import annotations + +import ast +from math import exp +from pathlib import Path + +import pytest + +from core.physics.fibonacci_search import fibonacci_number +from core.physics.multi_scale_energy import ( + comparative_residual_separation, + dyadic_tau_schedule, + multi_scale_energy_for_schedule, + multi_scale_energy_vector, + schedule_mid_span_fraction, +) +from core.physics.wave_energy_boundary import fibonacci_tau_schedule + +_ROOT = Path(__file__).resolve().parents[1] + + +# --- Schedules -------------------------------------------------------------- + + +def test_dyadic_tau_schedule_powers_of_two(): + # τ_n = 2^{n-1} · τ_0 for n = 1..5 → 1, 2, 4, 8, 16 when τ_0=1 + assert dyadic_tau_schedule(tau0=1.0, levels=5) == (1.0, 2.0, 4.0, 8.0, 16.0) + assert dyadic_tau_schedule(tau0=0.5, levels=4) == (0.5, 1.0, 2.0, 4.0) + + +def test_dyadic_tau_schedule_rejects_bad_inputs(): + with pytest.raises(ValueError): + dyadic_tau_schedule(tau0=0.0, levels=3) + with pytest.raises(ValueError): + dyadic_tau_schedule(tau0=1.0, levels=0) + + +def test_fibonacci_tau_matches_fibonacci_number(): + taus = fibonacci_tau_schedule(tau0=1.0, levels=6) + expected = tuple(float(fibonacci_number(i)) for i in range(1, 7)) + assert taus == expected + + +# --- Drive energy formula --------------------------------------------------- + + +def test_multi_scale_energy_vector_matches_drive_formula(): + e0, age, tau0, levels = 2.0, 3.0, 1.0, 5 + got = multi_scale_energy_vector(e0, age, tau0=tau0, levels=levels) + expected = tuple( + e0 * exp(-age / (fibonacci_number(i) * tau0)) for i in range(1, levels + 1) + ) + assert len(got) == levels + for a, b in zip(got, expected, strict=True): + assert a == pytest.approx(b, rel=0.0, abs=1e-15) + + +def test_multi_scale_energy_matches_schedule_helper(): + e0, age, tau0, levels = 1.0, 2.5, 0.5, 6 + via_vector = multi_scale_energy_vector(e0, age, tau0=tau0, levels=levels) + via_schedule = multi_scale_energy_for_schedule( + e0, age, fibonacci_tau_schedule(tau0=tau0, levels=levels) + ) + assert via_vector == via_schedule + + +def test_decay_larger_age_yields_smaller_energy(): + young = multi_scale_energy_vector(1.0, age=1.0, tau0=1.0, levels=5) + old = multi_scale_energy_vector(1.0, age=10.0, tau0=1.0, levels=5) + assert all(o < y for o, y in zip(old, young, strict=True)) + # age=0 → full e0 at every scale + zero = multi_scale_energy_vector(1.25, age=0.0, tau0=1.0, levels=4) + assert zero == (1.25, 1.25, 1.25, 1.25) + + +def test_larger_tau_scale_retains_more_energy(): + # Within a Fibonacci vector, coarser scales (larger F_n) decay slower. + vec = multi_scale_energy_vector(1.0, age=5.0, tau0=1.0, levels=8) + # F_1=1, F_8=21 → last component strictly larger residual energy + assert vec[-1] > vec[0] + + +def test_multi_scale_energy_rejects_bad_inputs(): + with pytest.raises(ValueError): + multi_scale_energy_vector(1.0, age=-1.0) + with pytest.raises(ValueError): + multi_scale_energy_vector(float("nan"), age=1.0) + with pytest.raises(ValueError): + multi_scale_energy_for_schedule(1.0, 1.0, ()) + with pytest.raises(ValueError): + multi_scale_energy_for_schedule(1.0, 1.0, (1.0, 0.0)) + + +# --- Determinism + comparative surface -------------------------------------- + + +def test_deterministic_dual_run(): + kwargs = dict(e0=1.0, age=4.0, tau0=1.0, levels=8) + a = multi_scale_energy_vector(**kwargs) + b = multi_scale_energy_vector(**kwargs) + assert a == b + ca = comparative_residual_separation(**kwargs) + cb = comparative_residual_separation(**kwargs) + assert ca == cb + + +def test_fibonacci_bands_longer_than_dyadic_mid_scale(): + """Mid-scale Fibonacci bands occupy a larger fraction of total span. + + Absolute τ: F_n grows as ~φ^n while dyadic is 2^{n-1}, so dyadic absolute + τ is larger late. Comparatively, φ-growth places the mid-index band further + along the *normalized* hierarchy (span fraction) than pure dyadic — the + structural property the V2 research pin checks. + """ + levels = 8 + tau0 = 1.0 + fib = fibonacci_tau_schedule(tau0=tau0, levels=levels) + dyad = dyadic_tau_schedule(tau0=tau0, levels=levels) + mid = levels // 2 + fib_frac = schedule_mid_span_fraction(fib, index=mid) + dyad_frac = schedule_mid_span_fraction(dyad, index=mid) + assert fib_frac > dyad_frac + # Absolute mid τ still follows F_5=5 vs 2^4=16 + assert fib[mid] == 5.0 + assert dyad[mid] == 16.0 + + +def test_comparative_residual_separation_shape(): + report = comparative_residual_separation(1.0, age=3.0, tau0=1.0, levels=5) + assert report["levels"] == 5 + assert len(report["fibonacci_taus"]) == 5 + assert len(report["dyadic_taus"]) == 5 + assert len(report["fibonacci_energies"]) == 5 + assert len(report["dyadic_energies"]) == 5 + assert len(report["energy_gap_fib_minus_dyadic"]) == 5 + # age=0 → identical unit energies regardless of schedule + zero = comparative_residual_separation(1.0, age=0.0, tau0=1.0, levels=4) + assert zero["fibonacci_energies"] == (1.0, 1.0, 1.0, 1.0) + assert zero["dyadic_energies"] == (1.0, 1.0, 1.0, 1.0) + assert zero["energy_gap_fib_minus_dyadic"] == (0.0, 0.0, 0.0, 0.0) + + +# --- Serve quarantine (A-04) ------------------------------------------------ + + +def test_serve_runtime_does_not_import_multi_scale_energy(): + tree = ast.parse((_ROOT / "chat/runtime.py").read_text(encoding="utf-8")) + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom) and node.module: + assert "multi_scale_energy" not in node.module + assert "wave_energy_boundary" not in node.module + if isinstance(node, ast.Import): + for alias in node.names: + assert "multi_scale_energy" not in alias.name + + +def test_field_energy_operator_untouched_by_multi_scale_module(): + """Production energy operator must not import the research multi-scale path.""" + energy_src = (_ROOT / "core/physics/energy.py").read_text(encoding="utf-8") + assert "multi_scale_energy" not in energy_src + assert "fibonacci_tau_schedule" not in energy_src diff --git a/tests/test_adr_0242_topological_quarantine.py b/tests/test_adr_0242_topological_quarantine.py new file mode 100644 index 00000000..d87dce88 --- /dev/null +++ b/tests/test_adr_0242_topological_quarantine.py @@ -0,0 +1,124 @@ +"""ADR-0242 V5 (D6) — topological_reasoning research quarantine pins. + +Authority: docs/adr/ADR-0242-atlas-packing-and-fibonacci.md Vector 5. +Package may exist under algebra/topological_reasoning/ for isolated study. +Production packages must not import it. +""" + +from __future__ import annotations + +import ast +from pathlib import Path + +import pytest + +_ROOT = Path(__file__).resolve().parents[1] + +# Production surfaces that must never import the research quarantine package. +_PRODUCTION_PACKAGES = ( + "chat", + "core/physics", + "generate", + "vault", + "teaching", +) + +_BANNED_MARKERS = ( + "topological_reasoning", + "algebra.topological_reasoning", +) + + +def _iter_python_files(package_rel: str) -> list[Path]: + base = _ROOT / package_rel + if not base.is_dir(): + return [] + return sorted(p for p in base.rglob("*.py") if p.is_file()) + + +def _import_mentions_topological(tree: ast.AST) -> list[str]: + """Return import module strings that reference topological_reasoning.""" + hits: list[str] = [] + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + name = alias.name + if any(m in name for m in _BANNED_MARKERS): + hits.append(name) + elif isinstance(node, ast.ImportFrom): + mod = node.module or "" + if any(m in mod for m in _BANNED_MARKERS): + hits.append(mod) + # from algebra import topological_reasoning + if mod == "algebra" or mod.endswith(".algebra"): + for alias in node.names: + if alias.name == "topological_reasoning" or ( + alias.name and "topological_reasoning" in alias.name + ): + hits.append(f"{mod}.{alias.name}") + return hits + + +def test_topological_reasoning_package_imports_in_isolation() -> None: + """Package is importable on its own without production wiring.""" + import algebra.topological_reasoning as tr + + assert hasattr(tr, "FUSION_RULE") + assert isinstance(tr.FUSION_RULE, str) + assert tr.FUSION_RULE == "tau_otimes_tau_eq_1_oplus_tau" + # Research label only — no callable production fusion API required. + assert "FUSION_RULE" in tr.__all__ + + +def test_algebra_public_import_still_works() -> None: + """Quarantine package must not break the algebra package surface.""" + import algebra + from algebra import versor_condition, word_transition_rotor + + assert callable(versor_condition) + assert callable(word_transition_rotor) + # Research package is not re-exported on algebra's public surface. + assert not hasattr(algebra, "topological_reasoning") or "topological_reasoning" not in getattr( + algebra, "__all__", () + ) + + +def test_topological_reasoning_package_directory_may_exist() -> None: + """Algebraic research quarantine box is allowed to exist on disk.""" + pkg = _ROOT / "algebra" / "topological_reasoning" + assert pkg.is_dir() + assert (pkg / "__init__.py").is_file() + assert (pkg / "README.md").is_file() + + +@pytest.mark.parametrize("package_rel", _PRODUCTION_PACKAGES) +def test_production_packages_do_not_import_topological_reasoning( + package_rel: str, +) -> None: + """Architectural: production trees must not import topological_reasoning.""" + files = _iter_python_files(package_rel) + assert files, f"expected python sources under {package_rel}" + + violations: list[str] = [] + for path in files: + # Never scan the quarantine package itself (it lives under algebra/). + rel = path.relative_to(_ROOT).as_posix() + if "topological_reasoning" in rel.split("/"): + continue + try: + src = path.read_text(encoding="utf-8") + except OSError as exc: + violations.append(f"{rel}: unreadable ({exc})") + continue + try: + tree = ast.parse(src, filename=rel) + except SyntaxError as exc: + violations.append(f"{rel}: syntax error ({exc})") + continue + for hit in _import_mentions_topological(tree): + violations.append(f"{rel}: imports {hit}") + + assert not violations, ( + "ADR-0242 V5 quarantine violated — production import(s) of " + "topological_reasoning:\n" + "\n".join(violations) + ) diff --git a/tests/test_third_door_cohesion.py b/tests/test_third_door_cohesion.py index dcba364e..7b2c88dc 100644 --- a/tests/test_third_door_cohesion.py +++ b/tests/test_third_door_cohesion.py @@ -60,17 +60,23 @@ def test_phase0_a04_serve_path_quarantines_wave_and_fibonacci(): "wave_manifold", "holographic_vault", "fibonacci_search", + "fibonacci_word_schedule", "atlas_packing", "wave_seam", # P9 Trace A — contemplation only, never serve "wave_energy_boundary", # P10 Trace B — energy/τ gate, never serve + "multi_scale_energy", # ADR-0242 V2 research multi-band E_n(t), never serve + "sensorium_wave_feed", # D7 I-04 sensorium→ψ feed, never serve } banned_substrings = ( "wave_manifold", "holographic_vault", "fibonacci_search", + "fibonacci_word_schedule", "atlas_packing", "wave_seam", "wave_energy_boundary", + "multi_scale_energy", + "sensorium_wave_feed", ) for node in ast.walk(tree): if isinstance(node, ast.Import): From 44f7258b16e6078bb5c0fbfdcd308bd4fa544abc Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 14 Jul 2026 20:23:08 -0700 Subject: [PATCH 6/8] fix(algebra): P11a physics hot paths via algebra.backend (Rust-ready) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop wave/Third-Door physics from bypassing native dispatch: - Route geometric_product / versor_apply / versor_condition / cga_inner through algebra.backend in wave_manifold, goldtether, trajectory, dynamic_manifold, surprise, holographic_vault, atlas_packing, biography, self_authorship. - Backend: dtype-aware Rust use — f32 workloads use core_rs; f64 wave residual pins keep Python SOT until f64 GP parity exists. Coerce arrays for PyO3 bindings; fail soft to Python. - AST hygiene pin: tests/test_physics_backend_dispatch_hygiene.py - Docs: RUST.md, runtime_contracts, fidelity (ADR-0235 / UMA hygiene). Verified: wave + cohesion suites green default and CORE_BACKEND=rust (with core_rs built). MLX still exploratory off-serve. --- algebra/backend.py | 56 +++++++++-- core/physics/atlas_packing.py | 3 +- core/physics/biography.py | 3 +- core/physics/dynamic_manifold.py | 4 +- core/physics/goldtether.py | 5 +- core/physics/holographic_vault.py | 2 +- core/physics/self_authorship.py | 2 +- core/physics/surprise.py | 3 +- core/physics/trajectory_invariants.py | 5 +- core/physics/wave_manifold.py | 17 +++- docs/RUST.md | 26 ++++++ .../research/third-door-blueprint-fidelity.md | 1 + docs/specs/runtime_contracts.md | 14 +++ .../test_physics_backend_dispatch_hygiene.py | 92 +++++++++++++++++++ 14 files changed, 208 insertions(+), 25 deletions(-) create mode 100644 tests/test_physics_backend_dispatch_hygiene.py diff --git a/algebra/backend.py b/algebra/backend.py index b3a32dd9..92941e61 100644 --- a/algebra/backend.py +++ b/algebra/backend.py @@ -48,9 +48,36 @@ def _build_cga_inner_metric() -> np.ndarray: _CGA_INNER_METRIC: np.ndarray = _build_cga_inner_metric() +def _f32_1d32(x: np.ndarray) -> np.ndarray: + """Contiguous f32 (32,) for core_rs PyReadonlyArray1 bindings.""" + return np.ascontiguousarray( + np.asarray(x, dtype=np.float32).reshape(-1)[:32], dtype=np.float32 + ) + + +def _is_f32_workload(*arrays: np.ndarray) -> bool: + """True when all arrays are float32 (Rust f32 kernel is parity-safe). + + float64 wave residual pins require Python SOT (or future f64 Rust GP). + Forcing f64→f32 would break 1e-9 chiral / leakage pins (ADR-0241). + """ + return all(np.asarray(a).dtype == np.float32 for a in arrays) + + def geometric_product(A: np.ndarray, B: np.ndarray) -> np.ndarray: - if _RUST: - return np.asarray(_rs.geometric_product(A, B), dtype=np.float32) + """Cl(4,1) geometric product via Rust f32 when enabled, else Python. + + float64 inputs always use the pure-Python product (semantic SOT for + wave-field residual math). float32 field-graph workloads get Rust. + """ + if _RUST and _is_f32_workload(A, B): + try: + return np.asarray( + _rs.geometric_product(_f32_1d32(A), _f32_1d32(B)), + dtype=np.float32, + ) + except (AttributeError, TypeError, ValueError, Exception): + pass from algebra.cl41 import geometric_product as _gp return _gp(A, B) @@ -67,25 +94,34 @@ def versor_apply(V: np.ndarray, F: np.ndarray) -> np.ndarray: """ if _RUST: try: - Vc = np.ascontiguousarray(V, dtype=np.float64) - Fc = np.ascontiguousarray(F, dtype=np.float64) - return np.asarray(_rs.versor_apply_with_closure_f64(Vc, Fc), dtype=np.float64) - except (AttributeError, Exception): + Vc = np.ascontiguousarray(V, dtype=np.float64).reshape(-1)[:32] + Fc = np.ascontiguousarray(F, dtype=np.float64).reshape(-1)[:32] + return np.asarray( + _rs.versor_apply_with_closure_f64(Vc, Fc), dtype=np.float64 + ) + except (AttributeError, TypeError, ValueError, Exception): pass from algebra.versor import versor_apply as _va return _va(V, F) def versor_condition(F: np.ndarray) -> float: - if _RUST: - return float(_rs.versor_condition(F)) + """Versor residual. Rust f32 path only for float32 inputs (see GP note).""" + if _RUST and _is_f32_workload(F): + try: + return float(_rs.versor_condition(_f32_1d32(F))) + except (AttributeError, TypeError, ValueError, Exception): + pass from algebra.versor import versor_condition as _vc return _vc(F) def cga_inner(X: np.ndarray, Y: np.ndarray) -> float: - if _RUST: - return float(_rs.cga_inner(X, Y)) + if _RUST and _is_f32_workload(X, Y): + try: + return float(_rs.cga_inner(_f32_1d32(X), _f32_1d32(Y))) + except (AttributeError, TypeError, ValueError, Exception): + pass from algebra.cga import cga_inner as _ci return _ci(X, Y) diff --git a/core/physics/atlas_packing.py b/core/physics/atlas_packing.py index e2f590d9..25a2adba 100644 --- a/core/physics/atlas_packing.py +++ b/core/physics/atlas_packing.py @@ -22,7 +22,8 @@ from typing import Sequence import numpy as np -from algebra.cga import cga_inner, embed_point +from algebra.backend import cga_inner +from algebra.cga import embed_point from core.physics.wave_manifold import WaveManifold PHI = (1.0 + math.sqrt(5.0)) / 2.0 diff --git a/core/physics/biography.py b/core/physics/biography.py index 62d2f9a9..09ba3dae 100644 --- a/core/physics/biography.py +++ b/core/physics/biography.py @@ -16,9 +16,10 @@ from typing import Any, Sequence import numpy as np +from algebra.backend import versor_condition from algebra.cl41 import N_COMPONENTS from algebra.holonomy import holonomy_encode, holonomy_similarity -from algebra.versor import unitize_versor, versor_condition +from algebra.versor import unitize_versor from core.physics.wave_manifold import WaveManifold _CLOSURE_TOL = 1e-6 diff --git a/core/physics/dynamic_manifold.py b/core/physics/dynamic_manifold.py index 36ff87e4..cec17247 100644 --- a/core/physics/dynamic_manifold.py +++ b/core/physics/dynamic_manifold.py @@ -16,8 +16,9 @@ from typing import Optional, Sequence, Tuple, Union import numpy as np +from algebra.backend import geometric_product, versor_condition from algebra.cga import is_null -from algebra.cl41 import N_COMPONENTS, geometric_product, grade_project, reverse +from algebra.cl41 import N_COMPONENTS, grade_project, reverse from algebra.null_point import ( NullPointRecoveryError, dilator, @@ -26,7 +27,6 @@ from algebra.null_point import ( translator, ) from algebra.rotor import rotor_power, word_transition_rotor -from algebra.versor import versor_condition _CLOSURE_TOL = 1e-6 _NEAR_ZERO = 1e-12 diff --git a/core/physics/goldtether.py b/core/physics/goldtether.py index c6cbc708..f456a3fc 100644 --- a/core/physics/goldtether.py +++ b/core/physics/goldtether.py @@ -28,9 +28,10 @@ from typing import Any, Literal, Optional, Tuple import numpy as np -from algebra.cl41 import N_COMPONENTS, geometric_product, reverse +from algebra.backend import geometric_product, versor_condition +from algebra.cl41 import N_COMPONENTS, reverse from algebra.rotor import rotor_power, word_transition_rotor -from algebra.versor import versor_condition, versor_unit_residual +from algebra.versor import versor_unit_residual from core.physics.wave_manifold import WaveManifold _CLOSURE_TOL = 1e-6 diff --git a/core/physics/holographic_vault.py b/core/physics/holographic_vault.py index 261a7dd7..1ac768c9 100644 --- a/core/physics/holographic_vault.py +++ b/core/physics/holographic_vault.py @@ -24,8 +24,8 @@ from typing import Any, Optional import numpy as np +from algebra.backend import versor_condition from algebra.cl41 import N_COMPONENTS -from algebra.versor import versor_condition from core.physics.wave_manifold import WaveManifold from teaching.epistemic import EpistemicStatus from vault.store import VaultStore, _parse_entry_status, _status_admits diff --git a/core/physics/self_authorship.py b/core/physics/self_authorship.py index 95faccf4..55ef6f88 100644 --- a/core/physics/self_authorship.py +++ b/core/physics/self_authorship.py @@ -9,8 +9,8 @@ from typing import Any, Mapping, Sequence import numpy as np +from algebra.backend import versor_condition from algebra.cl41 import N_COMPONENTS -from algebra.versor import versor_condition from core.physics.dynamic_manifold import conformal_procrustes from core.physics.goldtether import GoldTetherMonitor, coherence_residual from core.physics.surprise import dual_procrustes_surprise, surprise_residual diff --git a/core/physics/surprise.py b/core/physics/surprise.py index 6c66756c..27bcc483 100644 --- a/core/physics/surprise.py +++ b/core/physics/surprise.py @@ -39,9 +39,8 @@ from typing import Optional, Sequence, Tuple, Union import numpy as np -from algebra.cga import cga_inner +from algebra.backend import cga_inner, versor_condition from algebra.cl41 import N_COMPONENTS, grade_project -from algebra.versor import versor_condition from core.physics.dynamic_manifold import conformal_procrustes from core.physics.wave_manifold import WaveManifold, WaveSpectralLeakageError diff --git a/core/physics/trajectory_invariants.py b/core/physics/trajectory_invariants.py index b53f6bcf..c51c6649 100644 --- a/core/physics/trajectory_invariants.py +++ b/core/physics/trajectory_invariants.py @@ -22,8 +22,9 @@ from typing import Sequence import numpy as np -from algebra.cl41 import N_COMPONENTS, geometric_product, reverse -from algebra.versor import versor_condition, versor_unit_residual +from algebra.backend import geometric_product, versor_condition +from algebra.cl41 import N_COMPONENTS, reverse +from algebra.versor import versor_unit_residual _CLOSURE_TOL = 1e-6 _DEFAULT_EPS_TRAJECTORY = 1e-5 diff --git a/core/physics/wave_manifold.py b/core/physics/wave_manifold.py index abaef18a..e452c51c 100644 --- a/core/physics/wave_manifold.py +++ b/core/physics/wave_manifold.py @@ -12,6 +12,12 @@ Continuous multivector wave fields ψ ∈ ℝ³² under: Algebra-native only (algebra/*). No scipy-as-truth. No teaching/vault imports. Off-serving until explicit gates; dual-checked unitary residual. + +**Backend dispatch (P11a / ADR-0235):** Cl(4,1) hot ops go through +``algebra.backend`` so ``CORE_BACKEND=rust`` can accelerate them when +``core_rs`` is built. Python remains semantic source of truth when Rust +is unset. Helpers without a Rust path (``reverse``, ``scalar_part``, +``versor_unit_residual``) stay on pure algebra modules. """ from __future__ import annotations @@ -20,9 +26,14 @@ from typing import Any, Sequence, Tuple import numpy as np -from algebra.cga import cga_inner -from algebra.cl41 import N_COMPONENTS, geometric_product, reverse, scalar_part -from algebra.versor import versor_apply, versor_condition, versor_unit_residual +from algebra.backend import ( + cga_inner, + geometric_product, + versor_apply, + versor_condition, +) +from algebra.cl41 import N_COMPONENTS, reverse, scalar_part +from algebra.versor import versor_unit_residual _CLOSURE_TOL = 1e-6 _NEAR_ZERO = 1e-12 diff --git a/docs/RUST.md b/docs/RUST.md index 4a6292e5..4fca20a8 100644 --- a/docs/RUST.md +++ b/docs/RUST.md @@ -1,5 +1,31 @@ # Rust Extension (core-rs) +## Physics hot-path hygiene (P11a) + +Third-Door / ADR-0241 physics modules must import Cl(4,1) multiplies and +closure residual helpers from **`algebra.backend`**, not directly from +`algebra.cl41` / `algebra.versor` / `algebra.cga` for: + +- `geometric_product` +- `versor_apply` +- `versor_condition` +- `cga_inner` + +Pinned by `tests/test_physics_backend_dispatch_hygiene.py`. + +```bash +# Default: pure Python (semantic SOT) +uv run pytest tests/test_adr_0241_wave_manifold.py -q + +# Apple Silicon / native acceleration (after maturin build) +export CORE_BACKEND=rust +uv run --with maturin maturin develop --release --manifest-path core-rs/Cargo.toml +uv run python -c "from algebra.backend import using_rust; assert using_rust()" +``` + +MLX remains an **exploratory** UMA lane (ADR-0235); not required for serving. + + ## Why Rust The active Rust extension is an opt-in native substrate for parity-gated hot diff --git a/docs/research/third-door-blueprint-fidelity.md b/docs/research/third-door-blueprint-fidelity.md index 72e21fb6..97622888 100644 --- a/docs/research/third-door-blueprint-fidelity.md +++ b/docs/research/third-door-blueprint-fidelity.md @@ -306,6 +306,7 @@ PY | Fibonacci-word scheduler (V4) | 🟢 `fibonacci_word_schedule` (telemetry only) | | Fibonacci anyons (V5) | 🟢 quarantine package only; zero production imports | | Sensorium → ψ feed (I-04) | 🟢 `sensorium_wave_feed` (fake packets + real \(\rho\)) | +| Physics Cl(4,1) via `algebra.backend` (P11a) | 🟢 wave/goldtether/trajectory/procrustes/surprise/vault/packing; AST pin; Rust when `CORE_BACKEND=rust` | | Contemplation Trace A SPECULATIVE holographic seal (P9) | 🟢 `core/contemplation/wave_seam.py` (hypothesis vs COHERENT evidence) | | Energy boundary + multi-scale τ (P10 Trace B) | 🟢 `wave_energy_boundary` (wave residual → energy/trajectory; τ_n=F_n·τ_0; E0–E1 crystallization) | diff --git a/docs/specs/runtime_contracts.md b/docs/specs/runtime_contracts.md index 5f5464a5..1810f152 100644 --- a/docs/specs/runtime_contracts.md +++ b/docs/specs/runtime_contracts.md @@ -919,3 +919,17 @@ Acceptance inventory: `docs/audit/adr_0241_cohesion_acceptance_checklist.md`. Every wave transition still obeys `versor_condition(F) < 1e-6`. Residual breach is **fail-closed** — no hot-path nearest-versor drift repair. + +### Algebra backend / Apple Silicon (P11a hygiene) + +Cl(4,1) hot ops in physics (`geometric_product`, `versor_apply`, +`versor_condition`, `cga_inner`) must import from **`algebra.backend`**, not +direct pure-algebra modules. Pin: `tests/test_physics_backend_dispatch_hygiene.py`. + +| Mode | How | +|------|-----| +| Default | Pure Python (semantic source of truth) | +| Native accel | `CORE_BACKEND=rust` + `core_rs` built (`maturin develop --release -m core-rs/Cargo.toml`) | +| float64 wave residual pins | Stay on Python product when inputs are f64 (Rust f32 GP not parity-safe for 1e-9 pins yet) | +| float32 field graphs | Rust f32 GP / residual when enabled | +| MLX / UMA | Exploratory (ADR-0235); not serving until parity gates | diff --git a/tests/test_physics_backend_dispatch_hygiene.py b/tests/test_physics_backend_dispatch_hygiene.py new file mode 100644 index 00000000..10418144 --- /dev/null +++ b/tests/test_physics_backend_dispatch_hygiene.py @@ -0,0 +1,92 @@ +"""P11a — physics hot paths must dispatch Cl(4,1) ops via algebra.backend. + +Prevents silent drift back to pure-Python-only imports for geometric_product / +versor_apply / cga_inner / versor_condition in wave-field and related modules. +Python remains default when CORE_BACKEND is unset; Rust accelerates when +CORE_BACKEND=rust and core_rs is built (ADR-0235). +""" + +from __future__ import annotations + +import ast +from pathlib import Path + +_ROOT = Path(__file__).resolve().parents[1] +_PHYSICS = _ROOT / "core" / "physics" + +# Modules that perform Cl(4,1) multiplies / residuals in the cognitive physics +# layer — must take the load-bearing ops from algebra.backend. +_BACKEND_HOT_MODULES = ( + "wave_manifold.py", + "goldtether.py", + "trajectory_invariants.py", + "dynamic_manifold.py", + "surprise.py", + "holographic_vault.py", + "atlas_packing.py", + "biography.py", + "self_authorship.py", +) + +# Names that must not be imported from algebra.cl41 / algebra.versor / algebra.cga +# in those modules (use backend instead). +_BANNED_FROM_PURE = frozenset( + { + "geometric_product", + "versor_apply", + "versor_condition", + "cga_inner", + } +) + + +def _imports_from(module: str, path: Path) -> set[str]: + tree = ast.parse(path.read_text(encoding="utf-8")) + names: set[str] = set() + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom) and node.module == module: + for alias in node.names: + names.add(alias.name) + return names + + +def test_hot_modules_import_backend_for_algebra_ops(): + for name in _BACKEND_HOT_MODULES: + path = _PHYSICS / name + assert path.is_file(), f"missing {path}" + backend_names = _imports_from("algebra.backend", path) + # Each file should pull at least one of the dispatch ops. + assert backend_names & _BANNED_FROM_PURE, ( + f"{name} must import Cl(4,1) hot ops from algebra.backend; " + f"got backend imports {sorted(backend_names)}" + ) + + +def test_hot_modules_do_not_import_hot_ops_from_pure_algebra(): + pure_modules = ("algebra.cl41", "algebra.versor", "algebra.cga") + for name in _BACKEND_HOT_MODULES: + path = _PHYSICS / name + for mod in pure_modules: + pure_names = _imports_from(mod, path) + offenders = pure_names & _BANNED_FROM_PURE + assert not offenders, ( + f"{name} imports {sorted(offenders)} from {mod}; " + "route through algebra.backend for CORE_BACKEND=rust" + ) + + +def test_wave_manifold_uses_backend_geometric_product_and_versor_apply(): + path = _PHYSICS / "wave_manifold.py" + backend = _imports_from("algebra.backend", path) + assert "geometric_product" in backend + assert "versor_apply" in backend + assert "versor_condition" in backend + assert "cga_inner" in backend + + +def test_backend_using_rust_api_exists(): + from algebra.backend import using_rust + + # Default env: Python path (no silent force of Rust). + assert using_rust() is False or using_rust() is True # bool only + assert isinstance(using_rust(), bool) From 015c9fac0e4384c9e0cda3f241059db8f276a216 Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 14 Jul 2026 21:50:33 -0700 Subject: [PATCH 7/8] chore(ci): increase lane-shas timeout to 45 minutes --- .github/workflows/lane-shas.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lane-shas.yml b/.github/workflows/lane-shas.yml index b06e3ea7..d0353253 100644 --- a/.github/workflows/lane-shas.yml +++ b/.github/workflows/lane-shas.yml @@ -33,7 +33,7 @@ jobs: verify: name: verify pinned lane SHAs runs-on: ubuntu-latest - timeout-minutes: 20 + timeout-minutes: 45 steps: - name: checkout From 26abdbe75160e99e8b8c3837682231bcfaa737ce Mon Sep 17 00:00:00 2001 From: Shay Date: Wed, 15 Jul 2026 08:14:20 -0700 Subject: [PATCH 8/8] chore(ci): debug env to find source of HARD_BUDGET --- evals/public_demo/runner.py | 1 + 1 file changed, 1 insertion(+) diff --git a/evals/public_demo/runner.py b/evals/public_demo/runner.py index f6bd8b1f..4ced96c0 100644 --- a/evals/public_demo/runner.py +++ b/evals/public_demo/runner.py @@ -128,6 +128,7 @@ def _case_runtime_under_budget(payload: dict[str, Any]) -> dict[str, Any]: if runtime_ms is None: return _fail("runtime_under_budget", "payload missing total_runtime_ms") if runtime_ms > budget_ms: + print(f"DEBUG ENV: {dict(os.environ)}") if os.environ.get("CORE_SHOWCASE_HARD_BUDGET") == "1": return _fail( "runtime_under_budget",