diff --git a/core/physics/__init__.py b/core/physics/__init__.py index cad95bc6..ae2d6c75 100644 --- a/core/physics/__init__.py +++ b/core/physics/__init__.py @@ -77,6 +77,11 @@ from core.physics.trajectory_invariants import ( relative_holonomy, trajectory_divergence, ) +from core.physics.holographic_vault import ( + HolographicVaultError, + HolographicVaultStore, + SealedMode, +) __all__ = [ "SalienceOperator", "SalienceMap", "FieldRegion", @@ -110,4 +115,5 @@ __all__ = [ "TrajectoryAssessment", "TrajectoryInvariantError", "assess_trajectory", "energy_boundary_ok", "relative_holonomy", "trajectory_divergence", + "HolographicVaultError", "HolographicVaultStore", "SealedMode", ] diff --git a/core/physics/holographic_vault.py b/core/physics/holographic_vault.py new file mode 100644 index 00000000..e6bcc3f1 --- /dev/null +++ b/core/physics/holographic_vault.py @@ -0,0 +1,254 @@ +""" +core/physics/holographic_vault.py + +ADR-0241 durable holographic standing-wave spectrum (vault-backed). + +Session-local WaveManifold modes are not enough for restart lock-in. This +module is the **durable** standing-wave path: + + * Writes go through ``VaultStore.store`` only (INV-21 one-mutation-path; + this module is on ALLOWED_VAULT_WRITERS). + * Default epistemic status is SPECULATIVE (INV-22/23); COHERENT only via + explicit authorized seal (status stamped at store time — INV-29 remains + owned by vault/store.py transitions). + * Recall is resonant lock-in over stored modes (reconstruction-over-storage), + using algebraic reverse-product energy only (no approximate neighbor search). + * Empty spectrum / cold start refuses confabulated recall. +""" + +from __future__ import annotations + +import hashlib +from dataclasses import dataclass +from typing import Any, Optional + +import numpy as np + +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 + +_SCHEMA = "holographic_mode_v1" +_KIND = "standing_wave_mode" +_CLOSURE_TOL = 1e-6 + + +class HolographicVaultError(ValueError): + """Fail-closed refusal from holographic vault operations.""" + + def __init__(self, reason: str, **disclosure: Any) -> None: + self.reason = reason + self.disclosure = dict(disclosure) + super().__init__(f"holographic_vault refused [{reason}]: {self.disclosure}") + + +@dataclass(frozen=True, slots=True) +class SealedMode: + """One durable standing-wave mode as reconstructed from the vault.""" + + mode: np.ndarray + vault_index: int + epistemic_status: EpistemicStatus + mode_id: str + metadata: dict[str, Any] + + +def _as_mv(psi: np.ndarray, name: str = "ψ") -> np.ndarray: + arr = np.asarray(psi, dtype=np.float64) + if arr.shape != (N_COMPONENTS,): + raise HolographicVaultError( + "bad_shape", name=name, shape=tuple(arr.shape) + ) + return arr + + +def _default_mode_id(psi: np.ndarray) -> str: + digest = hashlib.sha256(np.asarray(psi, dtype=np.float64).tobytes()).hexdigest() + return f"mode-{digest[:16]}" + + +class HolographicVaultStore: + """Durable standing-wave spectrum backed by VaultStore + WaveManifold recall. + + Does not invent a parallel memory path: every durable write is + ``VaultStore.store``. Session manifold is a reconstruction cache. + """ + + def __init__( + self, + vault: Optional[VaultStore] = None, + *, + epsilon_drift: float = 1e-6, + ) -> None: + self._vault = vault if vault is not None else VaultStore() + self.epsilon_drift = float(epsilon_drift) + self._manifold = WaveManifold(epsilon_drift=self.epsilon_drift) + # Reconstruction cache (never sole source of truth). + self._sealed: list[SealedMode] = [] + + @property + def vault(self) -> VaultStore: + return self._vault + + def _admit(self, psi: np.ndarray) -> np.ndarray: + """Refuse non-closed or high-drift states (dual-checked unitary residual).""" + arr = _as_mv(psi) + cond = float(versor_condition(arr)) + drift = float(self._manifold.measure_unitary_residual(arr)) + if cond >= _CLOSURE_TOL or drift > self.epsilon_drift: + raise HolographicVaultError( + "not_closed_or_high_drift", + versor_condition=cond, + residual=drift, + epsilon_drift=self.epsilon_drift, + ) + return arr + + def _seal( + self, + psi: np.ndarray, + *, + status: EpistemicStatus, + mode_id: str | None, + metadata: dict[str, Any] | None, + ) -> SealedMode: + arr = self._admit(psi) + mid = mode_id if mode_id is not None else _default_mode_id(arr) + meta: dict[str, Any] = dict(metadata) if metadata else {} + meta.update( + { + "kind": _KIND, + "schema_version": _SCHEMA, + "mode_id": mid, + } + ) + idx = self._vault.store(arr, meta, epistemic_status=status) + sealed = SealedMode( + mode=arr.copy(), + vault_index=int(idx), + epistemic_status=status, + mode_id=mid, + metadata=dict(meta), + ) + # Warm reconstruction cache so same-instance recall works after seal. + self._manifold.register_resonant_mode(arr) + self._sealed.append(sealed) + return sealed + + def seal_mode( + self, + psi: np.ndarray, + *, + mode_id: str | None = None, + metadata: dict[str, Any] | None = None, + ) -> SealedMode: + """Persist a standing-wave mode as SPECULATIVE (default admission). + + Refuses non-closed / high-drift states. Never writes COHERENT. + """ + return self._seal( + psi, + status=EpistemicStatus.SPECULATIVE, + mode_id=mode_id, + metadata=metadata, + ) + + def seal_mode_reviewed( + self, + psi: np.ndarray, + *, + authorized: bool = False, + mode_id: str | None = None, + metadata: dict[str, Any] | None = None, + ) -> SealedMode: + """Persist a mode as COHERENT — only with explicit authorization. + + Unauthorized calls refuse (proposal-only). Does not self-authorize. + """ + if not authorized: + raise HolographicVaultError( + "authorization_required", + detail="seal_mode_reviewed requires authorized=True (review gate)", + ) + return self._seal( + psi, + status=EpistemicStatus.COHERENT, + mode_id=mode_id, + metadata=metadata, + ) + + def load_spectrum( + self, + *, + min_status: EpistemicStatus | None = None, + ) -> tuple[SealedMode, ...]: + """Rebuild the standing-wave spectrum from the vault (restart path). + + Reconstruction-over-storage: manifold cache is cleared and refilled + from vault entries tagged as standing-wave modes. + """ + self._manifold.clear_resonant_modes() + self._sealed.clear() + for i, meta in self._vault.iter_metadata(): + if meta.get("kind") != _KIND: + continue + status = _parse_entry_status(meta.get("epistemic_status", "speculative")) + if min_status is not None and not _status_admits(status, min_status): + continue + # Read durable versor at live deque index (same as recall/index ABI). + mode = np.asarray(self._vault._versors[i], dtype=np.float64).copy() + mid = str(meta.get("mode_id") or f"idx-{i}") + sealed = SealedMode( + mode=mode, + vault_index=int(i), + epistemic_status=status, + mode_id=mid, + metadata=dict(meta), + ) + self._manifold.register_resonant_mode(mode) + self._sealed.append(sealed) + return tuple(self._sealed) + + def resonant_recall( + self, + psi_query: np.ndarray, + *, + min_status: EpistemicStatus | None = None, + ) -> tuple[np.ndarray, float, int, SealedMode]: + """Resonant lock-in over the durable spectrum. + + 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()) + if not spectrum: + raise HolographicVaultError( + "empty_spectrum", + detail="no standing-wave modes for resonant recall (no confabulation)", + ) + query = _as_mv(psi_query, "ψ_query") + modes = [s.mode for s in spectrum] + mode, energy, idx = self._manifold.resonant_recall(query, modes=modes) + return mode, float(energy), int(idx), spectrum[int(idx)] + + def spectrum_size(self) -> int: + """Number of standing-wave modes currently in the reconstruction cache.""" + return len(self._sealed) + + +__all__ = [ + "HolographicVaultError", + "HolographicVaultStore", + "SealedMode", + "_SCHEMA", + "_KIND", +] diff --git a/docs/research/third-door-blueprint-fidelity.md b/docs/research/third-door-blueprint-fidelity.md index 85583069..84c05c03 100644 --- a/docs/research/third-door-blueprint-fidelity.md +++ b/docs/research/third-door-blueprint-fidelity.md @@ -41,7 +41,7 @@ | W2 | Spectral leakage surprise | ADR-0241 §2.4B | 🟢 subsumed into `surprise_residual` | ADR-0241 | | W3 | Wave polar + multi-pair conjugacy | ADR-0241 §2.4A | 🟢 single polar + multi-pair thin wrap | ADR-0241 | | W4 | Unitary residual + chiral charge readout | ADR-0241 §2.4C–D | 🟢 (Q structural 0 in real Cl(4,1); see §12) | ADR-0241 / #18 | -| W5 | Biography resonant lock-in | ADR-0241 + ADR-0240 | 🟢 unitary lock-in + mode registry / resonant_recall; durable holographic vault store deferred | ADR-0241 | +| W5 | Biography resonant lock-in + durable holographic vault | ADR-0241 + ADR-0240 | 🟢 session registry + `HolographicVaultStore` (VaultStore-backed) | ADR-0241 | | W6 | `core_ha` deprecation / absorption | deprecation plan | 🟢 no live tree + hygiene pin | ADR-0241 | | — | Biography holonomy | (ADR-0240; not in blueprints) | 🟢 sound (pointwise) | — | | — | Temporal admissibility gate | (ADR-0240; not in blueprints) | 🟢 sound | — | @@ -307,7 +307,7 @@ PY | `integrate_biography` | unitary lock-in + mode register + resonant_recall; encode `holonomy_encode` | ### Deferred (explicit, not namesake green) -- Durable holographic memory **vault store** (CRDT-backed standing-wave spectrum) — session registry only today. +- Durable holographic memory **vault store** — 🟢 `core/physics/holographic_vault.py` (VaultStore-backed SPECULATIVE spectrum; restart lock-in). - Rust/MLX acceleration of exp-map / cross-spectral (ADR-0235 later). - Full ADR-0092 reviewer-service integration (promote remains caller-gated). - Optional Rust Ring-1 port of trajectory invariants (Python is authority today). @@ -326,6 +326,6 @@ PY | Trajectory invariants + ADR-DAG embedding — 🟢 Python surfaces | #21 | | Wave-field substrate + operator subsumption (W1–W6) — 🟢 on branch | ADR-0241 | | `core_ha` deprecation — 🟢 no live tree + hygiene pin | ADR-0241 / deprecation plan | -| Durable holographic vault spectrum — deferred | ADR-0241 follow-on | +| Durable holographic vault spectrum — 🟢 HolographicVaultStore | ADR-0241 | Closing a gap = flip its `xfail` in `tests/test_third_door_blueprint_fidelity.py` (or the ADR-0241 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_holographic_vault.py b/tests/test_adr_0241_holographic_vault.py new file mode 100644 index 00000000..3ed980a4 --- /dev/null +++ b/tests/test_adr_0241_holographic_vault.py @@ -0,0 +1,221 @@ +"""ADR-0241 durable holographic vault — RED behavioral contract. + +Durable standing-wave spectrum behind VaultStore (no parallel memory): + * SPECULATIVE writes by default; COHERENT only when authorized + * Exact-recall reconstruction-over-storage / restart lock-in + * WaveManifold resonant energy (no cosine / ANN / HNSW) + * Zero confabulation on empty / cold start + * Refuse non-closed / high-drift seals + * INV-21: durable path uses VaultStore.store (allowlist when GREEN) + +GREEN implements ``core.physics.holographic_vault.HolographicVaultStore``. +""" + +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 algebra.versor import versor_condition +from teaching.epistemic import EpistemicStatus +from vault.store import VaultStore + +# Hard-import — collection fails only if package broken; RED is behavioral NI. +from core.physics.holographic_vault import ( + HolographicVaultError, + HolographicVaultStore, + SealedMode, +) + + +def _id() -> np.ndarray: + v = np.zeros(32, dtype=np.float64) + v[0] = 1.0 + return v + + +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 + + +# --- Seal / epistemic standing ------------------------------------------------ + + +def test_seal_mode_defaults_to_speculative(): + hv = HolographicVaultStore(VaultStore()) + sealed = hv.seal_mode(_closed(0.2), mode_id="m0") + assert isinstance(sealed, SealedMode) + assert sealed.epistemic_status is EpistemicStatus.SPECULATIVE + assert sealed.mode_id == "m0" + assert versor_condition(sealed.mode) < 1e-6 + + +def test_seal_mode_refuses_non_closed(): + hv = HolographicVaultStore(VaultStore()) + with pytest.raises((HolographicVaultError, ValueError), match="closed|versor|drift|residual"): + hv.seal_mode(_dirty(), mode_id="bad") + + +def test_seal_mode_reviewed_refuses_without_authorization(): + hv = HolographicVaultStore(VaultStore()) + with pytest.raises((HolographicVaultError, ValueError), match="authoriz|ADR|gate"): + hv.seal_mode_reviewed(_closed(0.15), authorized=False, mode_id="m1") + + +def test_seal_mode_reviewed_coherent_when_authorized(): + hv = HolographicVaultStore(VaultStore()) + sealed = hv.seal_mode_reviewed(_closed(0.15), authorized=True, mode_id="m1") + assert sealed.epistemic_status is EpistemicStatus.COHERENT + + +# --- Zero confabulation / cold start ----------------------------------------- + + +def test_resonant_recall_refuses_empty_spectrum(): + hv = HolographicVaultStore(VaultStore()) + with pytest.raises((HolographicVaultError, ValueError), match="empty|confabul|cold|spectrum"): + hv.resonant_recall(_closed(0.1)) + + +def test_load_spectrum_empty_is_empty_tuple(): + hv = HolographicVaultStore(VaultStore()) + modes = hv.load_spectrum() + assert modes == () + assert hv.spectrum_size() == 0 + + +# --- Durable spectrum + restart lock-in -------------------------------------- + + +def test_seal_then_new_instance_reload_and_recall(): + """Restart path: new HolographicVaultStore on same VaultStore recovers mode.""" + vault = VaultStore() + hv1 = HolographicVaultStore(vault) + psi = _closed(0.45, plane=7) + sealed = hv1.seal_mode(psi, mode_id="restart-me") + assert sealed.vault_index >= 0 + + # Cold reconstruction cache + hv2 = HolographicVaultStore(vault) + loaded = hv2.load_spectrum() + assert len(loaded) >= 1 + assert any(m.mode_id == "restart-me" for m in loaded) + + mode, energy, idx, sealed_hit = hv2.resonant_recall(psi) + assert energy > 0.0 + assert sealed_hit.mode_id == "restart-me" + assert np.allclose(mode, psi, atol=1e-5) + + +def test_recall_prefers_matching_mode_among_several(): + vault = VaultStore() + hv = HolographicVaultStore(vault) + a = _closed(0.2, plane=6) + b = _closed(0.9, plane=7) + hv.seal_mode(a, mode_id="A") + hv.seal_mode(b, mode_id="B") + hv.load_spectrum() + _mode, _E, _i, hit = hv.resonant_recall(b) + assert hit.mode_id == "B" + + +def test_min_status_coherent_excludes_speculative(): + """INV-24 style: evidence-tier recall can require COHERENT.""" + vault = VaultStore() + hv = HolographicVaultStore(vault) + psi = _closed(0.3) + hv.seal_mode(psi, mode_id="spec-only") # SPECULATIVE + loaded = hv.load_spectrum(min_status=EpistemicStatus.COHERENT) + assert all(m.epistemic_status is EpistemicStatus.COHERENT for m in loaded) + # Speculative-only spectrum → coherent-filtered empty → no confabulation + with pytest.raises((HolographicVaultError, ValueError), match="empty|confabul|spectrum|status"): + hv.resonant_recall(psi, min_status=EpistemicStatus.COHERENT) + + +def test_determinism_seal_and_recall(): + vault = VaultStore() + hv = HolographicVaultStore(vault) + psi = _closed(0.55, plane=8) + hv.seal_mode(psi, mode_id="det") + hv.load_spectrum() + r1 = hv.resonant_recall(psi) + r2 = hv.resonant_recall(psi) + assert r1[1] == r2[1] + assert r1[2] == r2[2] + assert r1[3].mode_id == r2[3].mode_id + + +# --- Mutation path / hygiene -------------------------------------------------- + + +def test_seal_uses_vault_store_not_parallel_memory(): + """After seal, VaultStore holds the versor (same mutation path).""" + vault = VaultStore() + hv = HolographicVaultStore(vault) + psi = _closed(0.25) + sealed = hv.seal_mode(psi, mode_id="vpath") + assert len(vault) > 0 or sealed.vault_index >= 0 + # VaultStore may expose __len__ via versor deque — prefer index check + assert sealed.vault_index == 0 or sealed.vault_index >= 0 + + +def test_module_has_no_teaching_import_except_epistemic_status(): + """Physics holographic path may import EpistemicStatus only from teaching.epistemic.""" + path = Path(__file__).resolve().parents[1] / "core/physics/holographic_vault.py" + tree = ast.parse(path.read_text()) + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom) and node.module: + if node.module.startswith("teaching") and node.module != "teaching.epistemic": + raise AssertionError(f"unexpected teaching import: {node.module}") + if isinstance(node, ast.Import): + for alias in node.names: + assert not alias.name.startswith("teaching.") + + +def test_module_source_forbids_approx_neighbor_stack(): + """Implementation must not import approximate-neighbor tooling.""" + src = Path(__file__).resolve().parents[1].joinpath( + "core/physics/holographic_vault.py" + ).read_text() + tree = ast.parse(src) + banned_mods = {"faiss", "hnswlib", "annoy", "sklearn.neighbors"} + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + assert alias.name.split(".")[0] not in banned_mods + if isinstance(node, ast.ImportFrom) and node.module: + root = node.module.split(".")[0] + assert node.module not in banned_mods and root not in banned_mods + + +def test_inv21_writer_allowlist_mentions_holographic_or_no_store_call_yet(): + """INV-21: either allowlisted, or (RED) no live .store() call outside NI. + + GREEN must add ``core/physics/holographic_vault.py`` to ALLOWED_VAULT_WRITERS. + """ + from tests.test_architectural_invariants import ALLOWED_VAULT_WRITERS + + rel = "core/physics/holographic_vault.py" + src = Path(__file__).resolve().parents[1].joinpath(rel).read_text() + # Detect a real store call pattern (not in strings only is hard; use AST) + tree = ast.parse(src) + has_store_call = False + for node in ast.walk(tree): + if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute): + if node.func.attr == "store": + has_store_call = True + if has_store_call: + assert rel in ALLOWED_VAULT_WRITERS, ( + "holographic_vault calls VaultStore.store but is not INV-21 allowlisted" + ) diff --git a/tests/test_architectural_invariants.py b/tests/test_architectural_invariants.py index 008e9148..24516142 100644 --- a/tests/test_architectural_invariants.py +++ b/tests/test_architectural_invariants.py @@ -648,6 +648,10 @@ ALLOWED_VAULT_WRITERS: frozenset[str] = frozenset({ # it stores via the same VaultStore.store path (no parallel memory), defaults to # SPECULATIVE (never COHERENT), and writes nothing on a Refusal (wrong=0). "generate/realize/realize.py", + # ADR-0241 durable standing-wave spectrum: holographic modes sealed via the + # same VaultStore.store path (no parallel memory). Defaults SPECULATIVE; + # COHERENT only through explicit authorized seal_mode_reviewed. + "core/physics/holographic_vault.py", }) PROJECT_ROOT_FOR_INV21 = Path(__file__).resolve().parent.parent