From 904380e96ad0e723880e8a51f632980510d3816a Mon Sep 17 00:00:00 2001 From: Shay Date: Thu, 16 Jul 2026 21:53:20 -0700 Subject: [PATCH 1/2] docs(adr-0243): commit ADR-0243 + implementation plan, pin sketch defects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commits the ADR-0243 (Wave-Field Cognitive Lifecycle) proposal verbatim as Proposed, and the arc-governing implementation plan derived from it plus docs/analysis/core_cohesion_master_plan.md and the ADR-0241/0242 post-Accept close audit findings. The plan records that most of ADR-0243's organs already exist on main (sensorium feed, WaveManifold, energy classes, GoldTether, surprise/discovery plumbing, self-authorship, biography, packs/en readback) and that tests/test_third_door_cohesion.py already pins I-01..I-05, A-02/A-04, and core_ha absence from the cohesion master plan — so this arc is the §2.2 relaxation reasoner plus three wiring lanes, not a rebuild. Also pins three defects in the ADR's §3 reference prototype so it can never be ported verbatim: the egress gate's antisymmetric quadratic form is identically zero (rejects every state, valid or not); the unitary relaxation loop cannot dissipate toward a ground state (the honest relaxer is the imaginary-time semigroup); and the prototype's block-matrix "pseudoscalar proxy" is a different operator from the real Cl(4,1) pseudoscalar action despite sharing I^2 = -Id. [Verification]: uv run core test --suite smoke -q -> 176 passed (133.41s); uv run python -m pytest tests/test_adr_0243_sketch_defect_pins.py tests/test_third_door_cohesion.py -q -> 19 passed (0.93s) --- .gitignore | 4 + ...hension-reasoning-and-resonant-learning.md | 309 ++++++++++++++++++ .../adr-0241-0242-mastery-close-status.md | 2 +- docs/plans/adr-0243-implementation-plan.md | 158 +++++++++ tests/test_adr_0243_sketch_defect_pins.py | 140 ++++++++ 5 files changed, 612 insertions(+), 1 deletion(-) create mode 100644 docs/adr/ADR-0243-wave-field-cognitive-lifecycle-comprehension-reasoning-and-resonant-learning.md create mode 100644 docs/plans/adr-0243-implementation-plan.md create mode 100644 tests/test_adr_0243_sketch_defect_pins.py diff --git a/.gitignore b/.gitignore index 7331f4a8..53c8c3c8 100644 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,7 @@ engine_state/_life_backup_*/ # Local MCP configuration (contains local tokens) .mcp.json + +# Google Drive sync pointer stubs (JSON pointers to Drive docs, not content). +# The canonical committed copy of any R&D doc lives under docs/ as real markdown. +*.gdoc diff --git a/docs/adr/ADR-0243-wave-field-cognitive-lifecycle-comprehension-reasoning-and-resonant-learning.md b/docs/adr/ADR-0243-wave-field-cognitive-lifecycle-comprehension-reasoning-and-resonant-learning.md new file mode 100644 index 00000000..b21967dd --- /dev/null +++ b/docs/adr/ADR-0243-wave-field-cognitive-lifecycle-comprehension-reasoning-and-resonant-learning.md @@ -0,0 +1,309 @@ +# ADR-0243: Wave-Field Cognitive Lifecycle — Comprehension, Resonant Reasoning, and Lifelong Learning + +**Status**: Proposed (acceptance path: benchmark evidence \+ Joshua review) +**Date**: 2026-07-14 +**Deciders**: Joshua Shay \+ multi-model R\&D +**Traceability**: Notion R\&D (Engineering Reference Vault Interconnection: `core_HA` Patterns) +**Related**: ADR-0003, ADR-0006, ADR-0238, ADR-0239, ADR-0240, ADR-0241, ADR-0242, `core/physics/wave_manifold.py` +**Canonical path**: `docs/adr/` + +--- + +## 1\. Context and Problem Statement + +With the successful unification of the **$Cl(4,1)$ Conformal Wave-Field ($\\psi$)** substrate ([ADR-0241](https://drive.google.com/file/d/1F_7QYtPysBP4qMbLGlGPnXgYx9IXug8nUYrpiCGSunE/view?usp=drivesdk)) and the implementation of the **Deterministic Fibonacci search** ([ADR-0242](https://drive.google.com/file/d/15_NECCPy-tEWGfYi_BNqawm8GytUTMkz1DsOqGVMXhI/view?usp=drivesdk)), CORE's physical layer has reached structural maturity. + +However, we must now define how these new physical and geometric evolutions are leveraged to solve the fundamental cognitive tasks where traditional architectures struggle: + +- **Comprehension & Ingress**: Traditional architectures parse and embed inputs into flat, context-dry vectors, leading to representation drift, attention decay over long contexts, and loss of structural relations. +- **Problem Solving & Reasoning**: Traditional systems treat reasoning as probabilistic path-search or auto-regressive step generation. This lacks mathematical guarantees of correctness and suffers from cumulative error propagation. +- **Egress & Generation**: Probabilistic autoregressive decoding selects discrete tokens one-by-one via softmax sampling, which has no global coherence guarantees, leading to hallucinations and semantic drift. +- **Contemplation & Learning**: Standard models require gradient-descent backpropagation to update static weights, which is computationally expensive, non-reconstructible, and prone to catastrophic forgetting. + +This ADR defines the complete **Wave-Field Cognitive Lifecycle**, leveraging the wave function to establish a fully deterministic, closed-loop, physical-relaxation-based paradigm for comprehension, reasoning, generation, and learning. + +--- + +## 2\. Decision and Architectural Formulation + +We dissolve the probabilistic, token-by-token paradigm of classical AI. We establish that **cognition is the continuous physical evolution, resonance, and relaxation of a Conformal Wave-Field ($\\psi$) across a single $Cl(4,1)$ geometric substrate**. + + \[INGRESS\] \[REASONING\] \[EGRESS\] + + Continuous Modalities Hamiltonian Well (H\_p) Thermodynamic State + + | | | + + v (Superposition) v (Physical Relaxation) v (Energy Class check) + + Ingress Wave (psi\_in) \=======\> Steady State (psi\_final) \=======\> Linguistic Readback + + ^ ^ | + + | (Resonant recall) | (Unitary update) v (GoldTether Gate) + + Standing-Wave Atlas \<===================+===========================\> safe, aligned output + + | + + v (Verified holonomy R) + + Biography update (R\_bio) + +--- + +### 2.1 Ingress and Reading Comprehension: Wave Ingestion and Holomorphic Dispersion + +Reading comprehension is modeled as **Wave-Packet Ingestion and Holomorphic Dispersion**, replacing flat token embeddings. + +1. **Ingress Wave Packet**: An incoming text block, symbolic formula, or multimodal sensory stream is compiled into a localized, coherent wave packet $\\psi\_{ ext{context}}(X)$. This compilation preserves spatial-temporal phase relationships: $$\\psi\_{ ext{context}}(X) \= \\sum\_i c\_i \\psi\_{ ext{token}\_i}(X)$$ +2. **Holomorphic Dispersion**: As $\\psi\_{ ext{context}}$ is injected, it propagates through the `VocabManifold`. Proximity and meaning are not calculated via nearest-neighbor vector scans. Instead, the wave disperses and performs parallel cross-correlation with the registered standing-wave modes ${\\psi\_k}$ of the Hyperbolic Atlas, generating a spectrum of resonant coefficients: $$R\_k \= \\int\_M \\langle \\psi\_{ ext{context}}(X) \\widetilde{\\psi}\_k(X) angle\_0 dX$$ This represents the instant, parallel projection of the input context onto the entire known semantic manifold. + +--- + +### 2.2 Reasoning and Problem Solving: Hamiltonian Well Relaxation + +Problem-solving is re-engineered as a **Physical Wave-Field Relaxation Process**, replacing probabilistic step-by-step tree search. + +1. **The Problem Hamiltonian**: The constraints and boundary conditions of a given problem (e.g. mathematical equalities, safety rules, or logical premises) are formulated as potential energy barriers or wells in a problem-specific Hamiltonian operator $\\mathcal{H}\_{ ext{problem}}$. +2. **Relaxation to Eigenstates**: The ingress wave field $\\psi\_{ ext{context}}(X)$ is set as the initial state $\\psi(X, 0)$. The system is allowed to evolve under the Algebraic Schrödinger Equation: $$\\partial\_t \\psi \= \\mathcal{H}*{ ext{problem}}(\\psi) I$$ Through this evolution, the wave field naturally disperses away from high-potential barriers (representing logical contradictions or safety violations) and settles (relaxes) into the lowest-energy, stable standing-wave eigenmodes of the problem manifold: $$\\psi*{ ext{steady}}(X) \= \\lim\_{t o \\infty} \\exp\\left( \\mathcal{H}*{ ext{problem}} I t ight) \\psi*{ ext{context}}(X)$$ The resulting steady-state wave $\\psi\_{ ext{steady}}(X)$ represents the exact, geometrically congruent solution to the problem. It is mathematically guaranteed to satisfy all boundary conditions with zero room for intermediate fabrication. + +--- + +### 2.3 Egress and Generative Articulation: Thermodynamic Wave Readback + +We replace probabilistic softmax token generation with **Thermodynamic Wave Readback**, providing ironclad coherence guarantees. + +1. **Thermodynamic Energy Classes**: The Field Energy Operator ($H$, defined in [ADR-0006](https://core-gitquarters.acbcontent.org/core-labs/core/src/branch/main/docs/adr/ADR-0006-field-energy-operator.md)) evaluates the "energy class" (E0 to E4) of the relaxed wave-field $\\psi\_{ ext{steady}}(X)$. + - **E0/E1 (Crystalline/Stable)**: Represents cold, settled knowledge. It is bypassed for generation and vaulted into the sharded Delta-CRDT registers. + - **E3/E4 (Hot/Critical)**: Indicates a high-activation, settled state that carries maximum semantic charge and "wants" to be articulated. +2. **Linguistic Readback**: For E3/E4 states, the system invokes the readback rules of the active language pack (`en/readback_rules.py`, `he/readback_rules.py`, `el/readback_rules.py`). These rules map the geometric components—the principal bivector directions and scale-invariant parameters of the wave field—directly to symbolic tokens or motor commands. +3. **GoldTether Gate**: Before any token or continuous action is permitted to exit the boundary, the **GoldTether unit residual** is evaluated: $$R\_{ ext{GoldTether}} \= \\sup\_{X \\in M} \\left| \\psi\_{ ext{steady}}(X) \\widetilde{\\psi}\_{ ext{steady}}(X) \- 1 ight|\_F \< 10^{-6}$$ If the generated state would introduce non-unitary drift (hallucination or ungrounded statements), the gate closes instantly, blocking the output and prompting a pre-ratified, safe fallback. + +--- + +### 2.4 Speculative Contemplation and Non-Resonant Curiosity + +Active thinking, self-reflection, and learning are modeled as **Speculative Contemplation and Non-Resonant Curiosity**, replacing classical gradient-descent backpropagation. + +1. **Speculative Generation**: During idle cycles, the contemplation loop (`core/contemplation/runner.py`) speculatively generates wave-packets $\\psi\_{ ext{speculative}}(X)$ representing potential hypotheses or analogical transfers. +2. **Orthogonal Surprise Check**: The non-resonant surprise residual of the speculative wave is evaluated: $$\\mathcal{S}(\\psi) \= \\psi\_{ ext{speculative}} \- \\mathcal{P}*{ ext{resonance}}(\\psi*{ ext{speculative}})$$ + - **Low Surprise**: The hypothesis is fully explained by the existing resonant schema. It is integrated immediately with no learning required. + - **High Surprise (Discovery Signal)**: If $E\_{ ext{surprise}} \> \\gamma$, the speculative wave contains structural novelty. This signal is held as a `DiscoveryCandidate` and routed to the offline review corridor. It does *not* alter active knowledge but directs the self-authorship loop (`core/physics/self_authorship.py`) to generate a proposal to expand the active Hamiltonian $\\mathcal{H}$, enabling structured learning without catastrophic forgetting. + +--- + +### 2.5 Lifelong Resonant Learning: Biography Holonomy Update + +CORE-native learning is the permanent record of the entity's lived experiences as a sequence of geometric transformations. + +Once a sequence of reasoning and action steps is validated (via the validation harness, [ADR-0240](https://drive.google.com/file/d/1eFNoXQl5BbUo6g4GBzRXi5tyIhT5RUGZQG6afaVXTg4/view?usp=drivesdk)), the exact unitary transformation $R \\in Spin(4,1)$ undergone by the wave-field is compiled into the **Biography Holonomy Blade** (`biography.py`): $$\\mathcal{H}*{ ext{bio}} \\leftarrow \\mathcal{H}*{ ext{bio}} \\cdot R$$ This is the ultimate, non-lossy, reconstruction-over-storage compilation of experience. It represents the "wisdom" of the entity, which can be replayed and audited byte-for-byte\! + +--- + +## 3\. Implementation Specification (The Cognitive Relaxation Loop) + +Below is the Python prototype implementing wave-field ingestion, Hamiltonian well relaxation (problem-solving), and the GoldTether egress gate inside the active reasoning pipeline. + +\# core/physics/cognitive\_lifecycle.py + +from \_\_future\_\_ import annotations + +import numpy as np + +import scipy.linalg as la + +from dataclasses import dataclass + +from typing import Callable, Tuple + +N\_COMPONENTS \= 32 + +@dataclass(frozen=True, slots=True) + +class IngressWavePacket: + + psi: np.ndarray \# 32-vector coefficients + + domain\_id: str + +@dataclass(frozen=True, slots=True) + +class EgressVerdict: + + admitted: bool + + wave\_out: np.ndarray + + residual: float + + message: str + +class CognitiveLifecycleEngine: + + def \_\_init\_\_(self, epsilon\_drift: float \= 1e-6): + + self.epsilon\_drift \= epsilon\_drift + + self.I \= np.zeros((N\_COMPONENTS, N\_COMPONENTS)) + + \# Central pseudoscalar proxy + + for i in range(N\_COMPONENTS // 2): + + self.I\[2\*i, 2\*i+1\] \= 1.0 + + self.I\[2\*i+1, 2\*i\] \= \-1.0 + + def ingest\_context(self, tokens: list\[np.ndarray\], domain\_id: str) \-\> IngressWavePacket: + + """ + + Compiles discrete symbolic token wave-packets into a superposed, + + coherent IngressWavePacket. + + """ + + psi\_sum \= np.zeros(N\_COMPONENTS, dtype=np.float64) + + for t in tokens: + + arr \= np.asarray(t, dtype=np.float64) + + psi\_sum \+= arr + + \# Normalize to preserve unitary probability amplitude + + norm \= np.linalg.norm(psi\_sum) + + psi\_norm \= (psi\_sum / norm) if norm \> 1e-12 else psi\_sum + + return IngressWavePacket(psi=psi\_norm, domain\_id=domain\_id) + + def solve\_via\_relaxation( + + self, + + ingress: IngressWavePacket, + + H\_problem: np.ndarray, + + relaxation\_steps: int \= 100, + + dt: float \= 0.01 + + ) \-\> np.ndarray: + + """ + + Solves a problem via continuous wave-field relaxation. + + The wave relaxes into the minimum-energy eigenstate of H\_problem. + + """ + + psi \= ingress.psi.copy() + + \# Schrödinger propagator: R \= exp(H\_problem \* I \* dt) + + generator \= np.dot(H\_problem, self.I) + + R \= la.expm(generator \* dt) + + + + \# Relaxation loop (Euler/exponential integrator) + + for \_ in range(relaxation\_steps): + + psi \= np.dot(R, psi) + + \# Enforce the null-cone amplitude normalization step + + norm \= np.linalg.norm(psi) + + if norm \> 1e-12: + + psi /= norm + + + + return psi + + def egress\_gate(self, psi\_steady: np.ndarray) \-\> EgressVerdict: + + """ + + Unitary GoldTether egress gate: blocks non-unitary/hallucinated + + wave-states from entering the readback and serving paths. + + """ + + psi\_arr \= np.asarray(psi\_steady, dtype=np.float64) + + \# Unitary residual check: || psi \* rev(psi) \- 1 ||\_F + + \# rev(psi) proxied via conjugate transpose under I-metric + + psi\_rev \= np.dot(self.I.T, psi\_arr) + + norm\_product \= np.dot(psi\_arr.T, psi\_rev) + + drift \= np.abs(norm\_product \- 1.0) + + + + if drift \> self.epsilon\_drift: + + return EgressVerdict( + + admitted=False, + + wave\_out=np.zeros\_like(psi\_arr), + + residual=float(drift), + + message="REJECTED: Unitary propagator drift exceeds epsilon\_drift limit (ungrounded state)." + + ) + + + + return EgressVerdict( + + admitted=True, + + wave\_out=psi\_arr, + + residual=float(drift), + + message="ADMITTED: Wave-field verified and promoted to readback path." + + ) + +--- + +## 4\. Consequences and Gating Rules + +### 4.1 Benefits + +- **Autoregressive Hallucination Eliminated**: By replacing step-by-step probabilistic token sampling with physical wave relaxation, output generation is strictly constrained by the geometry of the problem Hamiltonian. +- **Zero Coordinate Loss**: Resonant standing-wave lock-in ensures that recalled memories are mathematically exact, preventing the fuzzy centroid degradation of legacy architectures. +- **Topologically Protected Wisdom**: Experience is compiled directly into the Biography Holonomy Blade as unitary rotor products, providing an untamperable, replayable audit trail of lifelong learning. + +### 4.2 Gating Rules + +- **No Direct Hot-Path Promotion**: Reconstructed wave-fields or proposed Hamiltonian adjustments from the self-authorship loop (`core/physics/self_authorship.py`) must never bypass the one-mutation-path. Speculative changes must reside strictly within the `evals/` and `calibration/` quarantine zones until ratified by a signed human certificate. + +--- + +## 5\. References + +1. `docs/adr/ADR-0003-coordinate-system-dissolution.md` — Relational fields replacing coordinate frames. +2. `docs/adr/ADR-0238-GoldTether-Modulated-Supervised-Autonomy.md` — GoldTether residual monitoring. +3. `docs/adr/ADR-0239-Conformal-Procrustes-Surprise-Dual-Operator.md` — Conformal Procrustes and surprise. +4. `docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md` — Continuous wave-field framework. +5. `docs/adr/ADR-0242-deterministic-fibonacci-operators-and-evidence-gated-optimization.md` — Fibonacci search contract. + diff --git a/docs/plans/adr-0241-0242-mastery-close-status.md b/docs/plans/adr-0241-0242-mastery-close-status.md index 00dad05a..959f84a7 100644 --- a/docs/plans/adr-0241-0242-mastery-close-status.md +++ b/docs/plans/adr-0241-0242-mastery-close-status.md @@ -78,7 +78,7 @@ GitHub Actions became billing-locked (~$6, account-level) mid-arc, forcing a piv ## Current state (2026-07-16) - Both ADRs: **Accepted**, ratified, full audit trail. -- `main` was @ `18d3c70d` at arc close; post-Accept backlog follow-through is on `feat/adr-0241-0242-implementation` (tip includes `d388fc9e` feature commit + status notes; local-first validation; merge when ready). +- Post-Accept backlog **merged**: PR #55 (`d388fc9e` feature commit + status notes) landed on `main` @ `19819194`; all `[Verification]` numbers were independently reproduced by the 2026-07-16 audit (smoke 176; fast lane 11709 passed / 108 skipped; f64 parity 21/21 incl. Rust-path; invariants+demos+A-04 140). Three seam-vs-integration caveats carried into the ADR-0243 arc — see `docs/plans/adr-0243-implementation-plan.md` §1. - CI: **local-first is the real merge gate** (no Docker CI for merge); `uv.lock` committed and locked installs enforced in workflows; optional Mac Act runner must be `ubuntu-latest:host` (native), not `docker://…`. ## Remaining backlog (post-Accept follow-through; 2026-07-16 mastery close) diff --git a/docs/plans/adr-0243-implementation-plan.md b/docs/plans/adr-0243-implementation-plan.md new file mode 100644 index 00000000..77ae2f48 --- /dev/null +++ b/docs/plans/adr-0243-implementation-plan.md @@ -0,0 +1,158 @@ +# ADR-0243 Implementation Plan — Wave-Field Cognitive Lifecycle + +**Status**: Approved by Shay 2026-07-16 (plan-of-record for the 0243 arc) +**Authority docs**: `docs/adr/ADR-0243-wave-field-cognitive-lifecycle-comprehension-reasoning-and-resonant-learning.md` (Proposed), `docs/analysis/core_cohesion_master_plan.md` (canonical, doctrine-annotated) +**Related**: ADR-0006, ADR-0055, ADR-0145, ADR-0238, ADR-0239, ADR-0240, ADR-0241, ADR-0242 +**Prior-arc record**: `docs/plans/adr-0241-0242-mastery-close-status.md` + +--- + +## 1. Inputs carried from the ADR-0241/0242 close audit (2026-07-16) + +The independent audit of PR #55 confirmed the 0241/0242 close as genuine, with three +seam-vs-integration caveats that this arc must resolve or consciously inherit: + +1. **D9 "f64 GP parity" is a Python-SOT freeze, not cross-implementation parity.** + No Rust f64 geometric product exists. All f64 wave-field math stays in Python + until a `geometric_product_f64` PyO3 export is built *and* parity-gated. + Consequence here: the ADR-0243 relaxer is pure-Python f64 by doctrine, and the + master plan's §5 Rust bindings are **deferred** (see §7 below). +2. **The ADR-0242 §5 carry seams have zero live call sites.** `kappa_search_event` + (goldtether) and `cross_band_discovery_gate` (multi_scale_energy) are tested + primitives that nothing live invokes. Phase 3 Lane A wires them. +3. **The I-04 real-compiler sensorium feed is test-only** (correct per A-04 + quarantine) and the audio versor is float32-origin upcast to f64. Phase 3 + Lane B gives the feed its first (off-serving) consumer. + +## 2. Substrate inventory — what exists vs. what this arc builds + +Already landed (verified on `main` @ `19819194`): + +| Lifecycle stage | Existing organ | +| --- | --- | +| §2.1 ingress superposition | `core/physics/sensorium_wave_feed.py` (`ModalityPacket`, `compile_packet_to_psi`, `superpose_packets`, `phase_correlate`) | +| §2.1 resonant projection R_k | `WaveManifold.phase_correlation` / `resonant_recall` / `resonant_reconstruct` | +| §2.3 energy classes E0–E4 | `core/physics/energy.py` (`EnergyClass`, `FieldEnergyOperator`; ADR-0006) | +| §2.3 readback rules | `packs/en/readback_rules.py` (ADR-0145 energy-modulated readback) | +| §2.3 GoldTether residual | `core/physics/goldtether.py` (`coherence_residual`, `GoldTetherMonitor`) + `WaveManifold.measure_unitary_residual` | +| §2.4 surprise + γ check | `core/physics/surprise.py` (`surprise_residual`, `is_discovery_eligible`, `dual_operator`; ADR-0239) | +| §2.4 discovery plumbing | `core/contemplation/runner.py` already accepts a `DiscoveryCandidateSink`; `teaching/discovery.py` `DiscoveryCandidate` (ADR-0055 Phase B) | +| §2.4 self-authorship | `core/physics/self_authorship.py` (`SelfAuthorshipMiner`, I-03 pinned speculative-only) | +| §2.5 biography holonomy | `core/physics/biography.py` (`integrate_biography`, `reconstruct_biography`) — **no live call sites yet** | +| I-01…I-05, A-02/A-04, `core_ha` absence | `tests/test_third_door_cohesion.py` (master plan §3/§8 already pinned; §7 `core_ha` migration is **moot** — package absent and test-pinned) | + +Genuinely unbuilt: the §2.2 relaxation reasoner (`cognitive_lifecycle.py`), the +inter-organ wiring (three lanes), and the §4 falsifiability benchmarks. + +## 3. Sketch-defect ledger (ADR §3 reference prototype) + +Pinned executable in `tests/test_adr_0243_sketch_defect_pins.py`; the reference +prototype must **never** be ported verbatim (precedent: PR #52 sketch-defect pins). + +- **SD-A — the sketch egress gate always rejects.** It computes + `drift = |ψᵀIᵀψ − 1|` with `I` antisymmetric; `ψᵀIᵀψ ≡ 0` identically, so + `drift ≡ 1 > ε` for *every* state, valid or not. A fail-always gate is the + inverse of a hollow gate and equally worthless. The real residual already + exists: `WaveManifold.measure_unitary_residual` / `goldtether.coherence_residual`. +- **SD-B — the sketch "relaxation" cannot relax.** Iterating + `R = expm(H·I·dt)` with renormalization does not settle into the minimum-energy + eigenmode: the generator's spectrum is (block-wise) purely imaginary, so the + iterates oscillate and preserve mode magnitudes; nothing dissipates. Relaxation + to the ground state requires the **dissipative (imaginary-time) semigroup** + `ψ ← normalize(exp(−H·dt)·ψ)` — deterministic power iteration converging at a + rate set by the spectral gap of H. +- **SD-C — the sketch's block matrix "I" is not the Cl(4,1) pseudoscalar action.** + It shares the property I² = −Id but is a different operator from + left-multiplication by e₀e₁e₂e₃e₄ in the real algebra. All algebra goes + through `algebra/` — no bespoke component-shuffling proxies. + +## 4. Deviations ledger (implementation vs. ADR prose — for the acceptance packet) + +- **D-1 imaginary-time relaxer** replaces the sketch's `exp(H·I·t)` loop (SD-B). + Implemented via `np.linalg.eigh` on symmetric H: `exp(−H·dt) = Q·exp(−Λ·dt)·Qᵀ`, + pure numpy f64, no scipy dependency added. +- **D-2 honest guarantee restatement.** The ADR's "mathematically guaranteed … + zero room for intermediate fabrication" is conditional: it holds only insofar + as (a) the Hamiltonian compiler encodes the constraints faithfully, (b) the + ground state is separated by a positive spectral gap, and (c) the iteration has + converged (certified, not assumed). The acceptance packet states it this way. +- **D-3 I-05 reconciliation.** Relaxation is *deliberately non-unitary* inside + the off-serving solver — that is what dissipation is. I-05 (amplitude + conservation) governs live serve-path transitions; the relaxer's terminal + normalize is an **owned construction boundary** per the master plan's + doctrine note (no hot-path drift repair; breaches at the boundary fail closed). +- **D-4 R-02 premise retired at v1 scale.** `expm` of a 32×32 symmetric matrix + in numpy is trivial; no Rust port is needed or permitted (caveat 1) for v1. +- **D-5 E0/E1 crystallization is proposal-only.** No vault writes from the + lifecycle; cold states emit a typed crystallization *proposal artifact* + through the one-mutation-path (I-03), never a direct `COHERENT` write. + +## 5. Phases + +- **Phase 0 — Governance + doc hygiene** (this PR): commit ADR-0243 verbatim as + Proposed; sketch-defect pins; `*.gdoc` gitignored; stale prior-arc status line + fixed; this plan committed. Local strays (research-copy duplicates, + `core-rs/uv.lock`) removed from the working tree. +- **Phase 1 — merged into Phase 0** (pins land with the plan). +- **Phase 2 — flagship `core/physics/cognitive_lifecycle.py`** (1 PR): Tier-2 + off-serving (A-04 list + lazy barrel + transitive guard). Typed + content-addressed `ProblemHamiltonian` compiler for two checkable domains only + (propositional penalty projectors; convex quadratic). Imaginary-time relaxer + (D-1) with convergence certificate (eigen-residual + gap estimate) and typed + fail-closed errors. Egress verdict composing `measure_unitary_residual` + + `coherence_residual` + `EnergyClass` routing (E0/E1 → proposal artifact per + D-5; E3/E4 → readback-eligible). Ingress delegates to `sensorium_wave_feed` / + `WaveManifold` — no duplication. TDD; ground-state property test vs. direct + eigensolve; determinism digests. +- **Phase 3 — wiring lanes** (three independent PRs after Phase 2 API lands): + - **Lane A (§2.4; closes caveat 2):** wire `is_discovery_eligible` + + `cross_band_discovery_gate` into the contemplation runner's existing sink; + high-surprise speculative packets emit `DiscoveryCandidate`s + (proposal-only). Give `kappa_search_event` its live telemetry call site at + the κ line-search (calibration-gated per R-04). + - **Lane B (§2.1→§2.3; closes caveat 3):** off-serving corridor in `evals/`: + real Audio/Vision compilers → ψ_in → relaxation → E-class → `en` readback + rules → GoldTether gate. First live consumer of the I-04 feed. + - **Lane C (§2.5):** ADR-0240 validation-harness PASS → + `integrate_biography`, with I-01 closure asserts and provenance recording. +- **Phase 4 — falsifiability benchmarks** (1 PR): + `evals/adr_0243_cognitive_lifecycle/` fixed-replay eval + (`adr_0242_v2_energy_compare` pattern): fidelity score, surprise separation + (ID vs OOD), insertion cost, f32 drift over T=1000, typed failure thresholds. + **Decisive falsifier:** a propositional slice scored against + `evals/deductive_logic` ROBDD gold with wrong=0 accounting. "Field engine is + NOT the reasoner today" stands unless this evidence says otherwise. +- **Phase 5 — acceptance packet** (1 PR): D10-pattern packet — claims vs. + evidence, deviations D-1…D-5, deferred items — for Joshua's ruling. Status + flip via governance commit only (provenance-guard compliant). + +## 6. Dependencies and process + +Phase 2 precedes 3/4; Lanes A/B/C are mutually independent; Phase 5 last. +Every PR: dedicated worktree off `forgejo/main`, in-worktree smoke gate +(`uv run core test --suite smoke -q`) before push, `[Verification]:` line in the +PR description, Forgejo MCP for PR operations (no `gh`, no `tea`), stacked +merges substrate-first, merged branches + worktrees deleted immediately. +Lane briefs for Phase 3 are written at Phase 2 completion (production-line +pattern, `docs/handoff/`); dispatch is Shay's call. + +## 7. Explicitly deferred (not in this arc) + +- Master plan §5 Rust bindings (`cl41::wedge` exists; `expm`/`versor_unit_residual` + SIMD do not) — blocked on a Rust f64 GP + D9-style parity gate (caveat 1). +- MLX / Apple-UMA lanes — aspirational prose; no benchmark evidence. +- Delta-CRDT multi-writer vault — behind the multi-writer gate (PR #42 ruling). +- `he`/`el` readback rules — packs are skeletal; `en` only for the corridor. +- Any serve-path activation of lifecycle machinery — A-04 quarantine holds + until ratification. + +## 8. Risks + +- **H-compiler scope explosion** → v1 fixed at two typed domains; extensions + require new evidence, not new matchers (ADR-0175 discipline). +- **Reasoner overclaim / thesis drift** → the Phase 4 wrong=0 ROBDD cross-check + is the gate; disagreements fail the run and go in the packet verbatim. +- **Hollow gates in egress wiring** → every gate ships with a must-reject test + (and SD-A shows the dual failure mode: a gate that rejects everything). +- **I-05 doctrinal tension** → D-3 reconciliation text; normalize only at the + owned construction boundary; boundary breaches fail closed, never repaired. diff --git a/tests/test_adr_0243_sketch_defect_pins.py b/tests/test_adr_0243_sketch_defect_pins.py new file mode 100644 index 00000000..68c23312 --- /dev/null +++ b/tests/test_adr_0243_sketch_defect_pins.py @@ -0,0 +1,140 @@ +"""ADR-0243 §3 reference-prototype sketch-defect pins (SD-A, SD-B, SD-C). + +Authority: docs/plans/adr-0243-implementation-plan.md §3 (sketch-defect ledger). +Precedent: PR #52 sketch-defect pins for the rejected Drive-draft re-implementation. + +The ADR-0243 §3 Python prototype (``CognitiveLifecycleEngine``) contains +mathematical defects that make a verbatim port worthless or misleading. These +pins prove each defect with a deterministic counterexample so the sketch can +never be re-landed as-is: + +SD-A The sketch egress gate computes ``drift = |psi^T I^T psi - 1|`` with an + antisymmetric ``I``. The quadratic form of an antisymmetric matrix is + identically zero, so drift == 1 for EVERY state and the gate rejects + everything — valid and invalid alike. A fail-always gate is the inverse + of a hollow gate and equally worthless. The real residual is + ``WaveManifold.measure_unitary_residual`` / ``goldtether.coherence_residual``. + +SD-B The sketch "relaxation" loop iterates ``R = expm(H @ I * dt)`` with + renormalization. The generator's spectrum is (block-wise) purely + imaginary, so iterates oscillate and nothing dissipates: the loop never + settles into the minimum-energy eigenmode of H. Honest deterministic + relaxation is the dissipative (imaginary-time) semigroup + ``psi <- normalize(exp(-H dt) psi)`` — power iteration whose convergence + rate is the spectral gap of H. + +SD-C The sketch's block matrix "I" ("central pseudoscalar proxy") is NOT the + Cl(4,1) pseudoscalar action: it shares I^2 = -Id but is a different + operator from left-multiplication by e0 e1 e2 e3 e4 in the real algebra. + All algebra goes through ``algebra/`` — no component-shuffling proxies. + +Deterministic fixtures only — no random spinors as truth. +""" + +from __future__ import annotations + +import numpy as np + +from algebra.cl41 import N_COMPONENTS, geometric_product + + +def _sketch_proxy_i() -> np.ndarray: + """The ADR-0243 §3 prototype's 'central pseudoscalar proxy' matrix.""" + proxy = np.zeros((N_COMPONENTS, N_COMPONENTS), dtype=np.float64) + for i in range(N_COMPONENTS // 2): + proxy[2 * i, 2 * i + 1] = 1.0 + proxy[2 * i + 1, 2 * i] = -1.0 + return proxy + + +def _expm_series(M: np.ndarray, terms: int = 60) -> np.ndarray: + """Deterministic truncated-series matrix exponential (no scipy).""" + out = np.eye(M.shape[0], dtype=np.float64) + term = np.eye(M.shape[0], dtype=np.float64) + for k in range(1, terms): + term = term @ M / float(k) + out = out + term + return out + + +def _onehot(i: int) -> np.ndarray: + v = np.zeros(N_COMPONENTS, dtype=np.float64) + v[i] = 1.0 + return v + + +# --- SD-A: the sketch egress gate rejects every state ------------------------- + + +def test_sd_a_sketch_egress_gate_rejects_every_state(): + proxy = _sketch_proxy_i() + mixed = np.arange(1, N_COMPONENTS + 1, dtype=np.float64) + mixed /= np.linalg.norm(mixed) + candidates = [_onehot(0), _onehot(5), _onehot(31), mixed] + + for psi in candidates: + # Exactly the sketch's egress computation. + psi_rev = proxy.T @ psi + norm_product = psi @ psi_rev + drift = abs(norm_product - 1.0) + # Antisymmetric quadratic form vanishes identically... + assert abs(norm_product) < 1e-12 + # ...so the sketch gate rejects EVERYTHING, unit-norm states included. + assert drift > 1e-6 # sketch epsilon_drift + assert abs(drift - 1.0) < 1e-12 + + +# --- SD-B: the sketch loop cannot relax; imaginary time can ------------------- + + +def test_sd_b_sketch_loop_oscillates_while_imaginary_time_relaxes(): + energies = np.linspace(0.5, 4.0, N_COMPONENTS) + hamiltonian = np.diag(energies) # ground state = component 0 + psi0 = np.ones(N_COMPONENTS, dtype=np.float64) + psi0 /= np.linalg.norm(psi0) + + # Exactly the sketch's relaxation loop: R = expm(H @ I * dt), 100 steps. + propagator = _expm_series(hamiltonian @ _sketch_proxy_i() * 0.01) + psi = psi0.copy() + for _ in range(100): + psi = propagator @ psi + norm = np.linalg.norm(psi) + if norm > 1e-12: + psi /= norm + sketch_ground_overlap = abs(psi[0]) + + # Honest dissipative semigroup: psi <- normalize(exp(-H dt) psi). + decay = np.exp(-energies * 0.5) + phi = psi0.copy() + for _ in range(200): + phi = decay * phi + phi /= np.linalg.norm(phi) + imaginary_time_ground_overlap = abs(phi[0]) + + # The sketch never concentrates on the ground state... + assert sketch_ground_overlap < 0.9 + # ...while imaginary time converges to it (rate = spectral gap). + assert imaginary_time_ground_overlap > 1.0 - 1e-8 + + +# --- SD-C: the proxy matrix is not the Cl(4,1) pseudoscalar action ------------ + + +def test_sd_c_proxy_matrix_is_not_the_pseudoscalar_action(): + # Real pseudoscalar e0 e1 e2 e3 e4 (0-indexed basis vectors, f64). + pseudoscalar = _onehot(1) # e0 lives at component 1 + for i in range(1, 5): + pseudoscalar = geometric_product(pseudoscalar, _onehot(1 + i)) + + # Left-multiplication-by-pseudoscalar as a 32x32 matrix. + action = np.zeros((N_COMPONENTS, N_COMPONENTS), dtype=np.float64) + for j in range(N_COMPONENTS): + action[:, j] = geometric_product(pseudoscalar, _onehot(j)) + + proxy = _sketch_proxy_i() + identity = np.eye(N_COMPONENTS) + # Both square to -Id (I5^2 = -1 in Cl(4,1)) — the proxy mimics the square... + assert np.allclose(action @ action, -identity, atol=1e-12) + assert np.allclose(proxy @ proxy, -identity, atol=1e-12) + # ...but is a DIFFERENT operator: the actions disagree outright. + assert float(np.max(np.abs(action - proxy))) > 0.5 From c3b4d045f1c4e616ff278bc5c1e02b8600d41c7b Mon Sep 17 00:00:00 2001 From: Shay Date: Fri, 17 Jul 2026 06:19:33 -0700 Subject: [PATCH 2/2] =?UTF-8?q?feat(adr-0243):=20flagship=20cognitive=5Fli?= =?UTF-8?q?fecycle.py=20=E2=80=94=20ingress/relax/egress,=20Tier-2=20off-s?= =?UTF-8?q?erving?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements ADR-0243 §2 honestly per the plan's deviations ledger, correcting the §3 sketch's three pinned defects (SD-A/B/C): - Ingress delegates superposition to sensorium_wave_feed, normalizes once at the owned construction boundary (D-3). - Relaxation is the imaginary-time semigroup psi <- normalize(exp(-(H-lam0)*dt)*psi) (D-1) with a certified, never-assumed convergence certificate (D-2): exact ground energy, achieved Rayleigh energy, eigen-residual, spectral gap, and a psi_digest binding the certificate to the exact state it certifies. - Two checkable problem domains: quadratic_well (convex target-decoding) and propositional (Cl(4,1) blade lattice = assignment lattice of <=5 atoms; diagonal penalty Hamiltonian; entailment via UNSAT(premises & !conclusion) scored against independent truth-table gold). - Egress composes unit-amplitude-density + certificate-binding + versor closure (routes, does not admit, per the dual of SD-A) + ADR-0006 energy classes + E0/E1 crystallization policy. Cold states emit a CrystallizationProposal (epistemic_status pinned SPECULATIVE by the type; D-5/I-03) — never a vault write. - Quarantined off-serving (A-04): lazy barrel export, banned in the transitive subprocess probe and the cohesion AST suite. Adversarial verification (finder/verifier workflow; most verify agents lost to a session limit, every surviving finding reproduced in-tree before acting) surfaced and this closes two real defects beyond the plan text: - egress_gate admitted any unit state paired with any converged certificate from an unrelated run, so CrystallizationProposal could package a foreign psi_digest next to an unrelated certificate_id as false provenance. Fixed by binding the certificate to its state (psi_digest) and refusing mismatches (certificate_state_mismatch). - A state with a spectral gap below the requested tolerance could certify with ZERO ground-space overlap (energy window alone can't resolve it). Certification now also requires the excited weight (E-lam0)/gap <= tol (refusal spectral_gap_below_tolerance), and the degeneracy cluster is capped at the acceptance window so certificates report the honest rate-limiting gap instead of absorbing a hairline split. 38 tests (31 original + 7 hardening: certificate-binding must-reject, gap-refusal + true-ground pair, dense-branch (eigh) refusals, iterate-collapse, Hamiltonian/egress shape must-rejects, E2 hold route, hardcoded canonical entailment verdicts independent of the gold function's code shape). [Verification]: lifecycle+pins+cohesion+transitive 58 passed; in-worktree smoke 176 passed (131s); fast lane 11750 passed, 108 skipped (574s, exit 0). --- core/physics/__init__.py | 78 ++ core/physics/cognitive_lifecycle.py | 960 +++++++++++++++++++++ docs/plans/adr-0243-implementation-plan.md | 14 + tests/test_adr_0243_cognitive_lifecycle.py | 662 ++++++++++++++ tests/test_serve_quarantine_transitive.py | 1 + tests/test_third_door_cohesion.py | 2 + 6 files changed, 1717 insertions(+) create mode 100644 core/physics/cognitive_lifecycle.py create mode 100644 tests/test_adr_0243_cognitive_lifecycle.py diff --git a/core/physics/__init__.py b/core/physics/__init__.py index 81c583c9..5cded114 100644 --- a/core/physics/__init__.py +++ b/core/physics/__init__.py @@ -118,6 +118,32 @@ _LAZY_EXPORTS: dict[str, str] = { "multi_scale_energy_for_schedule": "core.physics.multi_scale_energy", "multi_scale_energy_vector": "core.physics.multi_scale_energy", "schedule_mid_span_fraction": "core.physics.multi_scale_energy", + # cognitive_lifecycle — ADR-0243 ingress→relaxation→egress (never serve) + "CognitiveLifecycleEngine": "core.physics.cognitive_lifecycle", + "CognitiveLifecycleError": "core.physics.cognitive_lifecycle", + "CrystallizationProposal": "core.physics.cognitive_lifecycle", + "EgressValidationError": "core.physics.cognitive_lifecycle", + "EgressVerdict": "core.physics.cognitive_lifecycle", + "HamiltonianCompileError": "core.physics.cognitive_lifecycle", + "IngressDegenerate": "core.physics.cognitive_lifecycle", + "IngressWavePacket": "core.physics.cognitive_lifecycle", + "LifecycleOutcome": "core.physics.cognitive_lifecycle", + "ProblemHamiltonian": "core.physics.cognitive_lifecycle", + "PropositionalEntailmentVerdict": "core.physics.cognitive_lifecycle", + "PropositionalProblem": "core.physics.cognitive_lifecycle", + "RelaxationCertificate": "core.physics.cognitive_lifecycle", + "RelaxationInputError": "core.physics.cognitive_lifecycle", + "RelaxationNotConverged": "core.physics.cognitive_lifecycle", + "RelaxationNumericalFailure": "core.physics.cognitive_lifecycle", + "RelaxationResult": "core.physics.cognitive_lifecycle", + "assignment_component_index": "core.physics.cognitive_lifecycle", + "compile_propositional": "core.physics.cognitive_lifecycle", + "compile_quadratic_well": "core.physics.cognitive_lifecycle", + "egress_gate": "core.physics.cognitive_lifecycle", + "ingest_context": "core.physics.cognitive_lifecycle", + "propositional_entails": "core.physics.cognitive_lifecycle", + "relax_to_ground": "core.physics.cognitive_lifecycle", + "uniform_assignment_state": "core.physics.cognitive_lifecycle", } from typing import TYPE_CHECKING @@ -153,6 +179,33 @@ if TYPE_CHECKING: # static-analysis only — never imported at runtime (serve q multi_scale_energy_vector, schedule_mid_span_fraction, ) + from core.physics.cognitive_lifecycle import ( + CognitiveLifecycleEngine, + CognitiveLifecycleError, + CrystallizationProposal, + EgressValidationError, + EgressVerdict, + HamiltonianCompileError, + IngressDegenerate, + IngressWavePacket, + LifecycleOutcome, + ProblemHamiltonian, + PropositionalEntailmentVerdict, + PropositionalProblem, + RelaxationCertificate, + RelaxationInputError, + RelaxationNotConverged, + RelaxationNumericalFailure, + RelaxationResult, + assignment_component_index, + compile_propositional, + compile_quadratic_well, + egress_gate, + ingest_context, + propositional_entails, + relax_to_ground, + uniform_assignment_state, + ) __all__ = [ "SalienceOperator", "SalienceMap", "FieldRegion", @@ -207,6 +260,31 @@ __all__ = [ "multi_scale_energy_for_schedule", "multi_scale_energy_vector", "schedule_mid_span_fraction", + "CognitiveLifecycleEngine", + "CognitiveLifecycleError", + "CrystallizationProposal", + "EgressValidationError", + "EgressVerdict", + "HamiltonianCompileError", + "IngressDegenerate", + "IngressWavePacket", + "LifecycleOutcome", + "ProblemHamiltonian", + "PropositionalEntailmentVerdict", + "PropositionalProblem", + "RelaxationCertificate", + "RelaxationInputError", + "RelaxationNotConverged", + "RelaxationNumericalFailure", + "RelaxationResult", + "assignment_component_index", + "compile_propositional", + "compile_quadratic_well", + "egress_gate", + "ingest_context", + "propositional_entails", + "relax_to_ground", + "uniform_assignment_state", ] diff --git a/core/physics/cognitive_lifecycle.py b/core/physics/cognitive_lifecycle.py new file mode 100644 index 00000000..e18253d7 --- /dev/null +++ b/core/physics/cognitive_lifecycle.py @@ -0,0 +1,960 @@ +"""ADR-0243 §2 — Wave-Field Cognitive Lifecycle (Tier-2, OFF-SERVING). + +Ingress → Hamiltonian-well relaxation → egress, on the Cl(4,1) coefficient +space. Implements the ADR-0243 lifecycle honestly, with the §3 reference +prototype's defects corrected (they are pinned in +``tests/test_adr_0243_sketch_defect_pins.py``; deviations D-1…D-5 are recorded +in ``docs/plans/adr-0243-implementation-plan.md`` §4): + +* **Ingress** delegates to :mod:`core.physics.sensorium_wave_feed` + (superposition) and normalizes ONCE at this module's owned construction + boundary (deviation D-3; master-plan doctrine note — no hot-path repair). +* **Relaxation** is the dissipative imaginary-time semigroup + ``ψ ← normalize(exp(−(H−λ0)·dt)·ψ)`` — deterministic power iteration that + provably converges to the ground eigenspace at a rate set by the spectral + gap (deviation D-1). The sketch's ``exp(H·I·t)`` loop oscillates and cannot + relax (pin SD-B). Convergence is certified, never assumed (deviation D-2): + the certificate carries the exact ground energy from the spectrum, the + achieved Rayleigh energy, the eigen-residual, the gap (certification + requires the gap be resolvable at the requested tolerance), and a byte + digest of the certified state binding the evidence to that exact ψ. +* **Egress** composes the existing organs — unit amplitude density, + the certificate↔state binding check (a borrowed certificate refuses), + :meth:`WaveManifold.measure_unitary_residual` (the ADR's R_GoldTether; + reported, and REQUIRED only on the crystallization route where states must + be closed versors), ADR-0006 energy classes via + :func:`core.physics.wave_energy_boundary.energy_profile_from_wave`, and the + E0/E1 crystallization policy via + :func:`~core.physics.wave_energy_boundary.crystallization_for_holographic_seal`. + Gating a multi-mode superposition on versor closure would reject every + legitimate interference state (the dual of pin SD-A), so versor closure + routes rather than admits. +* **No mutation**: cold states emit a :class:`CrystallizationProposal` + (``epistemic_status="SPECULATIVE"`` enforced by the type) — never a vault + write (deviation D-5 / cohesion I-03). This module imports no vault store. + +Problem domains (v1, plan §5 Phase 2 — two checkable domains only): + +* ``quadratic_well`` — H = curvature·(Id − ψ₀ψ₀ᵀ): ground space is span(ψ₀); + relaxation decodes the target from any non-orthogonal start. +* ``propositional`` — the Cl(4,1) blade lattice IS the assignment lattice of + ≤ 5 atoms: blade {e_{i₁}…e_{i_k}} ↔ assignment with exactly those atoms + True. Clauses compile to a DIAGONAL penalty Hamiltonian counting falsified + clauses per assignment; the ground space is the span of satisfying + assignments and relaxation decodes the model set. Verdicts (SAT / + entailment) read the exact ground energy of the same H — integer counts + scaled by ``penalty``, no floating eigensolve on the diagonal path. + +Honesty note (D-2): the propositional compiler enumerates the ≤ 32 assignment +components — this is exact small-domain decoding, not a scalability claim. +Its value is falsifiability: verdicts are scored against independent gold +(truth tables here; ``generate.proof_chain`` ROBDD in the Phase 4 eval). + +Serve quarantine (A-04): never imported by ``chat/runtime.py``; exported +lazily via the ``core.physics`` barrel; enforced by +``tests/test_serve_quarantine_transitive.py`` and the cohesion suite. +""" + +from __future__ import annotations + +import hashlib +import json +from dataclasses import dataclass, field +from typing import Any, Mapping, Sequence + +import numpy as np + +from algebra.cl41 import N_COMPONENTS, geometric_product +from core.physics.energy import EnergyClass, EnergyProfile, FieldEnergyOperator +from core.physics.sensorium_wave_feed import PacketLike, _coerce_packet, superpose_packets +from core.physics.wave_energy_boundary import ( + CrystallizationDecision, + crystallization_for_holographic_seal, + energy_profile_from_wave, +) +from core.physics.wave_manifold import WaveManifold + +_NEAR_ZERO = 1e-12 +_UNIT_TOL = 1e-9 +_EPSILON_DRIFT = 1e-6 +_MAX_ATOMS = 5 +_SPECULATIVE = "SPECULATIVE" + + +# --- Typed fail-closed errors -------------------------------------------------- + + +class CognitiveLifecycleError(ValueError): + """Fail-closed lifecycle refusal with structured disclosure.""" + + def __init__(self, reason: str, **disclosure: Any) -> None: + self.reason = reason + self.disclosure = dict(disclosure) + super().__init__(f"cognitive_lifecycle refused [{reason}]: {self.disclosure}") + + +class IngressDegenerate(CognitiveLifecycleError): + """Superposed context has no resolvable amplitude (no confabulated field).""" + + +class HamiltonianCompileError(CognitiveLifecycleError): + """Problem → Hamiltonian compilation refused (malformed constraints).""" + + +class RelaxationInputError(CognitiveLifecycleError): + """Relaxer input refused (shape / non-finite / non-unit ψ0). No repair.""" + + +class RelaxationNumericalFailure(CognitiveLifecycleError): + """Non-finite value produced during relaxation (mirrors OptimizationFailure).""" + + +class RelaxationNotConverged(CognitiveLifecycleError): + """Relaxation did not certify the ground state; carries the certificate.""" + + def __init__(self, reason: str, certificate: "RelaxationCertificate", **disclosure: Any) -> None: + self.certificate = certificate + super().__init__(reason, **disclosure) + + +class EgressValidationError(CognitiveLifecycleError): + """Egress gate refused to evaluate a malformed state (shape / non-finite).""" + + +# --- Content addressing ---------------------------------------------------------- + + +def _content_id(payload: Mapping[str, Any]) -> str: + raw = json.dumps(payload, sort_keys=True, separators=(",", ":"), default=str) + return hashlib.sha256(raw.encode("utf-8")).hexdigest()[:24] + + +def _psi_digest(psi: np.ndarray) -> str: + return hashlib.sha256(np.ascontiguousarray(psi, dtype=np.float64).tobytes()).hexdigest()[:24] + + +def _as_psi(x: np.ndarray, name: str, *, error: type[CognitiveLifecycleError]) -> np.ndarray: + arr = np.asarray(x, dtype=np.float64) + if arr.shape != (N_COMPONENTS,): + raise error("bad_shape", name=name, shape=list(np.shape(x))) + if not np.all(np.isfinite(arr)): + raise error("non_finite", name=name) + return arr + + +# --- Blade lattice ↔ assignment lattice (propositional substrate) ----------------- +# +# Subset S ⊆ {0..4} of basis-vector indices ↔ the canonical blade e_{i1}…e_{ik} +# (ascending product) ↔ the truth assignment with exactly the atoms in S True. +# The component index and sign are DERIVED from the algebra's own geometric +# product — no hand-maintained table to drift from algebra/cl41. + + +def _vector_onehot(i: int) -> np.ndarray: + v = np.zeros(N_COMPONENTS, dtype=np.float64) + v[1 + i] = 1.0 # basis vector e_i lives at component 1+i (0-indexed atoms) + return v + + +def _build_subset_component_map() -> tuple[tuple[int, ...], tuple[float, ...]]: + indices: list[int] = [] + signs: list[float] = [] + for mask in range(1 << _MAX_ATOMS): + blade = np.zeros(N_COMPONENTS, dtype=np.float64) + blade[0] = 1.0 + for i in range(_MAX_ATOMS): + if mask & (1 << i): + blade = geometric_product(blade, _vector_onehot(i)) + support = np.nonzero(np.abs(blade) > 0.5)[0] + if support.size != 1: + raise RuntimeError(f"blade map degenerate for mask {mask}: support={support}") + idx = int(support[0]) + indices.append(idx) + signs.append(float(np.sign(blade[idx]))) + if len(set(indices)) != (1 << _MAX_ATOMS): + raise RuntimeError("blade map is not a bijection onto components") + return tuple(indices), tuple(signs) + + +_SUBSET_COMPONENT, _SUBSET_SIGN = _build_subset_component_map() + + +def assignment_component_index(assignment_mask: int) -> int: + """Component index of the blade encoding *assignment_mask* (bit i = atom i True).""" + if not (0 <= int(assignment_mask) < (1 << _MAX_ATOMS)): + raise HamiltonianCompileError("assignment_mask_out_of_range", mask=int(assignment_mask)) + return _SUBSET_COMPONENT[int(assignment_mask)] + + +# --- Ingress ---------------------------------------------------------------------- + + +@dataclass(frozen=True, slots=True) +class IngressWavePacket: + """Normalized ingress field ψ_context with provenance (ADR-0243 §2.1).""" + + psi: np.ndarray + domain_id: str + modality_ids: tuple[str, ...] + packet_digest: str + + def __post_init__(self) -> None: + arr = _as_psi(self.psi, "ψ_context", error=IngressDegenerate) + arr = arr.copy() + arr.setflags(write=False) + object.__setattr__(self, "psi", arr) + + +def ingest_context(packets: Sequence[PacketLike], domain_id: str) -> IngressWavePacket: + """Superpose modality packets and normalize at the owned construction boundary. + + Delegates superposition to :func:`sensorium_wave_feed.superpose_packets` + (which refuses empty input). Normalization here is the ONE owned + construction boundary of the lifecycle (D-3) — a degenerate superposition + (destructive cancellation below ``1e-12``) is refused, never zero-filled. + """ + domain = str(domain_id).strip() + if not domain: + raise IngressDegenerate("empty_domain_id") + total = superpose_packets(packets) + norm = float(np.linalg.norm(total)) + if not np.isfinite(norm) or norm < _NEAR_ZERO: + raise IngressDegenerate("degenerate_superposition", norm=norm, n_packets=len(packets)) + psi = (total / norm).astype(np.float64) + modality_ids = tuple(_coerce_packet(p).modality_id for p in packets) + return IngressWavePacket( + psi=psi, + domain_id=domain, + modality_ids=modality_ids, + packet_digest=_content_id( + {"psi": _psi_digest(psi), "domain": domain, "modalities": list(modality_ids)} + ), + ) + + +# --- Problem Hamiltonians ----------------------------------------------------------- + + +@dataclass(frozen=True, slots=True) +class ProblemHamiltonian: + """Typed, content-addressed constraint operator H (symmetric, 32×32, f64). + + ``matrix`` is validated (shape, finiteness, symmetry ≤ 1e-12) and frozen + read-only; asymmetric input is refused, never symmetrized (no repair). + """ + + matrix: np.ndarray + domain: str + metadata: Mapping[str, Any] = field(default_factory=dict) + hamiltonian_id: str = "" + is_diagonal: bool = False + + def __post_init__(self) -> None: + arr = np.asarray(self.matrix, dtype=np.float64) + if arr.shape != (N_COMPONENTS, N_COMPONENTS): + raise HamiltonianCompileError("bad_shape", shape=list(arr.shape)) + if not np.all(np.isfinite(arr)): + raise HamiltonianCompileError("non_finite_matrix") + asym = float(np.max(np.abs(arr - arr.T))) + if asym > 1e-12: + raise HamiltonianCompileError("not_symmetric", max_asymmetry=asym) + arr = arr.copy() + arr.setflags(write=False) + diagonal = bool(np.count_nonzero(arr - np.diag(np.diagonal(arr))) == 0) + meta = dict(self.metadata) + object.__setattr__(self, "matrix", arr) + object.__setattr__(self, "metadata", meta) + object.__setattr__(self, "is_diagonal", diagonal) + object.__setattr__( + self, + "hamiltonian_id", + _content_id( + { + "domain": str(self.domain), + "matrix_sha": hashlib.sha256(arr.tobytes()).hexdigest(), + "metadata": {k: str(v) for k, v in sorted(meta.items())}, + } + ), + ) + + +def compile_quadratic_well(target_psi: np.ndarray, *, curvature: float = 1.0) -> ProblemHamiltonian: + """H = curvature·(Id − ψ₀ψ₀ᵀ): ground space span(ψ₀) at energy 0, gap = curvature.""" + target = _as_psi(target_psi, "target_psi", error=HamiltonianCompileError) + c = float(curvature) + if not np.isfinite(c) or c <= 0.0: + raise HamiltonianCompileError("curvature_not_positive", curvature=c) + norm_err = abs(float(np.linalg.norm(target)) - 1.0) + if norm_err > _UNIT_TOL: + raise HamiltonianCompileError("target_not_unit", norm_residual=norm_err) + matrix = c * (np.eye(N_COMPONENTS, dtype=np.float64) - np.outer(target, target)) + return ProblemHamiltonian( + matrix=matrix, + domain="quadratic_well", + metadata={"curvature": c, "target_digest": _psi_digest(target)}, + ) + + +PropositionalLiteral = tuple[str, bool] +Clause = tuple[PropositionalLiteral, ...] + + +@dataclass(frozen=True, slots=True) +class PropositionalProblem: + """CNF over ≤ 5 atoms; atom i ↔ basis vector e_i (order as given).""" + + atoms: tuple[str, ...] + clauses: tuple[Clause, ...] + problem_id: str = "" + + def __post_init__(self) -> None: + atoms = tuple(str(a) for a in self.atoms) + if not (1 <= len(atoms) <= _MAX_ATOMS): + raise HamiltonianCompileError("atom_count_out_of_range", n_atoms=len(atoms)) + if len(set(atoms)) != len(atoms) or any(not a.strip() for a in atoms): + raise HamiltonianCompileError("atoms_not_unique_nonempty", atoms=list(atoms)) + clauses: list[Clause] = [] + for ci, clause in enumerate(self.clauses): + lits = tuple((str(a), bool(p)) for a, p in clause) + if not lits: + raise HamiltonianCompileError("empty_clause", clause_index=ci) + if len(set(lits)) != len(lits): + raise HamiltonianCompileError("duplicate_literal", clause_index=ci) + for atom, _ in lits: + if atom not in atoms: + raise HamiltonianCompileError("unknown_atom", clause_index=ci, atom=atom) + clauses.append(lits) + object.__setattr__(self, "atoms", atoms) + object.__setattr__(self, "clauses", tuple(clauses)) + object.__setattr__( + self, + "problem_id", + _content_id({"atoms": list(atoms), "clauses": [list(c) for c in clauses]}), + ) + + @property + def n_atoms(self) -> int: + return len(self.atoms) + + +def _falsification_counts(problem: PropositionalProblem) -> tuple[int, ...]: + """Clauses falsified per assignment mask (exact integer counts).""" + k = problem.n_atoms + index = {a: i for i, a in enumerate(problem.atoms)} + counts = [0] * (1 << k) + for mask in range(1 << k): + for clause in problem.clauses: + satisfied = False + for atom, polarity in clause: + bit = bool(mask & (1 << index[atom])) + if bit == polarity: + satisfied = True + break + if not satisfied: + counts[mask] += 1 + return tuple(counts) + + +def compile_propositional(problem: PropositionalProblem, *, penalty: float = 1.0) -> ProblemHamiltonian: + """Diagonal penalty Hamiltonian: diag[component(a)] = penalty · #clauses falsified by a. + + Out-of-domain components (blades using vectors beyond the atom set) get + ``penalty·(len(clauses)+1)`` — strictly above every in-domain value, so + the ground space is always inside the assignment lattice. Satisfiable iff + the exact ground energy is 0. + """ + p = float(penalty) + if not np.isfinite(p) or p <= 0.0: + raise HamiltonianCompileError("penalty_not_positive", penalty=p) + counts = _falsification_counts(problem) + diag = np.full(N_COMPONENTS, p * float(len(problem.clauses) + 1), dtype=np.float64) + for mask, count in enumerate(counts): + diag[_SUBSET_COMPONENT[mask]] = p * float(count) + return ProblemHamiltonian( + matrix=np.diag(diag), + domain="propositional", + metadata={"problem_id": problem.problem_id, "penalty": p, "n_atoms": problem.n_atoms}, + ) + + +def uniform_assignment_state(problem: PropositionalProblem) -> np.ndarray: + """Unit uniform superposition over the problem's assignment components. + + Uses the algebra-derived blade signs so every assignment basis state enters + with amplitude +1/√(2^k) on the CANONICAL blade orientation. + """ + k = problem.n_atoms + psi = np.zeros(N_COMPONENTS, dtype=np.float64) + amp = 1.0 / float(np.sqrt(1 << k)) + for mask in range(1 << k): + psi[_SUBSET_COMPONENT[mask]] = amp * _SUBSET_SIGN[mask] + return psi + + +# --- Relaxation (imaginary-time semigroup; deviation D-1) --------------------------- + + +@dataclass(frozen=True, slots=True) +class RelaxationCertificate: + """Convergence evidence for one relaxation run (D-2: certified, not assumed). + + ``psi_digest`` binds the certificate to the exact final state it describes + (byte digest of ψ_steady) — the egress gate refuses a certificate presented + with any other state, so convergence evidence cannot be borrowed. + """ + + hamiltonian_id: str + domain: str + dt: float + tol: float + max_steps: int + steps_taken: int + ground_energy: float + achieved_energy: float + spectral_gap: float + eigen_residual: float + energy_monotone: bool + converged: bool + reason: str + psi_digest: str + certificate_id: str = "" + + def __post_init__(self) -> None: + payload = { + "hamiltonian_id": self.hamiltonian_id, + "domain": self.domain, + "dt": repr(float(self.dt)), + "tol": repr(float(self.tol)), + "max_steps": int(self.max_steps), + "steps_taken": int(self.steps_taken), + "ground_energy": repr(float(self.ground_energy)), + "achieved_energy": repr(float(self.achieved_energy)), + "spectral_gap": repr(float(self.spectral_gap)), + "eigen_residual": repr(float(self.eigen_residual)), + "energy_monotone": bool(self.energy_monotone), + "converged": bool(self.converged), + "reason": self.reason, + "psi_digest": self.psi_digest, + } + object.__setattr__(self, "certificate_id", _content_id(payload)) + + def as_dict(self) -> dict[str, Any]: + return { + "hamiltonian_id": self.hamiltonian_id, + "domain": self.domain, + "dt": float(self.dt), + "tol": float(self.tol), + "max_steps": int(self.max_steps), + "steps_taken": int(self.steps_taken), + "ground_energy": float(self.ground_energy), + "achieved_energy": float(self.achieved_energy), + "spectral_gap": float(self.spectral_gap), + "eigen_residual": float(self.eigen_residual), + "energy_monotone": bool(self.energy_monotone), + "converged": bool(self.converged), + "reason": self.reason, + "psi_digest": self.psi_digest, + "certificate_id": self.certificate_id, + } + + +@dataclass(frozen=True, slots=True) +class RelaxationResult: + psi_steady: np.ndarray + certificate: RelaxationCertificate + + def __post_init__(self) -> None: + arr = np.asarray(self.psi_steady, dtype=np.float64).copy() + arr.setflags(write=False) + object.__setattr__(self, "psi_steady", arr) + + +def _spectral_gap(evals: np.ndarray, tol: float) -> tuple[float, float, float]: + """(λ0, gap, energy_tol) with the degeneracy cluster ⊆ the acceptance window. + + Capping ``deg_tol`` at ``energy_tol`` keeps the certificate internally + consistent: an eigenvalue counted into the ground cluster is never refused + by the energy check, and ``gap`` is the honest rate-limiting gap (a split + just above ``energy_tol`` is reported, not absorbed). + """ + lam0 = float(evals[0]) + energy_tol = float(tol) * max(1.0, abs(lam0)) + deg_tol = min(1e-9 * max(1.0, abs(lam0)) + 1e-12, energy_tol) + above = evals[evals > lam0 + deg_tol] + gap = float(above[0] - lam0) if above.size else 0.0 + return lam0, gap, energy_tol + + +def relax_to_ground( + psi0: np.ndarray, + hamiltonian: ProblemHamiltonian, + *, + dt: float = 1.0, + max_steps: int = 512, + tol: float = 1e-10, + require_converged: bool = True, +) -> RelaxationResult: + """Deterministic imaginary-time relaxation to the ground eigenspace of H. + + ``ψ ← normalize(exp(−(H−λ0)·dt)·ψ)`` — normalized power iteration on the + dissipative semigroup (D-1; the λ0 shift only rescales, dynamics + identical). Along the iteration the Rayleigh energy is non-increasing (a + falsifiable physics invariant, recorded as ``energy_monotone``). + + Converged means ``‖Hψ − Eψ‖ ≤ tol`` AND ``E − λ0 ≤ tol·max(1,|λ0|)`` AND, + when the spectral gap is positive, ``E − λ0 ≤ tol·gap`` — the excited-space + weight of ψ is bounded by ``(E−λ0)/gap``, so the third check certifies + ground weight ≥ 1−tol instead of trusting an energy window the spectrum + may not resolve. A start orthogonal to the ground space settles in an + excited eigenspace with a small residual and is refused as + ``excited_eigenspace``; a state whose energy sits inside the window while + the gap is below the requested resolution is refused as + ``spectral_gap_below_tolerance``, never mis-certified. Degenerate ground + spaces (gap 0 after clustering) converge to the normalized projection of + ψ0 (input-dependent decoding within the solution space). Fail-closed: + non-finite input/iterates and non-unit ψ0 raise typed errors; nothing is + repaired. + """ + psi = _as_psi(psi0, "ψ0", error=RelaxationInputError).copy() + unit_err = abs(float(np.linalg.norm(psi)) - 1.0) + if unit_err > _UNIT_TOL: + raise RelaxationInputError("psi0_not_normalized", norm_residual=unit_err) + dt_f = float(dt) + if not np.isfinite(dt_f) or dt_f <= 0.0: + raise RelaxationInputError("dt_not_positive", dt=dt_f) + steps = int(max_steps) + if steps < 1: + raise RelaxationInputError("max_steps_not_positive", max_steps=steps) + tol_f = float(tol) + if not np.isfinite(tol_f) or tol_f <= 0.0: + raise RelaxationInputError("tol_not_positive", tol=tol_f) + + H_mat = hamiltonian.matrix + if hamiltonian.is_diagonal: + # Exact spectrum from the diagonal — no LAPACK on the propositional path. + diag = np.diagonal(H_mat).copy() + lam0, gap, energy_tol = _spectral_gap(np.sort(diag), tol_f) + decay = np.exp(-(diag - lam0) * dt_f) + + def step(v: np.ndarray) -> np.ndarray: + return decay * v + + def apply_h(v: np.ndarray) -> np.ndarray: + return diag * v + + else: + evals_full, evecs_full = np.linalg.eigh(H_mat) + lam0, gap, energy_tol = _spectral_gap(evals_full, tol_f) + propagator = evecs_full @ np.diag(np.exp(-(evals_full - lam0) * dt_f)) @ evecs_full.T + + def step(v: np.ndarray) -> np.ndarray: + return propagator @ v + + def apply_h(v: np.ndarray) -> np.ndarray: + return H_mat @ v + + def _measure(v: np.ndarray) -> tuple[float, float]: + hv = apply_h(v) + energy = float(v @ hv) + residual = float(np.linalg.norm(hv - energy * v)) + return energy, residual + + def _certified(energy: float, residual: float) -> bool: + near_ground = (energy - lam0) <= energy_tol + gap_resolved = gap == 0.0 or (energy - lam0) <= tol_f * gap + return residual <= tol_f and near_ground and gap_resolved + + energies: list[float] = [] + energy, residual = _measure(psi) + energies.append(energy) + steps_taken = 0 + for _ in range(steps): + if _certified(energy, residual): + break + psi = step(psi) + if not np.all(np.isfinite(psi)): + # Defensive only: decay/propagator entries are exp(−x) with x ≥ 0. + raise RelaxationNumericalFailure("non_finite_iterate", steps_taken=steps_taken) + norm = float(np.linalg.norm(psi)) + if norm < _NEAR_ZERO: + raise RelaxationNumericalFailure("iterate_collapsed", steps_taken=steps_taken) + psi = psi / norm + steps_taken += 1 + energy, residual = _measure(psi) + energies.append(energy) + + monotone = all( + energies[i + 1] <= energies[i] + 1e-9 * max(1.0, abs(energies[i])) + for i in range(len(energies) - 1) + ) + residual_ok = residual <= tol_f + at_ground = (energy - lam0) <= energy_tol + converged = _certified(energy, residual) + if converged: + reason = "ground_state_certified" + elif residual_ok and not at_ground: + reason = "excited_eigenspace" + elif residual_ok: + reason = "spectral_gap_below_tolerance" + else: + reason = "max_steps_exhausted" + + certificate = RelaxationCertificate( + hamiltonian_id=hamiltonian.hamiltonian_id, + domain=hamiltonian.domain, + dt=dt_f, + tol=tol_f, + max_steps=steps, + steps_taken=steps_taken, + ground_energy=lam0, + achieved_energy=energy, + spectral_gap=gap, + eigen_residual=residual, + energy_monotone=monotone, + converged=converged, + reason=reason, + psi_digest=_psi_digest(psi), + ) + if not converged and require_converged: + raise RelaxationNotConverged( + reason, + certificate, + achieved_energy=energy, + ground_energy=lam0, + eigen_residual=residual, + ) + return RelaxationResult(psi_steady=psi, certificate=certificate) + + +# --- Propositional verdicts (exact spectrum path) ----------------------------------- + + +@dataclass(frozen=True, slots=True) +class PropositionalEntailmentVerdict: + """Entailment via UNSAT(premises ∧ ¬conclusion) on the SAME compiled H family. + + ``satisfiable_premises=False`` discloses vacuous (ex falso) entailment. + Distinct name from ``generate.proof_chain.EntailmentVerdict`` (the ROBDD + flagship) — that is the independent gold this domain is scored against. + """ + + entailed: bool + satisfiable_premises: bool + ground_energy_premises: float + ground_energy_augmented: float + penalty: float + verdict_id: str = "" + + def __post_init__(self) -> None: + object.__setattr__( + self, + "verdict_id", + _content_id( + { + "entailed": bool(self.entailed), + "satisfiable_premises": bool(self.satisfiable_premises), + "ge_premises": repr(float(self.ground_energy_premises)), + "ge_augmented": repr(float(self.ground_energy_augmented)), + "penalty": repr(float(self.penalty)), + } + ), + ) + + +def _in_domain_ground_energy(problem: PropositionalProblem, *, penalty: float) -> float: + counts = _falsification_counts(problem) + return float(penalty) * float(min(counts)) + + +def propositional_entails( + premises: PropositionalProblem, + conclusion: Clause, + *, + penalty: float = 1.0, +) -> PropositionalEntailmentVerdict: + """premises ⊨ (⋁ conclusion) iff premises ∧ ¬conclusion is UNSAT. + + ¬conclusion compiles to unit clauses over the SAME atom set (unknown atoms + are refused — the v1 domain is closed-vocabulary). The verdict reads exact + integer ground energies of the compiled Hamiltonians; relaxation is the + constructive decoder of the same operators, so what is asserted and what + relaxes are one object. + """ + p = float(penalty) + if not np.isfinite(p) or p <= 0.0: + raise HamiltonianCompileError("penalty_not_positive", penalty=p) + lits = tuple((str(a), bool(pol)) for a, pol in conclusion) + if not lits: + raise HamiltonianCompileError("empty_conclusion") + for atom, _ in lits: + if atom not in premises.atoms: + raise HamiltonianCompileError("unknown_atom_in_conclusion", atom=atom) + negation_units: tuple[Clause, ...] = tuple(((atom, not pol),) for atom, pol in lits) + augmented = PropositionalProblem( + atoms=premises.atoms, + clauses=premises.clauses + negation_units, + ) + ge_premises = _in_domain_ground_energy(premises, penalty=p) + ge_augmented = _in_domain_ground_energy(augmented, penalty=p) + return PropositionalEntailmentVerdict( + entailed=bool(ge_augmented > 0.0), + satisfiable_premises=bool(ge_premises == 0.0), + ground_energy_premises=ge_premises, + ground_energy_augmented=ge_augmented, + penalty=p, + ) + + +# --- Egress (ADR-0243 §2.3) ---------------------------------------------------------- + + +@dataclass(frozen=True, slots=True) +class CrystallizationProposal: + """Proposal-only cold-state artifact (D-5 / I-03). NEVER a vault write. + + ``epistemic_status`` is pinned to ``"SPECULATIVE"`` by the type itself; + ratification and any COHERENT promotion live outside this module, behind + the one-mutation-path. + """ + + proposal_id: str + epistemic_status: str + psi_digest: str + certificate_id: str + decision: CrystallizationDecision + adr_refs: tuple[str, ...] = ("ADR-0243", "ADR-0241") + + def __post_init__(self) -> None: + if self.epistemic_status != _SPECULATIVE: + raise CognitiveLifecycleError( + "proposal_must_be_speculative", epistemic_status=self.epistemic_status + ) + + def as_dict(self) -> dict[str, Any]: + return { + "proposal_id": self.proposal_id, + "epistemic_status": self.epistemic_status, + "psi_digest": self.psi_digest, + "certificate_id": self.certificate_id, + "decision": self.decision.as_dict(), + "adr_refs": list(self.adr_refs), + } + + +@dataclass(frozen=True, slots=True) +class EgressVerdict: + """Composed egress verdict (ADR-0243 §2.3, corrected per pin SD-A). + + ``admitted`` = unit amplitude density + a converged certificate BOUND to + this exact ψ (``certificate.psi_digest`` must match the presented state + byte-for-byte; a borrowed certificate refuses as + ``certificate_state_mismatch``, so no proposal can pair a state with + foreign convergence evidence). ``versor_closed`` (the ADR's R_GoldTether + ≤ ε) ROUTES — it is required on the crystallization path (only closed + versors may SPECULATIVE-seal) and reported on all paths; demanding it of + multi-mode superpositions would reject every legitimate interference + state. + """ + + admitted: bool + reason: str + route: str # refused | readback_eligible | crystallization_proposal | hold + unit_norm_residual: float + versor_residual: float + versor_closed: bool + energy_class: EnergyClass + energy_profile: EnergyProfile + proposal: CrystallizationProposal | None + + +def egress_gate( + psi_steady: np.ndarray, + certificate: RelaxationCertificate, + *, + epsilon_drift: float = _EPSILON_DRIFT, + manifold: WaveManifold | None = None, + operator: FieldEnergyOperator | None = None, + **energy_kwargs: object, +) -> EgressVerdict: + """Thermodynamic egress: admit → classify → route (E0/E1 cold, E3/E4 hot). + + Structural energy axes (convergence, activation, aspect) are + caller-supplied via ``energy_kwargs`` — never invented here; the + coherence residual is measured on ψ by the energy boundary itself. + Malformed states raise; legitimate bad states get ``admitted=False``. + """ + arr = _as_psi(psi_steady, "ψ_steady", error=EgressValidationError) + m = manifold if manifold is not None else WaveManifold(epsilon_drift=float(epsilon_drift)) + unit_norm_residual = abs(float(np.linalg.norm(arr)) - 1.0) + versor_residual = float(m.measure_unitary_residual(arr)) + versor_closed = versor_residual <= float(epsilon_drift) + psi_dig = _psi_digest(arr) + profile = energy_profile_from_wave( + arr, operator=operator, manifold=m, epsilon_drift=float(epsilon_drift), **energy_kwargs + ) + energy_class = profile.energy_class + + if unit_norm_residual > float(epsilon_drift): + admitted, reason = False, "amplitude_density_not_unit" + elif psi_dig != certificate.psi_digest: + admitted, reason = False, "certificate_state_mismatch" + elif not certificate.converged: + admitted, reason = False, f"relaxation_not_certified:{certificate.reason}" + else: + admitted, reason = True, "admitted" + + proposal: CrystallizationProposal | None = None + if not admitted: + route = "refused" + elif energy_class in (EnergyClass.E3, EnergyClass.E4): + route = "readback_eligible" + elif energy_class.vault_candidate: + decision = crystallization_for_holographic_seal( + arr, + epsilon_drift=float(epsilon_drift), + manifold=m, + operator=operator, + **energy_kwargs, + ) + if decision.may_speculative_seal: + route = "crystallization_proposal" + proposal = CrystallizationProposal( + proposal_id="crystal-" + + _content_id( + { + "psi": psi_dig, + "certificate": certificate.certificate_id, + "decision": decision.as_dict(), + } + ), + epistemic_status=_SPECULATIVE, + psi_digest=psi_dig, + certificate_id=certificate.certificate_id, + decision=decision, + ) + else: + route = "hold" # cold but not crystalline (e.g. open superposition) + else: + route = "hold" # E2 mid-band: neither vault-cold nor readback-hot + + return EgressVerdict( + admitted=admitted, + reason=reason, + route=route, + unit_norm_residual=unit_norm_residual, + versor_residual=versor_residual, + versor_closed=versor_closed, + energy_class=energy_class, + energy_profile=profile, + proposal=proposal, + ) + + +# --- Composed lifecycle --------------------------------------------------------------- + + +@dataclass(frozen=True, slots=True) +class LifecycleOutcome: + ingress: IngressWavePacket + relaxation: RelaxationResult + verdict: EgressVerdict + outcome_id: str = "" + + def __post_init__(self) -> None: + object.__setattr__( + self, + "outcome_id", + _content_id( + { + "ingress": self.ingress.packet_digest, + "certificate": self.relaxation.certificate.certificate_id, + "route": self.verdict.route, + "psi": _psi_digest(self.relaxation.psi_steady), + } + ), + ) + + +class CognitiveLifecycleEngine: + """Thin deterministic composer over the pure lifecycle functions. + + Name matches the ADR §3 sketch for traceability; the implementation is + the corrected one (pins SD-A/SD-B/SD-C; deviations D-1…D-5). + """ + + def __init__(self, *, epsilon_drift: float = _EPSILON_DRIFT) -> None: + self.epsilon_drift = float(epsilon_drift) + self.manifold = WaveManifold(epsilon_drift=self.epsilon_drift) + + def ingest_context(self, packets: Sequence[PacketLike], domain_id: str) -> IngressWavePacket: + return ingest_context(packets, domain_id) + + def relax( + self, + ingress: IngressWavePacket, + hamiltonian: ProblemHamiltonian, + **kwargs: Any, + ) -> RelaxationResult: + return relax_to_ground(ingress.psi, hamiltonian, **kwargs) + + def egress( + self, + psi_steady: np.ndarray, + certificate: RelaxationCertificate, + **energy_kwargs: Any, + ) -> EgressVerdict: + return egress_gate( + psi_steady, + certificate, + epsilon_drift=self.epsilon_drift, + manifold=self.manifold, + **energy_kwargs, + ) + + def solve( + self, + packets: Sequence[PacketLike], + domain_id: str, + hamiltonian: ProblemHamiltonian, + *, + dt: float = 1.0, + max_steps: int = 512, + tol: float = 1e-10, + energy_inputs: Mapping[str, object] | None = None, + ) -> LifecycleOutcome: + """Ingress → relax → egress. Fail-closed at every stage (typed errors).""" + ingress = self.ingest_context(packets, domain_id) + result = relax_to_ground(ingress.psi, hamiltonian, dt=dt, max_steps=max_steps, tol=tol) + verdict = self.egress( + result.psi_steady, result.certificate, **dict(energy_inputs or {}) + ) + return LifecycleOutcome(ingress=ingress, relaxation=result, verdict=verdict) + + +__all__ = [ + "CognitiveLifecycleEngine", + "CognitiveLifecycleError", + "CrystallizationProposal", + "EgressValidationError", + "EgressVerdict", + "HamiltonianCompileError", + "IngressDegenerate", + "IngressWavePacket", + "LifecycleOutcome", + "ProblemHamiltonian", + "PropositionalEntailmentVerdict", + "PropositionalProblem", + "RelaxationCertificate", + "RelaxationInputError", + "RelaxationNotConverged", + "RelaxationNumericalFailure", + "RelaxationResult", + "assignment_component_index", + "compile_propositional", + "compile_quadratic_well", + "egress_gate", + "ingest_context", + "propositional_entails", + "relax_to_ground", + "uniform_assignment_state", +] diff --git a/docs/plans/adr-0243-implementation-plan.md b/docs/plans/adr-0243-implementation-plan.md index 77ae2f48..6b43ef1d 100644 --- a/docs/plans/adr-0243-implementation-plan.md +++ b/docs/plans/adr-0243-implementation-plan.md @@ -104,6 +104,20 @@ prototype must **never** be ported verbatim (precedent: PR #52 sketch-defect pin D-5; E3/E4 → readback-eligible). Ingress delegates to `sensorium_wave_feed` / `WaveManifold` — no duplication. TDD; ground-state property test vs. direct eigensolve; determinism digests. + - *Phase 2 verification record:* adversarial finder/verifier workflow over + the module (most verify agents were lost to a session limit; every + surviving finding was reproduced in-tree before acting). Two hardenings + landed beyond the plan text: (1) `RelaxationCertificate.psi_digest` binds + convergence evidence to the exact certified state and `egress_gate` + refuses borrowed certificates (`certificate_state_mismatch`) — closes a + false-provenance `CrystallizationProposal`; (2) certification additionally + requires the spectral gap be resolvable at the requested tolerance + (excited weight ≤ (E−λ0)/gap ≤ tol, refusal + `spectral_gap_below_tolerance`), and the degeneracy cluster is capped at + the acceptance window so the certificate reports the honest rate-limiting + gap — closes a zero-ground-overlap mis-certification reachable at loose + `tol`. Dense-branch refusals, the E2 hold route, iterate-collapse, and + hardcoded canonical entailment verdicts are pinned in tests. - **Phase 3 — wiring lanes** (three independent PRs after Phase 2 API lands): - **Lane A (§2.4; closes caveat 2):** wire `is_discovery_eligible` + `cross_band_discovery_gate` into the contemplation runner's existing sink; diff --git a/tests/test_adr_0243_cognitive_lifecycle.py b/tests/test_adr_0243_cognitive_lifecycle.py new file mode 100644 index 00000000..497286d1 --- /dev/null +++ b/tests/test_adr_0243_cognitive_lifecycle.py @@ -0,0 +1,662 @@ +"""ADR-0243 §2 cognitive lifecycle — ingress → relaxation → egress (Tier-2). + +Authority: docs/plans/adr-0243-implementation-plan.md §5 Phase 2. +Companion pins: tests/test_adr_0243_sketch_defect_pins.py (SD-A/SD-B/SD-C). + +Gold is INDEPENDENT of the module under test: propositional truth is a +truth-table evaluator written here; ground states are cross-checked against +direct spectral projection. Deterministic fixtures only — no random spinors. +""" + +from __future__ import annotations + +import ast +import hashlib +import json +import subprocess +import sys +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 core.physics.cognitive_lifecycle import ( + CognitiveLifecycleEngine, + CognitiveLifecycleError, + CrystallizationProposal, + EgressValidationError, + HamiltonianCompileError, + IngressDegenerate, + ProblemHamiltonian, + PropositionalProblem, + RelaxationInputError, + RelaxationNotConverged, + RelaxationNumericalFailure, + assignment_component_index, + compile_propositional, + compile_quadratic_well, + egress_gate, + ingest_context, + propositional_entails, + relax_to_ground, + uniform_assignment_state, +) +from core.physics.sensorium_wave_feed import fake_deterministic_packet +from core.physics.wave_energy_boundary import crystallization_for_holographic_seal +from core.physics.wave_manifold import WaveManifold + +_ROOT = Path(__file__).resolve().parents[1] + + +def _onehot(i: int) -> np.ndarray: + v = np.zeros(N_COMPONENTS, dtype=np.float64) + v[i] = 1.0 + return v + + +def _unit(v: np.ndarray) -> np.ndarray: + return v / np.linalg.norm(v) + + +# --- Independent gold --------------------------------------------------------- + + +def _truth_table_counts(atoms, clauses) -> list[int]: + """Clauses falsified per assignment mask — independent of the module.""" + index = {a: i for i, a in enumerate(atoms)} + counts = [] + for mask in range(1 << len(atoms)): + c = 0 + for clause in clauses: + if not any(bool(mask >> index[a] & 1) == pol for a, pol in clause): + c += 1 + counts.append(c) + return counts + + +def _entails_gold(atoms, premises, conclusion) -> bool: + """Semantic entailment by truth table — independent of the module.""" + index = {a: i for i, a in enumerate(atoms)} + for mask in range(1 << len(atoms)): + model = all( + any(bool(mask >> index[a] & 1) == pol for a, pol in clause) + for clause in premises + ) + if model and not any( + bool(mask >> index[a] & 1) == pol for a, pol in conclusion + ): + return False + return True + + +_ATOMS3 = ("a", "b", "c") +_PREMISES3 = ( + (("a", True), ("b", True)), + (("a", False), ("c", True)), + (("b", False), ("c", True)), +) + + +# --- Blade lattice ↔ assignment lattice ---------------------------------------- + + +def test_assignment_component_map_is_bijective_and_grade_consistent(): + indices = [assignment_component_index(m) for m in range(32)] + assert sorted(indices) == list(range(32)) + assert assignment_component_index(0) == 0 # ∅ ↔ scalar + for i in range(5): + assert assignment_component_index(1 << i) == 1 + i # {e_i} ↔ vector slot + assert assignment_component_index(0b11111) == 31 # full set ↔ pseudoscalar + + +def test_assignment_component_index_out_of_range_refused(): + with pytest.raises(HamiltonianCompileError): + assignment_component_index(32) + + +# --- Ingress -------------------------------------------------------------------- + + +def test_ingest_context_superposes_normalizes_and_digests(): + packets = [ + fake_deterministic_packet("audio", angle=0.3, plane=6), + fake_deterministic_packet("vision", angle=0.4, plane=7), + ] + ingress = ingest_context(packets, "demo") + assert abs(float(np.linalg.norm(ingress.psi)) - 1.0) < 1e-12 + assert ingress.modality_ids == ("audio", "vision") + again = ingest_context(packets, "demo") + assert ingress.packet_digest == again.packet_digest + assert np.array_equal(ingress.psi, again.psi) + with pytest.raises(ValueError): + ingress.psi[0] = 5.0 # frozen read-only field + + +def test_ingest_context_refuses_empty_and_degenerate(): + with pytest.raises(ValueError): + ingest_context([], "demo") # delegation: superpose_packets refuses empty + p = fake_deterministic_packet("audio") + anti = {"modality_id": "anti", "coefficients": -p.coefficients} + with pytest.raises(IngressDegenerate): + ingest_context([p, anti], "demo") # destructive cancellation + with pytest.raises(IngressDegenerate): + ingest_context([p], " ") # empty domain id + + +# --- Hamiltonian compilers ------------------------------------------------------- + + +def test_quadratic_well_spectrum_and_refusals(): + target = _onehot(0) + ham = compile_quadratic_well(target, curvature=2.0) + evals, evecs = np.linalg.eigh(ham.matrix) + assert abs(evals[0]) < 1e-12 + assert np.allclose(evals[1:], 2.0, atol=1e-12) + assert abs(abs(float(evecs[:, 0] @ target)) - 1.0) < 1e-9 + with pytest.raises(HamiltonianCompileError): + compile_quadratic_well(2.0 * target) # non-unit target — no repair + with pytest.raises(HamiltonianCompileError): + compile_quadratic_well(target, curvature=0.0) + bad = target.copy() + bad[3] = np.nan + with pytest.raises(HamiltonianCompileError): + compile_quadratic_well(bad) + + +def test_problem_hamiltonian_validation_and_immutability(): + asym = np.zeros((N_COMPONENTS, N_COMPONENTS)) + asym[0, 1] = 1.0 + with pytest.raises(HamiltonianCompileError): + ProblemHamiltonian(matrix=asym, domain="test") + with pytest.raises(HamiltonianCompileError): + ProblemHamiltonian(matrix=np.eye(16), domain="test") # wrong shape + nan_mat = np.eye(N_COMPONENTS) + nan_mat[4, 4] = np.nan + with pytest.raises(HamiltonianCompileError): + ProblemHamiltonian(matrix=nan_mat, domain="test") + ham = ProblemHamiltonian(matrix=np.eye(N_COMPONENTS), domain="test") + assert ham.is_diagonal + assert ham.hamiltonian_id + with pytest.raises(ValueError): + ham.matrix[0, 0] = 9.0 # read-only + dense = compile_quadratic_well(_unit(_onehot(0) + _onehot(6))) + assert not dense.is_diagonal + + +def test_propositional_diag_matches_independent_truth_table_gold(): + problem = PropositionalProblem(atoms=_ATOMS3, clauses=_PREMISES3) + ham = compile_propositional(problem, penalty=1.0) + assert ham.is_diagonal + diag = np.diagonal(ham.matrix) + gold = _truth_table_counts(_ATOMS3, _PREMISES3) + mapped = set() + for mask, count in enumerate(gold): + idx = assignment_component_index(mask) + mapped.add(idx) + assert diag[idx] == float(count) + out_of_domain = 1.0 * (len(_PREMISES3) + 1) + for idx in set(range(N_COMPONENTS)) - mapped: + assert diag[idx] == out_of_domain + + +def test_propositional_problem_validation(): + with pytest.raises(HamiltonianCompileError): + PropositionalProblem(atoms=("a",) * 6, clauses=()) # >5 and duplicate + with pytest.raises(HamiltonianCompileError): + PropositionalProblem(atoms=("a", "a"), clauses=()) + with pytest.raises(HamiltonianCompileError): + PropositionalProblem(atoms=("a",), clauses=((),)) # empty clause + with pytest.raises(HamiltonianCompileError): + PropositionalProblem(atoms=("a",), clauses=((("z", True),),)) + with pytest.raises(HamiltonianCompileError): + PropositionalProblem(atoms=("a",), clauses=((("a", True), ("a", True)),)) + p1 = PropositionalProblem(atoms=("a", "b"), clauses=((("a", True),),)) + p2 = PropositionalProblem(atoms=("a", "b"), clauses=((("b", True),),)) + assert p1.problem_id and p1.problem_id != p2.problem_id + with pytest.raises(HamiltonianCompileError): + compile_propositional(p1, penalty=-1.0) + + +# --- Relaxation -------------------------------------------------------------------- + + +def test_relaxation_decodes_quadratic_well_target_dense_path(): + target = np.asarray(make_rotor_from_angle(0.3, bivector_idx=6), dtype=np.float64) + ham = compile_quadratic_well(target, curvature=1.0) + psi0 = _unit(target + 0.5 * _onehot(2) + 0.3 * _onehot(9)) + result = relax_to_ground(psi0, ham) + cert = result.certificate + assert cert.converged and cert.reason == "ground_state_certified" + assert abs(cert.ground_energy) < 1e-9 + assert abs(cert.spectral_gap - 1.0) < 1e-9 + assert cert.steps_taken >= 1 + # Independent gold: normalized spectral projection onto the ground space. + proj = target * float(target @ psi0) + gold = proj / np.linalg.norm(proj) + assert np.allclose(result.psi_steady, gold, atol=1e-6) + assert abs(abs(float(result.psi_steady @ target)) - 1.0) < 1e-8 + + +def test_relaxation_decodes_propositional_model_set_uniform_start(): + problem = PropositionalProblem( + atoms=("a", "b"), + clauses=((("a", True), ("b", True)), (("a", False), ("b", True))), + ) + gold_counts = _truth_table_counts(problem.atoms, problem.clauses) + models = [m for m, c in enumerate(gold_counts) if c == 0] + assert models == [2, 3] # b=True with a free — independent check + ham = compile_propositional(problem) + result = relax_to_ground(uniform_assignment_state(problem), ham) + cert = result.certificate + assert cert.converged + assert cert.ground_energy == 0.0 # exact on the diagonal path + assert cert.spectral_gap == 1.0 # min nonzero falsification count × penalty + psi = result.psi_steady + model_components = {assignment_component_index(m) for m in models} + for idx in range(N_COMPONENTS): + if idx in model_components: + assert abs(abs(psi[idx]) - 1.0 / np.sqrt(2.0)) < 1e-9 + else: + assert abs(psi[idx]) < 1e-9 + + +def test_relaxation_degenerate_ground_preserves_input_weighting(): + problem = PropositionalProblem( + atoms=("a", "b"), + clauses=((("a", True), ("b", True)), (("a", False), ("b", True))), + ) + ham = compile_propositional(problem) + c2, c3 = assignment_component_index(2), assignment_component_index(3) + psi0 = np.zeros(N_COMPONENTS) + psi0[c2], psi0[c3] = 0.8, 0.6 # unit by construction + result = relax_to_ground(psi0, ham) + assert abs(abs(result.psi_steady[c2]) - 0.8) < 1e-9 + assert abs(abs(result.psi_steady[c3]) - 0.6) < 1e-9 + + +def test_relaxation_energy_is_monotone_nonincreasing(): + target = np.asarray(make_rotor_from_angle(0.3, bivector_idx=6), dtype=np.float64) + psi0 = _unit(target + 0.5 * _onehot(2) + 0.3 * _onehot(9)) + result = relax_to_ground(psi0, compile_quadratic_well(target)) + assert result.certificate.energy_monotone + + +def test_relaxation_refuses_orthogonal_start_as_excited_eigenspace(): + ham = compile_quadratic_well(_onehot(0), curvature=1.0) + with pytest.raises(RelaxationNotConverged) as exc_info: + relax_to_ground(_onehot(6), ham) # exactly orthogonal to the ground space + cert = exc_info.value.certificate + assert cert.reason == "excited_eigenspace" + assert not cert.converged + assert abs(cert.achieved_energy - 1.0) < 1e-9 # settled at the excited level + + +def test_relaxation_max_steps_exhaustion_and_nonconverged_return_path(): + diag = np.ones(N_COMPONENTS) + diag[0], diag[1] = 0.0, 1e-6 # tiny gap — cannot converge in 3 steps + ham = ProblemHamiltonian(matrix=np.diag(diag), domain="tiny_gap") + psi0 = _unit(_onehot(0) + _onehot(1)) + with pytest.raises(RelaxationNotConverged) as exc_info: + relax_to_ground(psi0, ham, max_steps=3) + assert exc_info.value.certificate.reason == "max_steps_exhausted" + result = relax_to_ground(psi0, ham, max_steps=3, require_converged=False) + assert not result.certificate.converged + + +def test_relaxation_input_refusals_fail_closed(): + ham = compile_quadratic_well(_onehot(0)) + with pytest.raises(RelaxationInputError): + relax_to_ground(0.5 * _onehot(0), ham) # non-unit ψ0 — no hidden repair + nan_psi = _onehot(0).copy() + nan_psi[7] = np.nan + with pytest.raises(RelaxationInputError): + relax_to_ground(nan_psi, ham) + with pytest.raises(RelaxationInputError): + relax_to_ground(_onehot(0), ham, dt=0.0) + with pytest.raises(RelaxationInputError): + relax_to_ground(_onehot(0), ham, max_steps=0) + with pytest.raises(RelaxationInputError): + relax_to_ground(_onehot(0), ham, tol=0.0) + + +def test_relaxation_refuses_unresolvable_spectral_gap_but_certifies_true_ground(): + """A gap below the requested tolerance must never mis-certify (audit F4a). + + With gap 1e-7 and tol 1e-6 the energy window alone cannot separate ground + from first-excited: the pure excited e-state passed both legacy checks with + ZERO ground overlap. The excited-weight check refuses it — while the TRUE + ground state (energy exactly λ0) still certifies under the same H and tol. + """ + diag = np.ones(N_COMPONENTS) + diag[0], diag[1] = 0.0, 1e-7 + ham = ProblemHamiltonian(matrix=np.diag(diag), domain="sub_tol_gap") + with pytest.raises(RelaxationNotConverged) as exc_info: + relax_to_ground(_onehot(1), ham, tol=1e-6) + cert = exc_info.value.certificate + assert cert.reason == "spectral_gap_below_tolerance" + assert not cert.converged + assert cert.spectral_gap == 1e-7 # exact on the diagonal path + ground = relax_to_ground(_onehot(0), ham, tol=1e-6) + assert ground.certificate.converged + assert ground.certificate.reason == "ground_state_certified" + + +def test_relaxation_certificate_reports_rate_limiting_gap(): + """Cluster ⊆ acceptance window: the reported gap is the honest rate (audit F4b). + + A 5e-10 split sat inside the old 1e-9 degeneracy cluster, so the refusal + certificate claimed gap=1.0 while the energy check refused that very level. + The cluster is now capped at the acceptance window, so the certificate + reports the true rate-limiting split. + """ + diag = np.ones(N_COMPONENTS) + diag[0], diag[1] = 0.0, 5e-10 + ham = ProblemHamiltonian(matrix=np.diag(diag), domain="hairline_split") + with pytest.raises(RelaxationNotConverged) as exc_info: + relax_to_ground(_onehot(1), ham) + cert = exc_info.value.certificate + assert cert.reason == "excited_eigenspace" + assert cert.spectral_gap == 5e-10 # not the 1.0 the old cluster absorbed it into + + +def test_relaxation_dense_path_refusals_fail_closed(): + """The eigh/propagator branch must refuse honestly, not just decode happy paths. + + A projection-returning mutant with a fabricated converged certificate + passed the suite when refusals were exercised only on the diagonal branch + (audit F2a) — these two pin the dense branch's refusal mechanics. + """ + target = np.asarray(make_rotor_from_angle(0.3, bivector_idx=6), dtype=np.float64) + slow = compile_quadratic_well(target, curvature=1e-6) + assert not slow.is_diagonal + psi0 = _unit(target + 0.5 * _onehot(2)) + with pytest.raises(RelaxationNotConverged) as exc_info: + relax_to_ground(psi0, slow, max_steps=3) + assert exc_info.value.certificate.reason == "max_steps_exhausted" + + well = compile_quadratic_well(target, curvature=1.0) + assert not well.is_diagonal + with pytest.raises(RelaxationNotConverged) as exc_info: + relax_to_ground(_onehot(2), well, max_steps=8) # e2 ⊥ span(scalar, biv6) + cert = exc_info.value.certificate + assert cert.reason == "excited_eigenspace" + assert not cert.converged + + +def test_relaxation_iterate_collapse_raises_numerical_failure(): + """Underflow of every surviving component is a typed failure, not a repair.""" + diag = np.ones(N_COMPONENTS) + diag[0] = 0.0 + ham = ProblemHamiltonian(matrix=np.diag(diag), domain="collapse") + psi0 = np.zeros(N_COMPONENTS) + psi0[0], psi0[1] = 1e-13, 1.0 # ground weight below _NEAR_ZERO after one decay + with pytest.raises(RelaxationNumericalFailure) as exc_info: + relax_to_ground(_unit(psi0), ham, dt=100.0) + assert exc_info.value.reason == "iterate_collapsed" + + +def test_relaxation_is_bit_deterministic(): + target = np.asarray(make_rotor_from_angle(0.3, bivector_idx=6), dtype=np.float64) + ham = compile_quadratic_well(target) + psi0 = _unit(target + 0.5 * _onehot(2)) + r1 = relax_to_ground(psi0, ham) + r2 = relax_to_ground(psi0, ham) + assert np.array_equal(r1.psi_steady, r2.psi_steady) + assert r1.certificate.certificate_id == r2.certificate.certificate_id + + +# --- Propositional verdicts (exact spectrum path) ----------------------------------- + + +@pytest.mark.parametrize( + "conclusion", + [ + (("c", True),), + (("a", True),), + (("a", True), ("b", True)), + (("b", False),), + ], +) +def test_entailment_matches_independent_truth_table_gold(conclusion): + premises = PropositionalProblem(atoms=_ATOMS3, clauses=_PREMISES3) + verdict = propositional_entails(premises, conclusion) + assert verdict.entailed == _entails_gold(_ATOMS3, _PREMISES3, conclusion) + assert verdict.satisfiable_premises # these premises have models + assert verdict.verdict_id + + +def test_entailment_hardcoded_canonical_verdicts(): + """Hand-verified verdicts, hardcoded — no shared code shape with the module. + + The truth-table gold above uses the same literal-evaluation idiom as the + compiler (audit F2e), so these canonical cases pin absolute truth values. + """ + mp = PropositionalProblem( + atoms=("p", "q"), + clauses=((("p", False), ("q", True)), (("p", True),)), # p→q, p + ) + assert propositional_entails(mp, (("q", True),)).entailed is True # modus ponens + assert propositional_entails(mp, (("q", False),)).entailed is False + + disj = PropositionalProblem(atoms=("p", "q"), clauses=((("p", True), ("q", True)),)) + assert propositional_entails(disj, (("p", True),)).entailed is False # p∨q ⊭ p + + chain = PropositionalProblem( + atoms=("p", "q", "r"), + clauses=( + (("p", False), ("q", True)), # p→q + (("q", False), ("r", True)), # q→r + (("p", True),), + ), + ) + verdict = propositional_entails(chain, (("r", True),)) + assert verdict.entailed is True # hypothetical syllogism + assert verdict.satisfiable_premises is True + + +def test_entailment_vacuous_from_unsat_premises_is_disclosed(): + premises = PropositionalProblem( + atoms=("a",), clauses=((("a", True),), (("a", False),)) + ) + verdict = propositional_entails(premises, (("a", True),)) + assert verdict.entailed + assert not verdict.satisfiable_premises # ex falso — disclosed, not hidden + assert verdict.ground_energy_premises > 0.0 + + +def test_entailment_refuses_unknown_atom_and_empty_conclusion(): + premises = PropositionalProblem(atoms=("a",), clauses=((("a", True),),)) + with pytest.raises(HamiltonianCompileError): + propositional_entails(premises, (("z", True),)) + with pytest.raises(HamiltonianCompileError): + propositional_entails(premises, ()) + + +# --- Egress --------------------------------------------------------------------------- + + +def _crystalline_outcome(): + engine = CognitiveLifecycleEngine() + target = np.asarray(make_rotor_from_angle(0.3, bivector_idx=6), dtype=np.float64) + ham = compile_quadratic_well(target) + packets = [fake_deterministic_packet("audio", angle=0.25, plane=6)] + return engine, engine.solve(packets, "crystal-demo", ham) + + +def test_egress_routes_cold_closed_versor_to_crystallization_proposal(): + _engine, outcome = _crystalline_outcome() + verdict = outcome.verdict + assert verdict.admitted and verdict.reason == "admitted" + assert verdict.versor_closed + assert verdict.energy_class.vault_candidate + assert verdict.route == "crystallization_proposal" + proposal = verdict.proposal + assert proposal is not None + assert proposal.epistemic_status == "SPECULATIVE" + assert proposal.certificate_id == outcome.relaxation.certificate.certificate_id + assert proposal.decision.may_speculative_seal + json.dumps(proposal.as_dict()) # JSON-serializable artifact + + +def test_egress_routes_hot_state_to_readback_eligible(): + _engine, outcome = _crystalline_outcome() + verdict = egress_gate( + outcome.relaxation.psi_steady, + outcome.relaxation.certificate, + convergence_density=8, + activation_count=8, + current_cycle=1, + last_activation_cycle=1, + morphology_features={"mood": "imperative"}, + ) + assert verdict.admitted + assert verdict.energy_class.value in ("E3", "E4") + assert verdict.route == "readback_eligible" + assert verdict.proposal is None + + +def test_egress_holds_cold_open_superposition_without_proposal(): + problem = PropositionalProblem( + atoms=("a", "b"), + clauses=((("a", True), ("b", True)), (("a", False), ("b", True))), + ) + result = relax_to_ground(uniform_assignment_state(problem), compile_propositional(problem)) + verdict = egress_gate(result.psi_steady, result.certificate) + assert verdict.admitted + assert not verdict.versor_closed # interference state, not a versor + assert verdict.energy_class.vault_candidate + assert verdict.route == "hold" # cold but not crystalline: no proposal + assert verdict.proposal is None + + +def test_egress_refuses_unnormalized_and_uncertified_states(): + _engine, outcome = _crystalline_outcome() + cert = outcome.relaxation.certificate + scaled = 0.5 * outcome.relaxation.psi_steady + verdict = egress_gate(scaled, cert) + assert not verdict.admitted and verdict.route == "refused" + assert verdict.reason == "amplitude_density_not_unit" + + diag = np.ones(N_COMPONENTS) + diag[0], diag[1] = 0.0, 1e-6 + slow = ProblemHamiltonian(matrix=np.diag(diag), domain="tiny_gap") + nonconv = relax_to_ground( + _unit(_onehot(0) + _onehot(1)), slow, max_steps=3, require_converged=False + ) + verdict2 = egress_gate(nonconv.psi_steady, nonconv.certificate) + assert not verdict2.admitted and verdict2.route == "refused" + assert verdict2.reason.startswith("relaxation_not_certified") + + bad = outcome.relaxation.psi_steady.copy() + bad[3] = np.inf + with pytest.raises(EgressValidationError): + egress_gate(bad, cert) + with pytest.raises(EgressValidationError): + egress_gate(np.zeros(16), cert) # wrong shape — malformed, not refused + + +def test_egress_refuses_certificate_not_bound_to_state(): + """A borrowed converged certificate must not admit a foreign ψ (audit A). + + Before the binding, any unit state paired with any converged certificate + was admitted and could emit a CrystallizationProposal whose psi_digest and + certificate_id asserted a provenance that never existed. + """ + _engine, outcome = _crystalline_outcome() + psi = outcome.relaxation.psi_steady + cert = outcome.relaxation.certificate + # Independent gold for the binding: byte digest of the certified state. + gold_digest = hashlib.sha256( + np.ascontiguousarray(psi, dtype=np.float64).tobytes() + ).hexdigest()[:24] + assert cert.psi_digest == gold_digest + assert cert.as_dict()["psi_digest"] == gold_digest + + foreign = np.asarray(make_rotor_from_angle(1.1, bivector_idx=8), dtype=np.float64) + verdict = egress_gate(foreign, cert) # unit closed versor, never relaxed + assert not verdict.admitted + assert verdict.reason == "certificate_state_mismatch" + assert verdict.route == "refused" + assert verdict.proposal is None + + +def test_egress_holds_e2_midband_without_proposal(): + """The E2 else-branch routes to hold — neither vault-cold nor readback-hot.""" + _engine, outcome = _crystalline_outcome() + verdict = egress_gate( + outcome.relaxation.psi_steady, + outcome.relaxation.certificate, + convergence_density=8, + morphology_features={"aspect": "qatal"}, + ) + assert verdict.admitted + assert verdict.energy_class.value == "E2" + assert not verdict.energy_class.vault_candidate + assert verdict.route == "hold" + assert verdict.proposal is None + + +def test_crystallization_proposal_type_pins_speculative_status(): + rotor = np.asarray(make_rotor_from_angle(0.2, bivector_idx=6), dtype=np.float64) + decision = crystallization_for_holographic_seal(rotor) + with pytest.raises(CognitiveLifecycleError): + CrystallizationProposal( + proposal_id="crystal-x", + epistemic_status="COHERENT", # forbidden by the type itself (I-03) + psi_digest="d", + certificate_id="c", + decision=decision, + ) + + +def test_composed_lifecycle_outcome_is_deterministic(): + _e1, o1 = _crystalline_outcome() + _e2, o2 = _crystalline_outcome() + assert o1.outcome_id == o2.outcome_id + assert np.array_equal(o1.relaxation.psi_steady, o2.relaxation.psi_steady) + + +# --- Quarantine / structural pins ----------------------------------------------------- + + +def test_module_imports_no_vault_surface(): + """I-03 structural pin: the lifecycle can propose, never touch a vault.""" + src = (_ROOT / "core/physics/cognitive_lifecycle.py").read_text() + tree = ast.parse(src) + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + assert "vault" not in alias.name, alias.name + if isinstance(node, ast.ImportFrom) and node.module: + assert "vault" not in node.module, node.module + + +def test_barrel_export_is_lazy_and_resolves(): + probe = ( + "import sys;" + "import core.physics;" + "assert 'core.physics.cognitive_lifecycle' not in sys.modules, 'eager load';" + "core.physics.CognitiveLifecycleEngine;" + "assert 'core.physics.cognitive_lifecycle' in sys.modules;" + "print('ok')" + ) + result = subprocess.run( + [sys.executable, "-c", probe], + cwd=str(_ROOT), + capture_output=True, + text=True, + env={"PYTHONPATH": str(_ROOT), "PATH": ""}, + ) + assert result.returncode == 0, result.stderr[-2000:] + assert result.stdout.strip().endswith("ok") + + +def test_versor_closure_of_steady_state_reported_via_wave_manifold(): + """Egress residual is the canonical WaveManifold measurement, not a fork.""" + _engine, outcome = _crystalline_outcome() + manifold = WaveManifold() + assert outcome.verdict.versor_residual == pytest.approx( + manifold.measure_unitary_residual(outcome.relaxation.psi_steady), abs=0.0 + ) diff --git a/tests/test_serve_quarantine_transitive.py b/tests/test_serve_quarantine_transitive.py index cc254dd6..be22cd22 100644 --- a/tests/test_serve_quarantine_transitive.py +++ b/tests/test_serve_quarantine_transitive.py @@ -28,6 +28,7 @@ _BANNED = ( "core.physics.wave_energy_boundary", "core.physics.multi_scale_energy", "core.physics.sensorium_wave_feed", + "core.physics.cognitive_lifecycle", # ADR-0243 lifecycle — never serve # NOTE: core.physics.wave_manifold is intentionally excluded pending the # Joshua design ruling (goldtether delegates to it). Add it here if the # ruling is "quarantine wave_manifold for real". diff --git a/tests/test_third_door_cohesion.py b/tests/test_third_door_cohesion.py index 9a9eed13..a7a00ff4 100644 --- a/tests/test_third_door_cohesion.py +++ b/tests/test_third_door_cohesion.py @@ -70,6 +70,7 @@ def test_phase0_a04_serve_path_quarantines_wave_and_fibonacci(): "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 + "cognitive_lifecycle", # ADR-0243 ingress→relaxation→egress, never serve } banned_substrings = ( "holographic_vault", @@ -80,6 +81,7 @@ def test_phase0_a04_serve_path_quarantines_wave_and_fibonacci(): "wave_energy_boundary", "multi_scale_energy", "sensorium_wave_feed", + "cognitive_lifecycle", ) for node in ast.walk(tree): if isinstance(node, ast.Import):