diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index e4d81e10c..2023ea5bd 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -1,3 +1,78 @@ +## 2026-09-02 — E-SIX-SEMANTIC-FAMILIES-MUST-NOT-IMPERSONATE-EACH-OTHER-1 + +**Status:** OPERATOR RULING (semantic-family recovery), landed as the +September cleanup on this branch. **Confidence:** ruled; the code removals in +the same change are the enforcement, the forensic report the evidence. + +**Six semantic families, ratified as distinct:** (1) episodic / Markov loci — +`CausalWitnessFacet`, tenant 14, sign = orientation, pointer semantics closed +to the `Locus`/ClassView API; (2) epistemic qualia — cheap signed +proprioceptive magnitude of the CURRENT reasoning state, shipped precedent +`QualiaI4_16D`, stakes not logic, additive where its contract permits; +(3) epistemic population basins — population-relative semantic geometry +(the mammal / whale / wombat / horse / elephant examples), which has NO +shipped signed-i4 ABI, tenant, ClassView or axis vocabulary and is an accepted +VACANCY; (4) epistemic causality trajectory — `TrajectorySignature` / +`RevisionTrajectory`, an ordered evolution, not a magnitude register and not +the causal graph; (5) the causal graph; (6) the epistemic / knowledge graph. +Invariants: same physical shape ≠ same semantics; same codec ≠ same +ClassView; the word "basin" ≠ one mathematical object; locus ≠ magnitude ≠ +population basin ≠ causal graph; trajectory ≠ causal graph. + +**What the forensic pass found (four independent mistakes, four sources):** +population coordinates treated as additive child evidence +(`w2b_one_node_field.rs` summarise + `BasinLanes::accumulate_children`, +#1128); that artificial cancellation "repaired" by an agree/disagree pair +(#1129, a 24-byte wire object V3 does not have); shape equivalence read as +semantic equivalence — a test borrowed the A9 locus codec to hold a +magnitude, the type was then defined as the loci register's "sibling", the +axes as their "twins" (#1128 → #1132); and a fixed 24-axis vocabulary derived +from the loci table declared the meaning of population basins (#1129/#1132, +OGAR #297). None of it reached persisted or live ABI: `canonical_node.rs`, +`soa_envelope.rs`, `facet.rs` are byte-identical to the pre-#1125 tree, +`ENVELOPE_LAYOUT_VERSION` is unchanged, no tenant was minted, no producer +wrote either register. + +**deepnsm-v2 ruling:** `Cam96Space` / `PairPalette` / centroid + dispersion +are reusable mathematical primitives; `basin_self_code` is an episodic-rail +application of them (subject's outgoing SPO neighbourhood, version-ranged, +`BasinRow` in tenant 15) and is NOT the population-basin representation. The +July falsifier stands: Cam96 spread alone is not a self-uncertainty signal +(`E-BASIN-WIDTH-IS-N-ARTIFACT-1`). Taxonomic ancestry in the key rails is +membership/lineage, not a coordinate system; the A9 `BasinAnchor` fixture was +a pointer stand-in, not a population representation. + +**Removed in this cleanup:** `basin_lanes.rs`, `epistemic_bassin.rs`, +`tests/w2b_one_node_field.rs`, `lance-graph-ogar::assert_epistemic_band_parity`; +OGAR: `ogar-epistemic` (0x0334), the loco calls 0x87..0x8B (`BELNAP_JOIN`, +`INFO_GAIN`, `SIGMA_TENSION`, `ACCUMULATE`, `STANCE_ENTROPY`; census 101 → 96). +**Preserved:** `hhtl::{missing_ancestors, direct_children}`, tenants 14/15, +Qualia, Cam96/PairPalette, `atoms::I4x32::sext4` (visibility reverted to +private), the generic `TERNLOG` 0x86 (independently justified by ndarray's +`ternlog`), all `sigma_propagation` surfaces, OGAR #295 `BasinCodebook`. + +**Deferred, explicitly NOT done here (next falsifier-first step):** any +population-basin tenant or ClassView; 16 vs 24 dimensions; fixed axes for the +mammal examples; Shannon/Tarski entropy-plateau selection; the hypothesis +that population geometry PROJECTS INTO qualia/proprioception (population → +basins → projection → qualia → style/frontier/recipe) rather than becoming a +competing carrier; an `EMPTY, −7..+7` nibble (EMPTY = no valid observation, +0 = observed neutral; deterministic, total, replayable) to be falsified +against plain i4 on sparse sign-symmetric projections; and the design survey +that must inventory Fisher-z/Helix orientation, bgz17 / highheelbgz / +bgz-hhtl-d phase structures, palette256 distribution and sparse-salience +readings, Qualia, Markov loci, Cam96/PairPalette, trajectory/revision and the +graph surfaces before any new persistent shape is proposed — asking what +reasoning needs that is NOT already a cheap reading of an existing shape. +Kant/Wittgenstein/Nietzsche/Hegel behaviours are to be tried as Style/Recipe +operations over those shapes first; no new "Hegel helix" (Helix/Fisher +geometry exists). + +**Process residue:** #1133 conflicts with this cleanup on +`epistemic_bassin.rs` (deleted); its jc Hambly-Lyons + sigker + harvest work +is independent and should land without that hunk. The pre-ban stash of a +two-register re-cut is contaminated and is not reapplied. + ## 2026-09-01 — E-PILLAR-11-GREEN-FOR-LATTICE-WALKS-LENGTH-PARAMETERIZED-1 **Status:** SHIPPED (jc W6 leg, `hambly_lyons.rs`; sigker Index regime @@ -95,7 +170,6 @@ weak — the real defect is non-cancellative ratio distortion, which needs a saturation flag on Contested reads). Greedy INFO_GAIN admission carries a published Ω(n/log n) lower bound (Golovin-Krause-Ray Thm 9); EC² is the adaptive-submodular repair as a rung-local script. - ## 2026-09-01 — E-THE-24-AXIS-BASIS-V3-EVERY-AXIS-IS-A-GROUNDED-PRESSURE-1 **Status:** BUILT on the operator's "mach weiter" — the catalogue derived diff --git a/.claude/board/LATEST_STATE.md b/.claude/board/LATEST_STATE.md index 8ca5080ec..b7dce620b 100644 --- a/.claude/board/LATEST_STATE.md +++ b/.claude/board/LATEST_STATE.md @@ -1,4 +1,12 @@ -## 2026-09-01 — branch (PR pending): D-DCR-2b EpistemicBassin24 — CONTRACT INVENTORY DELTA +## 2026-09-02 — branch (cleanup, no PR yet): semantic-family recovery — CONTRACT INVENTORY DELTA + +- ⊘ REMOVED `lance_graph_contract::epistemic_bassin` (all of it: `EpistemicBassin24`, `AxisState`, the Belnap masks, `sweep_ternlog`/`eval_ternlog`, `info_gain_u4`, `sigma_tension_u4`, `loco_band`, `axes`) and `lance_graph_contract::basin_lanes` — operator semantic-family ruling 2026-09-02 (`E-SIX-SEMANTIC-FAMILIES-MUST-NOT-IMPERSONATE-EACH-OTHER-1`). The 2026-09-01 inventory rows below are regraded ⊘ in place; nothing they named reached a tenant, a layout version or a producer. +- ⊘ REMOVED `lance_graph_ogar::assert_epistemic_band_parity` (its mirror is gone; OGAR retracted the 0x87..0x8B calls). +- KEPT `hhtl::{missing_ancestors, direct_children}` (address-only helpers, semantics-neutral); `atoms::I4x32::sext4` back to private. +- Population-basin geometry: accepted VACANCY — no tenant, no ClassView, no axis set until the falsifier-first design step. + +## 2026-09-01 — ⊘ REGRADED 2026-09-02, merged as #1129–#1132, then removed: D-DCR-2b EpistemicBassin24 — CONTRACT INVENTORY DELTA + - `jc::hambly_lyons` **Pillar 11 GREEN for lattice walks — length-parameterized (W6, 2026-09-01)**: Theorem 5 lattice leg added (Hambly-Lyons Annals 171 §2.4 — Thm 5/6, `c = 2e·ln(1+√2) = 4.7916`, verified on the published PDF; the arXiv v2 `e` is pre-publication): all 52 reduced words of length ≤ 3 separated at depth ⌊c·L⌋, 64 tree-like words at the identity (2e-15), 64 depth-2 false merges among length-8 reduced words all separated by depth 3 ≤ ⌊c·8⌋ = 38, d = 1 collapses to exactly 7 signature classes (the `d ≥ 2` precondition). `sigker` Index regime re-worded: lossless on the tree-quotient ONLY under a walk-length budget `N ≥ ⌊c(d)·(|X|+|Y|)⌋`; depth 2 is necessary-only. Default build stays zero-dep/DEFERRED. Still red for non-lattice quantized step vectors (Thm 9 gives non-triviality without an explicit depth). - `lance_graph_contract::epistemic_bassin::{EpistemicBassin24, AxisState, BASIS_AXES(24), BASIS_PAIR_BYTES(24), AXIS_COUNT_MAX(15), info_gain_u4, sigma_tension_u4}` — the field map's value carrier as an `agree_u4[24] + disagree_u4[24]` PAIR (operator co-architect ruling: the signed net was FALSIFIED — `+3 + −3 = 0` collapsed balanced conflict into silence). Net/polarity/contest/entropy DERIVED; `Contested` is a first-class state that survives one-hop accumulation. Readout adapters grounded in shipped certificates: `info_gain_u4` (Shannon ΔH over candidate counts), `sigma_tension_u4` (quarters of `sigma_propagation::pillar_5plus_bound`; 7 = the 1.75× PASS slack); Hambly-Lyons has NO lane (sigker classification gated on jc Pillar 11, DEFERRED). **No storage minted**: the loci-never-magnitude law is scoped to the A9 READING, not tenant 14's bytes — the bassin is a classid-selected reading of the same physical lane; a separate tenant waits for one real row needing both readings simultaneously (an `EpistemicWitness = 16` mint was built and discarded uncommitted on this ruling). diff --git a/.claude/board/PR_ARC_INVENTORY.md b/.claude/board/PR_ARC_INVENTORY.md index 8b508222c..9e0b775f7 100644 --- a/.claude/board/PR_ARC_INVENTORY.md +++ b/.claude/board/PR_ARC_INVENTORY.md @@ -10,6 +10,25 @@ > census §8.3 trap 10: read the body FIRST, then open for write — never > inline both in one expression. +## 2026-09-02 — lance-graph branch `claude/medcare-rs-continue-6nhbxn` (cleanup, PR not yet opened) — semantic-family recovery + +- **Removed:** `basin_lanes.rs`, `epistemic_bassin.rs`, `tests/w2b_one_node_field.rs`, + `lance-graph-ogar::assert_epistemic_band_parity` + its test. Paired OGAR + change: `ogar-epistemic` crate removed, loco calls 0x87..0x8B retracted, + census re-pinned 101 → 96, `TERNLOG` 0x86 kept as generic. +- **Locked:** the six semantic families and their non-impersonation + invariants (`E-SIX-SEMANTIC-FAMILIES-MUST-NOT-IMPERSONATE-EACH-OTHER-1`). +- **Deferred:** every population-basin design decision (tenant, ClassView, + dimensionality, axes, plateau selection, projection into qualia, the + EMPTY-nibble experiment) — next step is falsifier-first design on a clean + tree. +- **Docs:** EPIPHANIES entry above; LATEST_STATE delta; STATUS_BOARD + D-DCR-2b regraded; plan `dismech-causal-replay-v1.md` §W2b recovery note. +- **Confidence:** High that the removal is complete for live/persisted ABI + (byte-identical `canonical_node.rs`/`soa_envelope.rs`/`facet.rs` vs the + pre-#1125 tree); #1128–#1132 and OGAR #296/#297 entries below stand as + history, regraded rather than rewritten. + ## 2026-09-01 — lance-graph #1130 (MERGED c630ab9) — loco band mirror + armed parity; two palette rulings recorded **Added.** `epistemic_bassin::loco_band::EPISTEMIC_CALLS` (zero-dep mirror of ogar-loco's epistemic core band `0x86..0x8B`) + `lance-graph-ogar::parity::assert_epistemic_band_parity()` in the excluded armed tier — written against loco's shared-core TABLE by raw index so it compiles against any revision; the assertions carry the parity. Verified GREEN against OGAR main after OGAR #296 merged (both directions + count pin 6). diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index bcf0e07b1..5bd3b51c8 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -7,7 +7,7 @@ | D-DCR-0a | prior-art reconciliation: `contract::dismech_evidence` + `dismech-causality-v3-v1` §11 arms (2,449 / 4,076 / 361) are W1-W3's falsifier; plan §3a | **Shipped** (E-W0-MEASURED-THE-MASK-HALF-DOMINATES-...-1) | | D-DCR-1 | replay core: loco calls under the dismech vocabulary -> CausalEdge64/NarsTruth steps -> temporal.rs trace; determinism + perturbation falsifiers | **Shipped (#1120, merged `cc0046f8`)** + follow-up in PR — `lance-graph-planner/src/dismech_replay.rs` (`replay_step` / `replay_chain` / `first_divergence` / `ReplayTraceRow: LocalCausalRow`); 4 gates, 3 disable-verified red-then-green. Palette binds at the membrane (plain `u8` ordinal here); the caller supplies a durable `base_seq` and the planner DERIVES each row's `cast_seq` from it — nothing here mints a counter. (Wording corrected per CodeRabbit #1120: the earlier phrasing said `cast_seq` was caller-supplied, which reverses the API contract.) Membrane half CLOSED: contract `dismech_evidence::DISMECH_PREDICATES` (zero-dep 19-row mirror, floor 0x90, position lookup) + armed-tier fuse `lance_graph_ogar::parity::assert_dismech_palette_parity` against the real `ogar_dismech::RELATIONS`, both directions, 3 more disables verified. Codex #1120: 3 findings, all valid — `ReplayTraceRow.predicate` now carried as WITNESS (the P1 falsified the module's own doc claim), `first_divergence` contract narrowed to content `(predicate, edge)` (the review's literal whole-row remedy was measured and rejected), `next_base_seq` makes the per-STEP durable reservation explicit. 9 disables total. CodeRabbit #1120 (4 more, read after merge): board wording corrected; `validate_chain` + `UnmintedOrdinal` reject an out-of-band ordinal AT ADMISSION while replay stays total over history; `replay_chain -> Result` with `ReplayError::SequenceExhausted` checks the whole reservation up front (`base_seq + i` panicked in debug / wrapped in release at u64::MAX). 11 disables total; 10 module gates. PR #1122 review (both reviewers, same bug): `next_base_seq` saturated and handed back an ALREADY-MINTED coordinate at the top of the range — a duplicate `cast_seq`, with the test pinning it as "the saturating guard". Now `Option`; exhaustion is representable. Also corrected: admission = FIRST acceptance, never re-reading the durable log (the old wording contradicted its own replay-must-not-refuse-history argument). 12 disables | | D-DCR-2 | Mengenlehre candidate evaluation via `contract::revision::EvidenceMask` (support ∩ / refute ∖ over `dismech_evidence::Supports`) | **In PR** — `lance-graph-planner/src/dismech_candidates.rs` (`EvidenceItem` / `apply` / `evaluate` / `Evaluation` / `is_informative`). Only `Support` and `Refute` are set operations; `Partial` and `NoEvidence` are INERT by design (full-strength elimination must not be bought with partial evidence, and an asserted absence is not a licence to cut) — reported via `decisive`, never silently dropped. `narrowing` separates "decisive by stance" from "actually taught something", the primitive W5's frontier needs. 6 gates, 4 disable-verified. Spec corrected in preflight: the refute class is the evidence STANCE (`Supports`, shipped + measured), NOT the graph-construction skip filter the plan first named. The skip filter decides whether an item becomes an edge at all, so a candidate set built from the graph has already excluded it — `∖` would subtract twice. Plan §W2 carries the full correction | -| D-DCR-2b | **the field map** — propagate precision about a knowledge stage over the WHOLE field; agreement / disagreement / support chains / MISSING LINKS into the HHTL nodes; the boring `is_a`/`part_of` rails lifted into a causality graph with propagated node edges | **In progress** (operator ruling 2026-09-01: three kinds of Mengenlehre; W2 shipped only kind 3, the question mask). Kind 2 (threshold elimination — Shannon / EWA / Hambly / Lyons) is a READING of this map and belongs with W4. **Carrier named 2026-09-01** (`E-AN-HHTL-POSITION-IS-A-NODE-AND-A-NODE-HAS-A-VALUE-1`): an HHTL position is an SoA node whose VALUE lane carries the 12-byte payload read as **24 signed i4** lanes — `+` agreement / `−` disagreement / `0` silence. Still unruled: which lane, versioned vs live, sweep granularity, and a node-level hydrate step for rail-implicit positions. **Census + one-node falsifier shipped 2026-09-01** (`E-G24N4-ALREADY-SHIPS-AND-THAT-IS-WHY-W2B-CANNOT-USE-IT-1`): `G24N4` already ships on `ValueTenant::CausalWitness`, so the carrier is not greenfield — and its operator-locked loci-never-magnitude value law plus its reserved slots `16..24` rule that lane OUT for W2b; 260 of 480 slab bytes free, so space is not the constraint. `tests/w2b_one_node_field.rs` pins the carrier at one-node scale (5 falsifiers, each disable-verified) incl. the whale case; it mints no lane and reserves no byte, and gap 3 (sweep convergence) is untouched. **Slices 1+2 shipped 2026-09-01 (this branch)**: DN dissolution + mechanical/epistemic split + one-hop law; `basin_lanes::BasinLanes` (magnitude register, G24N4 shape) + `accumulate_children` (one-hop, exact-sum-then-clamp) + `hhtl::{missing_ancestors, direct_children}`. Open: multi-register contested-mass semantics, provenance marker, tenant mint for the magnitude register (census: NOT CausalWitness; append margin at slab 220). **⊘ Superseded 2026-09-01 (co-architect ruling, `E-THE-SIGNED-NET-WAS-FALSIFIED-NOT-LIMITED-AND-THE-LOCI-LAW-WAS-SCOPED-TOO-WIDE-1`):** signed net falsified → `epistemic_bassin::EpistemicBassin24` pair (contested ≠ silence, survives accumulation); loci law re-scoped to the A9 READING so the bassin is a classid-selected reading of tenant 14 — NO new tenant until one real row needs both readings; Shannon/EWA adapters shipped against `dismech_candidates` counts + `sigma_propagation` certificates; Hambly-Lyons laneless while jc Pillar 11 is red. The named 24-axis catalogue SHIPPED as v3 2026-09-01 (`ogar-epistemic` 0x0334 + `epistemic_bassin::axes` mirror; supersedable by a v4 mint). Still open: the child-mask index, the provenance marker (PROVENANCE is now axis 20 — the marker's ROW placement is still unruled), armed catalogue parity after the OGAR merge | +| D-DCR-2b | **the field map** — propagate precision about a knowledge stage over the WHOLE field; agreement / disagreement / support chains / MISSING LINKS into the HHTL nodes; the boring `is_a`/`part_of` rails lifted into a causality graph with propagated node edges | **In progress** (operator ruling 2026-09-01: three kinds of Mengenlehre; W2 shipped only kind 3, the question mask). Kind 2 (threshold elimination — Shannon / EWA / Hambly / Lyons) is a READING of this map and belongs with W4. **Carrier named 2026-09-01** (`E-AN-HHTL-POSITION-IS-A-NODE-AND-A-NODE-HAS-A-VALUE-1`): an HHTL position is an SoA node whose VALUE lane carries the 12-byte payload read as **24 signed i4** lanes — `+` agreement / `−` disagreement / `0` silence. Still unruled: which lane, versioned vs live, sweep granularity, and a node-level hydrate step for rail-implicit positions. **Census + one-node falsifier shipped 2026-09-01** (`E-G24N4-ALREADY-SHIPS-AND-THAT-IS-WHY-W2B-CANNOT-USE-IT-1`): `G24N4` already ships on `ValueTenant::CausalWitness`, so the carrier is not greenfield — and its operator-locked loci-never-magnitude value law plus its reserved slots `16..24` rule that lane OUT for W2b; 260 of 480 slab bytes free, so space is not the constraint. `tests/w2b_one_node_field.rs` pins the carrier at one-node scale (5 falsifiers, each disable-verified) incl. the whale case; it mints no lane and reserves no byte, and gap 3 (sweep convergence) is untouched. **Slices 1+2 shipped 2026-09-01 (this branch)**: DN dissolution + mechanical/epistemic split + one-hop law; `basin_lanes::BasinLanes` (magnitude register, G24N4 shape) + `accumulate_children` (one-hop, exact-sum-then-clamp) + `hhtl::{missing_ancestors, direct_children}`. Open: multi-register contested-mass semantics, provenance marker, tenant mint for the magnitude register (census: NOT CausalWitness; append margin at slab 220). **⊘ Superseded 2026-09-01 (co-architect ruling, `E-THE-SIGNED-NET-WAS-FALSIFIED-NOT-LIMITED-AND-THE-LOCI-LAW-WAS-SCOPED-TOO-WIDE-1`):** signed net falsified → `epistemic_bassin::EpistemicBassin24` pair (contested ≠ silence, survives accumulation); loci law re-scoped to the A9 READING so the bassin is a classid-selected reading of tenant 14 — NO new tenant until one real row needs both readings; Shannon/EWA adapters shipped against `dismech_candidates` counts + `sigma_propagation` certificates; Hambly-Lyons laneless while jc Pillar 11 is red. The named 24-axis catalogue SHIPPED as v3 2026-09-01 (`ogar-epistemic` 0x0334 + `epistemic_bassin::axes` mirror; supersedable by a v4 mint). Still open: the child-mask index, the provenance marker (PROVENANCE is now axis 20 — the marker's ROW placement is still unruled), armed catalogue parity after the OGAR merge **⊘ RETRACTED 2026-09-02 (operator semantic-family ruling; see `E-SIX-SEMANTIC-FAMILIES-MUST-NOT-IMPERSONATE-EACH-OTHER-1`):** `basin_lanes`, `epistemic_bassin` (the 24-byte pair), the fixed 24-axis basis (`ogar-epistemic` 0x0334) and the pair-specific loco band 0x87..0x8B are REMOVED — they aliased the episodic-loci, qualia-magnitude and population-basin families into one register. Population-basin geometry is an accepted VACANCY (no tenant, no ClassView, no axis set); tenants 14/15, Qualia, Cam96/PairPalette and the #1128 HHTL helpers stand. Kind-1 field map returns to **Open — falsifier-first design step next**. | | D-DCR-3 | counterfactual replay (edge cut through `contract::counterfactual`, Pearl rung 3), two-sided load-bearing/redundant gates | **In PR** — `lance-graph-planner/src/dismech_counterfactual.rs`. Both arms go through W1's `replay_chain` (no second replay path); the cut arm reserves the range AFTER the factual one and is tagged `InferenceType::Counterfactual` (−6) so the road not taken can never read as observed truth. **Measured correction:** the verdict reads FREQUENCY, not confidence — confidence saturates at 170 across every fixture, so a confidence bar would have been a vacuous threshold. `EdgeRole` carries the cut edge's own `CausalTopology` (59-60) + `ReasoningBand` (61-63) so "explains" stays distinguishable from "relates to". Also lands `impl EpisodicEdge for CausalEdge64` (the bridge `contract::counterfactual` documents as BLOCKED — the planner is the first crate depending on both sides). 8 gates, 4 disable-verified | | D-DCR-4 | Σ transport via `jc::ewa_sandwich` + candidate-entropy readout; entropy-surface CONSOLIDATION decision recorded first | Queued | | D-DCR-5 | frontier scheduling (info-gain / rung-cost via `EpistemicMode::for_rung`) | **HELD** — operator rung 5-9 table ruling + W0 KILL check | diff --git a/.claude/plans/dismech-causal-replay-v1.md b/.claude/plans/dismech-causal-replay-v1.md index cfa888f85..e6039deb8 100644 --- a/.claude/plans/dismech-causal-replay-v1.md +++ b/.claude/plans/dismech-causal-replay-v1.md @@ -542,6 +542,18 @@ the node level (granularity down, aggregation up). Consequence for W5 the CLASSID MINT LIST for the upper palettes — the hold therefore stays exactly where it is; no rung classid is minted before the table. +#### ⊘ RECOVERY (operator, 2026-09-02) — semantic-family ruling; the four rounds above are regraded, not rewritten + +The fourth-round "pair" and the fifth-round loco band were built on a +register that aliased three families (episodic loci, qualia magnitude, +population basin). Removed: `basin_lanes`, `epistemic_bassin`, the 24-axis +basis (`ogar-epistemic`), loco 0x87..0x8B. Kept: the DN dissolution, the +one-hop law, `hhtl::{missing_ancestors, direct_children}`, tenants 14/15. +Population-basin geometry is an accepted vacancy; W2b's kind-1 field map +reopens as a falsifier-first design step whose inputs are listed in +`E-SIX-SEMANTIC-FAMILIES-MUST-NOT-IMPERSONATE-EACH-OTHER-1` (not an axis set, +not a tenant, not a dimensionality). + #### Still open — probes, not rulings - ~~The named 24-axis catalogue~~ — **SHIPPED as v3** (2026-09-01, diff --git a/crates/lance-graph-contract/src/atoms.rs b/crates/lance-graph-contract/src/atoms.rs index 041288ced..285ee4059 100644 --- a/crates/lance-graph-contract/src/atoms.rs +++ b/crates/lance-graph-contract/src/atoms.rs @@ -118,11 +118,9 @@ impl I4x32 { /// Sign-extend a 4-bit two's-complement nibble to `i8` in `[−8, 7]`. /// /// Carrier-owned (the nibble codec belongs to the carrier, not a free fn); - /// `I4x64` reuses it via `I4x32::sext4`, and so does - /// `basin_lanes::BasinLanes` (the same codec at the node's 24-lane - /// width) — crate-visible for exactly those carriers, never a free fn. + /// `I4x64` reuses it via `I4x32::sext4`. #[inline] - pub(crate) const fn sext4(nibble: u8) -> i8 { + const fn sext4(nibble: u8) -> i8 { (((nibble & 0x0F) << 4) as i8) >> 4 } } diff --git a/crates/lance-graph-contract/src/basin_lanes.rs b/crates/lance-graph-contract/src/basin_lanes.rs deleted file mode 100644 index a3bea98f6..000000000 --- a/crates/lance-graph-contract/src/basin_lanes.rs +++ /dev/null @@ -1,419 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Copyright The Lance Authors - -//! `basin_lanes` — the **24 × i4** SIGNED-NET agreement reading -//! (`D-DCR-2b`) — **⊘ SUPERSEDED 2026-09-01 (operator co-architect ruling) -//! by [`epistemic_bassin::EpistemicBassin24`](crate::epistemic_bassin::EpistemicBassin24)**. -//! -//! The signed net was FALSIFIED as a representation, not merely limited: -//! `+3` support and `−3` refutation sum to `0`, making maximal balanced -//! conflict indistinguishable from silence — and agreement/disagreement is -//! precisely the interesting information. The successor stores the pair -//! (`agree_u4[24] + disagree_u4[24]`); net, polarity, conflict and entropy -//! are DERIVED there and forget nothing. This module stays per -//! reserve-don't-reclaim (it is on main, with consumers in its own tests -//! and `tests/w2b_one_node_field.rs`'s cross-reference); new writers use -//! the successor. Also corrected there: this module's "associative and -//! commutative EXACTLY" claim holds only within one unsplit call — -//! recursive composition of already-clamped registers is not associative. -//! -//! An HHTL position is a node, and the node's VALUE summarises the position's -//! children so the node speaks for itself without a second read. The carrier -//! is the register the node already owns — 12 bytes — read at NIBBLE -//! granularity: 24 signed i4 lanes in `[−8, 7]`, the **`G24N4`** shape. -//! -//! `G24N4` is NOT new here (the #1127 census corrected an earlier claim that -//! it was): it already ships as -//! [`causal_witness::CausalWitnessFacet`](crate::causal_witness::CausalWitnessFacet)'s -//! reading of `ValueTenant::CausalWitness` — but THAT lane's value law is -//! operator-locked to **loci, never strength/magnitude** (context pointers -//! into the ±8 Markov window), and its slots `16..24` are reserved. A W2b -//! child-summary is exactly the magnitude case that law forbids there. This -//! type is therefore the MAGNITUDE register in the same shape — the sibling -//! the census concluded must exist ("W2b needs its own lane") — and which -//! `ValueTenant` it occupies is the operator's mint (census: append margin at -//! slab offset 220, 260 bytes free), not decided here. -//! -//! # The sign IS the semantics -//! -//! One lane, three cells: **positive = agreement, negative = disagreement, -//! zero = silence.** The whale case falls out of the carrier itself — a whale -//! disagreeing with the mammal neighbourhood is a NEGATIVE lane, a value on -//! the node, never a removal from the set. And silence stays distinct from -//! denial (`0` vs any negative), the same distinction -//! [`Supports::NoEvidence`](crate::dismech_evidence::Supports) refuses to -//! collapse on the evidence side. -//! -//! # Mechanical hydration writes NOTHING here (operator-ruled, 2026-09-01) -//! -//! Minting a row at a nameable DN (from a book TOC, a rail's parent/child, an -//! OWL hierarchy) is MECHANICAL — structure only. Its epistemic output is -//! exactly [`SILENT`](BasinLanes::SILENT): all 24 lanes zero, the -//! zero-fallback ladder holding one level up. Original causality predicates — -//! the dismech palette, Tarski-precise assertions, these signed lanes — come -//! only from evidence and propagation. A mint that writes a nonzero lane is -//! structure impersonating knowledge; [`is_silent`](BasinLanes::is_silent) is -//! the checkable half of that rule. -//! -//! The lane-filling ARITHMETIC (how a child's stance becomes a signed value, -//! how siblings merge at the parent) is deliberately NOT here — it is -//! unmeasured, and per the plan it is a probe, not a codec. This module is -//! the carrier only. - -use crate::atoms::I4x32; - -/// Lanes in one register: 12 bytes × 2 nibbles. -pub const BASIN_LANES: usize = 24; - -/// Bytes the lanes occupy — the node's content-blind register width. -pub const BASIN_LANE_BYTES: usize = 12; - -/// Packed 24-lane signed-i4 agreement register (12 bytes, two lanes per -/// byte). Same nibble codec as [`I4x32`] / `I4x64` at the node's own width. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] -pub struct BasinLanes { - bytes: [u8; BASIN_LANE_BYTES], -} - -impl BasinLanes { - /// Epistemic silence: every lane `0`. The ONLY value mechanical - /// hydration may leave behind, and what an unwritten register reads as — - /// absent, never an assertion. - pub const SILENT: Self = Self { - bytes: [0; BASIN_LANE_BYTES], - }; - - /// Pack 24 signed lanes, saturating to `[−8, 7]`. Two's-complement - /// nibble: lane `2k` → low nibble of byte `k`, lane `2k+1` → high. - #[must_use] - pub fn pack(lanes: &[i8; BASIN_LANES]) -> Self { - let mut bytes = [0u8; BASIN_LANE_BYTES]; - for (k, b) in bytes.iter_mut().enumerate() { - let lo = lanes[2 * k].clamp(-8, 7) as u8 & 0x0F; - let hi = lanes[2 * k + 1].clamp(-8, 7) as u8 & 0x0F; - *b = (hi << 4) | lo; - } - Self { bytes } - } - - /// Unpack to signed lanes (sign-extended i4, `[−8, 7]`). - #[must_use] - pub fn unpack(&self) -> [i8; BASIN_LANES] { - let mut out = [0i8; BASIN_LANES]; - for (k, b) in self.bytes.iter().enumerate() { - out[2 * k] = I4x32::sext4(b & 0x0F); - out[2 * k + 1] = I4x32::sext4(b >> 4); - } - out - } - - /// The register's raw little-endian bytes, as they sit in the row. - #[must_use] - pub const fn to_le_bytes(&self) -> [u8; BASIN_LANE_BYTES] { - self.bytes - } - - /// Read a register from its raw bytes — total, no failure mode: every - /// 12-byte pattern is 24 representable lanes. - #[must_use] - pub const fn from_le_bytes(bytes: [u8; BASIN_LANE_BYTES]) -> Self { - Self { bytes } - } - - /// Is every lane zero? `true` = epistemic silence — a mechanically - /// hydrated (or never-written) register. The mint-side guard: a hydration - /// step must leave this `true`. - #[must_use] - pub fn is_silent(&self) -> bool { - *self == Self::SILENT - } - - /// How many lanes carry a NEGATIVE value — recorded disagreement. A - /// disagreeing child is a value here, never a removal from the set. - #[must_use] - pub fn disagreement_count(&self) -> usize { - self.unpack().iter().filter(|&&v| v < 0).count() - } - - /// How many lanes carry a POSITIVE value — recorded agreement. - #[must_use] - pub fn agreement_count(&self) -> usize { - self.unpack().iter().filter(|&&v| v > 0).count() - } - - /// **One-hop accumulation** (operator-ruled, 2026-09-01): a parent's - /// register expresses its DIRECT children accumulated — agreement AND - /// disagreement — never the grandchildren. Grandchild information reaches - /// a grandparent only through the child's own accumulated register, one - /// hop at a time; the global field map is the composition of one-hop - /// summaries, never a node reaching past its children (the same locality - /// that makes `FieldMask::inherit` a parent∪delta and the substrate - /// Markov, `I-SUBSTRATE-MARKOV`). - /// - /// Per-lane merge: **exact signed sum, clamped once to `[−8, 7]`** — - /// agreement stacks, disagreement pulls down, the bound is the carrier's - /// own range. Exact-then-clamp (not stepwise saturation) makes the merge - /// associative and commutative EXACTLY, not merely in expectation. - /// - /// **Measured limitation, pinned rather than hidden:** in ONE register a - /// balanced conflict (`+3` child and `−3` child on the same lane) sums to - /// `0` — indistinguishable from silence. That is the concrete case for - /// the ruled escape hatch "the nibbles can be expanded if necessary / - /// multiple 24×i4 further up": a contested-mass register beside the net - /// register. NOT built here — the multi-register semantics are the - /// operator's to shape; the collapse is pinned by - /// `a_balanced_conflict_collapses_to_silence_in_one_register` so the gap - /// stays loud. - /// - /// `accumulate_children(&[])` is [`SILENT`](Self::SILENT) — a childless - /// position asserts nothing. - #[must_use] - pub fn accumulate_children(children: &[Self]) -> Self { - // Exact i32 sums, ONE clamp at pack: per-step saturation would make - // the result depend on child ORDER for mixed signs ([+7,+7,−7] is 0 - // stepwise but 7 summed) — caught by this module's own disable-run - // when the stepwise version survived a wrapping_add disable (pack's - // clamp masked it). Order-independence is pinned by - // `accumulation_is_independent_of_child_order`. - let mut acc = [0i32; BASIN_LANES]; - for c in children { - let lanes = c.unpack(); - for (a, v) in acc.iter_mut().zip(lanes.iter()) { - *a += i32::from(*v); - } - } - let mut out = [0i8; BASIN_LANES]; - for (o, a) in out.iter_mut().zip(acc.iter()) { - *o = (*a).clamp(-8, 7) as i8; - } - Self::pack(&out) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::facet::{CascadeShape, CASCADE_UNITS}; - - #[test] - fn round_trips_every_representable_lane_value() { - for val in -8i8..=7 { - let lanes = [val; BASIN_LANES]; - assert_eq!( - BasinLanes::pack(&lanes).unpack(), - lanes, - "lane value {val} must round-trip" - ); - } - // A mixed pattern too — a uniform fixture cannot see a lane-order - // swap (lane 2k vs 2k+1 confusion round-trips on uniform input). - let mut mixed = [0i8; BASIN_LANES]; - for (i, l) in mixed.iter_mut().enumerate() { - *l = (i as i8 % 16) - 8; - } - assert_eq!(BasinLanes::pack(&mixed).unpack(), mixed); - } - - #[test] - fn saturates_outside_the_i4_range_instead_of_wrapping() { - assert_eq!( - BasinLanes::pack(&[100; BASIN_LANES]).unpack(), - [7; BASIN_LANES] - ); - assert_eq!( - BasinLanes::pack(&[-100; BASIN_LANES]).unpack(), - [-8; BASIN_LANES] - ); - // Just outside each bound: a wrapping codec would flip the SIGN here, - // which for this carrier turns agreement into disagreement — the - // worst possible corruption, so the boundary is pinned exactly. - assert_eq!( - BasinLanes::pack(&[8; BASIN_LANES]).unpack(), - [7; BASIN_LANES] - ); - assert_eq!( - BasinLanes::pack(&[-9; BASIN_LANES]).unpack(), - [-8; BASIN_LANES] - ); - } - - #[test] - fn silence_is_the_default_and_the_zero_register() { - // Zero-fallback one level up: an unwritten register IS silence. - assert_eq!(BasinLanes::default(), BasinLanes::SILENT); - assert!(BasinLanes::from_le_bytes([0; BASIN_LANE_BYTES]).is_silent()); - assert!(BasinLanes::SILENT.is_silent()); - assert_eq!(BasinLanes::SILENT.disagreement_count(), 0); - assert_eq!(BasinLanes::SILENT.agreement_count(), 0); - } - - /// The whale case at the carrier level, two-sided: disagreement is a - /// VALUE (recorded, countable, round-trips) and is distinct from BOTH - /// silence and agreement. A carrier that collapsed `−` into `0` (or into - /// removal) fails all three arms. - #[test] - fn a_negative_lane_is_recorded_disagreement_not_silence_and_not_removal() { - let mut lanes = [1i8; BASIN_LANES]; // a mammal neighbourhood agreeing - lanes[3] = -5; // the whale - let reg = BasinLanes::pack(&lanes); - assert!(!reg.is_silent(), "a disagreeing lane is not silence"); - assert_eq!(reg.disagreement_count(), 1, "the whale is RECORDED"); - assert_eq!( - reg.agreement_count(), - BASIN_LANES - 1, - "the rest of the neighbourhood is untouched — nothing was removed" - ); - assert_eq!( - reg.unpack()[3], - -5, - "the disagreement value itself survives" - ); - } - - /// Missing ≠ refuted, at the carrier level: lane 0 (silence) and a - /// negative lane are different cells. Collapsing them is the same error - /// class as `NoEvidence` narrowing a set. - #[test] - fn silence_and_denial_are_different_cells() { - let mut lanes = [0i8; BASIN_LANES]; - lanes[7] = -1; - let denied = BasinLanes::pack(&lanes); - lanes[7] = 0; - let silent = BasinLanes::pack(&lanes); - assert_ne!(denied, silent); - assert_eq!(denied.disagreement_count(), 1); - assert_eq!(silent.disagreement_count(), 0); - assert!(silent.is_silent()); - } - - /// The register width is the SAME 12 units every CascadeShape carves — - /// this reading adds no bytes to the node, it re-reads what is there. - #[test] - fn the_lane_register_is_the_cascade_register_re_read_at_nibble_grain() { - assert_eq!(BASIN_LANE_BYTES, CASCADE_UNITS); - assert_eq!(BASIN_LANES, 2 * CASCADE_UNITS); - for s in CascadeShape::ROTATIONS { - assert_eq!( - s.groups() as usize * s.levels() as usize, - BASIN_LANE_BYTES, - "every byte-granular shape covers the same register these lanes re-read" - ); - } - } - // ---- one-hop accumulation ---- - - #[test] - fn accumulation_stacks_agreement_and_records_disagreement_as_pull_down() { - let mut a = [0i8; BASIN_LANES]; - let mut b = [0i8; BASIN_LANES]; - a[0] = 2; - b[0] = 3; // both agree on lane 0 - a[1] = 4; - b[1] = -1; // contested lane 1: net stays positive but is PULLED DOWN - let acc = BasinLanes::accumulate_children(&[BasinLanes::pack(&a), BasinLanes::pack(&b)]); - let lanes = acc.unpack(); - assert_eq!(lanes[0], 5, "agreement stacks"); - assert_eq!( - lanes[1], 3, - "disagreement is IN the accumulation, not dropped" - ); - assert!( - lanes[2..].iter().all(|&v| v == 0), - "untouched lanes stay silent" - ); - } - - #[test] - fn accumulation_saturates_at_the_carrier_bound_in_both_directions() { - let up = BasinLanes::pack(&[5; BASIN_LANES]); - let down = BasinLanes::pack(&[-5; BASIN_LANES]); - assert_eq!( - BasinLanes::accumulate_children(&[up, up, up]).unpack(), - [7; BASIN_LANES] - ); - assert_eq!( - BasinLanes::accumulate_children(&[down, down, down]).unpack(), - [-8; BASIN_LANES] - ); - } - - #[test] - fn a_childless_position_accumulates_to_silence() { - assert!(BasinLanes::accumulate_children(&[]).is_silent()); - } - - /// ⊘ This pin did its job (2026-09-01): the collapse it records was - /// ruled a FALSIFICATION of the signed-net representation, and the - /// re-shape landed as `epistemic_bassin::EpistemicBassin24`, whose - /// reversed falsifier (`equal_support_and_refutation_is_distinguishable_ - /// from_silence`) asserts the exact opposite over the pair. The pin - /// stays green HERE because it is a true fact about THIS superseded - /// type — the record of why it was superseded. - #[test] - fn a_balanced_conflict_collapses_to_silence_in_one_register() { - let mut a = [0i8; BASIN_LANES]; - let mut b = [0i8; BASIN_LANES]; - a[5] = 3; - b[5] = -3; - let acc = BasinLanes::accumulate_children(&[BasinLanes::pack(&a), BasinLanes::pack(&b)]); - assert!( - acc.is_silent(), - "one net register cannot carry contested-ness; the day it can, re-pin" - ); - } - - /// One-hop locality made OBSERVABLE across two levels: the grandparent - /// reads the grandchild only through the child's accumulated register. - /// Can-fire: a grandchild move that moves the child moves the - /// grandparent. Silence: a grandchild move ABSORBED by the child's own - /// saturation leaves the grandparent byte-identical. - #[test] - fn a_grandchild_reaches_the_grandparent_only_through_the_child() { - use crate::hhtl::{direct_children, NiblePath}; - let gp = NiblePath::root(1); - let child = gp.child(2); - let gc1 = child.child(0); - let gc2 = child.child(1); - let occupied = [gp, child, gc1, gc2]; - assert_eq!(direct_children(gp, &occupied), vec![child]); - assert_eq!(direct_children(child, &occupied), vec![gc1, gc2]); - - let lanes_of = |v: i8| { - let mut l = [0i8; BASIN_LANES]; - l[0] = v; - BasinLanes::pack(&l) - }; - let two_level = |gc1_v: i8, gc2_v: i8| { - let child_reg = BasinLanes::accumulate_children(&[lanes_of(gc1_v), lanes_of(gc2_v)]); - BasinLanes::accumulate_children(&[child_reg]) - }; - // Can-fire: the grandchild flips sign hard -> the child moves -> the - // grandparent moves. - assert_ne!(two_level(3, 3), two_level(-6, 3)); - // Silence: both grandchild states saturate the child at +7, so the - // grandparent cannot tell them apart -- the grandchild's detail is - // the CHILD's knowledge, one hop only. - assert_eq!(two_level(7, 5), two_level(6, 7)); - } - /// Falsifier that DISTINGUISHES designs: under stepwise saturation - /// ([+7,+7,−7] clamps mid-stream) child order changes the parent; under - /// exact-sum-then-clamp it cannot. Verified failing against the stepwise - /// implementation before it was replaced. - #[test] - fn accumulation_is_independent_of_child_order() { - let lanes_of = |v: i8| { - let mut l = [0i8; BASIN_LANES]; - l[0] = v; - BasinLanes::pack(&l) - }; - let a = lanes_of(7); - let b = lanes_of(7); - let c = lanes_of(-7); - assert_eq!( - BasinLanes::accumulate_children(&[a, b, c]), - BasinLanes::accumulate_children(&[c, a, b]), - "one hop is a SET of children, not a sequence" - ); - assert_eq!(BasinLanes::accumulate_children(&[a, b, c]).unpack()[0], 7); - } -} diff --git a/crates/lance-graph-contract/src/epistemic_bassin.rs b/crates/lance-graph-contract/src/epistemic_bassin.rs deleted file mode 100644 index a2fd620d6..000000000 --- a/crates/lance-graph-contract/src/epistemic_bassin.rs +++ /dev/null @@ -1,962 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Copyright The Lance Authors - -//! `epistemic_bassin` — **`EpistemicBassin24`** (operator-named: das *Bassin*, the accumulation pool): 24 named epistemic axes as an -//! `agree_u4[24] + disagree_u4[24]` PAIR (`D-DCR-2b`, operator co-architect -//! ruling 2026-09-01). Supersedes `basin_lanes::BasinLanes`' signed-net -//! representation. -//! -//! # Why the signed net was FALSIFIED, not merely limited -//! -//! In one signed register, `+3` support and `−3` refutation sum to `0` — -//! maximal balanced conflict indistinguishable from silence. The `3` is -//! only the illustrative pair: the old lane was true two's-complement i4 -//! over the full `[−8, 7]` (`atoms::I4x32::sext4`, no narrower quantizer -//! ever existed), and the collapse holds for EVERY magnitude `1..=7` -//! (`+x + (−x) = 0`, byte-identical to `SILENT`); only `x = 8` escapes, -//! and only because `+8` is unrepresentable and clamps to `+7`. Widening -//! the lane would not have fixed it — the loss is DIMENSIONAL (two -//! independent counts projected onto one signed coordinate), not RANGE -//! (source audit 2026-09-01, `literature-harvest-2026-09-01-post-1132.md` -//! companion). If agreement -//! and disagreement are the interesting information, a representation that -//! destroys their coincidence is wrong, not constrained; `basin_lanes`' -//! own pinned collapse test is the evidence. The pair keeps both sides: -//! **net, polarity, conflict, masks and entropy are all DERIVED**, and -//! equal support/refutation is a first-class state (`Contested`), distinct -//! from silence by construction. Cost: one extra 12-byte register. -//! -//! # A BASSIN of values on a named BASIS — never an address -//! -//! The name carries both halves deliberately: the 24 axes are a named -//! epistemic **basis** (which axes, and their version, named by the facet -//! classid), and the register pair is the **Bassin** — the pool where one -//! hop of children's stance mass collects. Distinct from -//! [`EpisodicBasin`](crate::canonical_node::ValueTenant::EpisodicBasin), -//! which stores durable episodic REFERENCES; this type stores no reference -//! of any kind. -//! -//! The facet's 4-byte classid names the **24-axis basis** (which named -//! epistemic axes, and their version — "epistemic witness v3" is a classid, -//! not a Rust name). Each nibble is a **quantized value on a named axis**, -//! never an address: -//! -//! - parent/children topology lives in the HHTL KEY (+ an indexed 16-bit -//! child mask reconstructs every direct child by appending each set -//! nibble) — storing child pointers here would duplicate the key; -//! - durable episodic identity lives in `EpisodicBasin` / stable -//! references, not in ±8 stream offsets; -//! - exact proof and authority live in their exact carriers — premises/W, -//! `CausalEdge64`, NARS truth, revision/provenance; -//! - exact EWA covariance lives in the Σ/SPD carrier. -//! -//! What a lane MAY carry is **proprioception and pressure**: local -//! support/falsifier pressure (Tarski's exact depth stays derivable from -//! premise ancestry), ΔH / expected information gain (Shannon H itself -//! stays a readout of the candidate distribution), transformed -//! tension/residual/coherence (EWA's exact Σ stays elsewhere). -//! Counterfactual/revision axes describe pressure and change, never -//! empirical truth. -//! -//! # Storage is NOT minted here — the #1127 law was scoped too wide -//! -//! `loci-never-magnitude` is a law of the EXPERIMENTAL A9 ContextLoci -//! *reading* (`causal_witness::CausalWitnessFacet`), not of tenant 14's -//! physical bytes: the 12-byte register is content-blind, its -//! interpretation selected per row by the 4-byte classid, and multiple -//! readings of the same bytes are explicitly allowed (`causal_witness.rs` -//! itself ships as "the THIRD ClassView reading of the same register"). -//! #1127 accidentally promoted a reading-specific invariant into a storage -//! invariant. So this basis can be a classid-selected reading of the SAME -//! physical lane; a separate tenant is minted only when one real row -//! demonstrably needs ContextLoci AND the basis simultaneously. -//! -//! # One hop, and an honest associativity note -//! -//! A node's pair expresses its DIRECT children accumulated; whether the -//! next hop continues or cancels what it reads is the second hop's -//! decision. [`accumulate_children`](EpistemicBassin24::accumulate_children) -//! is exact-sum-then-clamp. Per side that is the MV-monoid `(L₁₆, ⊕, 0)` -//! with `a ⊕ b = min(15, a + b)` (Chang 1958): **associative and -//! commutative even across recursive hops** — any fold order of a hop tree -//! yields a bit-identical register (pinned exhaustively over all 16³ -//! triples, `accumulation_is_associative_and_non_cancellative_per_side`). -//! What the clamp costs is CANCELLATIVITY: `15 ⊕ 1 == 15 ⊕ 2`, so a -//! clamped 15 means "at least 15" and understates mass; it never converts -//! conflict into silence — which is precisely what the superseded signed -//! net could not guarantee. (An earlier wording here said "NOT -//! associative"; that was too weak in the safe direction — corrected -//! 2026-09-01 after the harvest probe.) - -/// Named axes in one basis register pair. -pub const BASIS_AXES: usize = 24; - -/// Bytes per side (24 × u4). -pub const BASIS_REGISTER_BYTES: usize = 12; - -/// Wire width of the pair: agree register then disagree register. -pub const BASIS_PAIR_BYTES: usize = 2 * BASIS_REGISTER_BYTES; - -// The two-register cost is ENFORCED, not narrated: the pair is exactly two -// V3 facet payloads wide and cannot fit one (the superseded `BasinLanes` -// pinned `BASIN_LANE_BYTES == CASCADE_UNITS`; this is the successor's pin). -const _: () = assert!(core::mem::size_of::() == BASIS_PAIR_BYTES); -const _: () = assert!(BASIS_PAIR_BYTES == 2 * crate::facet::CASCADE_UNITS); - -/// Per-axis count ceiling (u4). -pub const AXIS_COUNT_MAX: u8 = 15; - -/// All 24 axis bits set — the universe for the Belnap mask projections. -pub const AXIS_MASK_ALL: u64 = (1 << BASIS_AXES) - 1; - -/// What one axis reads as, derived from the pair. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum AxisState { - /// No recorded support and no recorded refutation. - Silent, - /// Support only. - Agree, - /// Refutation only. - Disagree, - /// Both sides recorded — the state the signed net destroyed. - Contested, -} - -/// The 24-axis epistemic basis pair: unsigned support and refutation -/// counts per named axis, two lanes per byte (axis `2k` → low nibble, -/// `2k+1` → high). -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] -pub struct EpistemicBassin24 { - agree: [u8; BASIS_REGISTER_BYTES], - disagree: [u8; BASIS_REGISTER_BYTES], -} - -fn pack_u4(counts: &[u8; BASIS_AXES]) -> [u8; BASIS_REGISTER_BYTES] { - let mut bytes = [0u8; BASIS_REGISTER_BYTES]; - for (k, b) in bytes.iter_mut().enumerate() { - let lo = counts[2 * k].min(AXIS_COUNT_MAX); - let hi = counts[2 * k + 1].min(AXIS_COUNT_MAX); - *b = (hi << 4) | lo; - } - bytes -} - -fn unpack_u4(bytes: &[u8; BASIS_REGISTER_BYTES]) -> [u8; BASIS_AXES] { - let mut out = [0u8; BASIS_AXES]; - for (k, b) in bytes.iter().enumerate() { - out[2 * k] = b & 0x0F; - out[2 * k + 1] = b >> 4; - } - out -} - -impl EpistemicBassin24 { - /// Epistemic silence: both sides zero on every axis. What mechanical - /// hydration leaves behind, and what an unwritten pair reads as. - pub const SILENT: Self = Self { - agree: [0; BASIS_REGISTER_BYTES], - disagree: [0; BASIS_REGISTER_BYTES], - }; - - /// Pack per-axis support and refutation counts, each saturating to - /// `0..=15`. - #[must_use] - pub fn pack(agree: &[u8; BASIS_AXES], disagree: &[u8; BASIS_AXES]) -> Self { - Self { - agree: pack_u4(agree), - disagree: pack_u4(disagree), - } - } - - /// Per-axis support counts. - #[must_use] - pub fn agree_counts(&self) -> [u8; BASIS_AXES] { - unpack_u4(&self.agree) - } - - /// Per-axis refutation counts. - #[must_use] - pub fn disagree_counts(&self) -> [u8; BASIS_AXES] { - unpack_u4(&self.disagree) - } - - /// DERIVED net stance on one axis: `agree − disagree`, in `−15..=15`. - /// The superseded representation STORED this and nothing else; here it - /// is a projection that forgets nothing underneath. - #[must_use] - pub fn net(&self, axis: usize) -> i8 { - assert!(axis < BASIS_AXES, "axis out of range"); - self.agree_counts()[axis] as i8 - self.disagree_counts()[axis] as i8 - } - - /// DERIVED contested mass on one axis: the two-sided overlap - /// `min(agree, disagree)` — `0` unless BOTH sides are recorded. - #[must_use] - pub fn contest(&self, axis: usize) -> u8 { - assert!(axis < BASIS_AXES, "axis out of range"); - self.agree_counts()[axis].min(self.disagree_counts()[axis]) - } - - /// The axis's derived state. - #[must_use] - pub fn axis_state(&self, axis: usize) -> AxisState { - assert!(axis < BASIS_AXES, "axis out of range"); - match (self.agree_counts()[axis], self.disagree_counts()[axis]) { - (0, 0) => AxisState::Silent, - (_, 0) => AxisState::Agree, - (0, _) => AxisState::Disagree, - _ => AxisState::Contested, - } - } - - /// Is every axis silent on both sides? - #[must_use] - pub fn is_silent(&self) -> bool { - *self == Self::SILENT - } - - /// Census over the 24 axes: `[silent, agree, disagree, contested]`. - #[must_use] - pub fn state_counts(&self) -> [usize; 4] { - let mut out = [0usize; 4]; - for axis in 0..BASIS_AXES { - out[match self.axis_state(axis) { - AxisState::Silent => 0, - AxisState::Agree => 1, - AxisState::Disagree => 2, - AxisState::Contested => 3, - }] += 1; - } - out - } - - /// Shannon entropy (bits) of the four-state axis distribution — the - /// register-level proprioception readout ("a value tenant must add to - /// entropy work"). `0.0` for a pure register; maximal `log2 4 = 2.0` - /// at the uniform 6/6/6/6 mix. Unlike the superseded net, `Contested` - /// is a cell of its own here, so balanced conflict RAISES this readout - /// instead of vanishing from it. - #[must_use] - pub fn entropy_bits(&self) -> f32 { - shannon(&self.state_counts()) - } - - /// One-hop contested-ness across the DIRECT children's states on one - /// axis — Shannon entropy (bits) of the four-state distribution of - /// `children[i].axis_state(axis)`. Empty children → `0.0`. - #[must_use] - pub fn stance_entropy_bits(children: &[Self], axis: usize) -> f32 { - assert!(axis < BASIS_AXES, "axis out of range"); - let mut counts = [0usize; 4]; - for c in children { - counts[match c.axis_state(axis) { - AxisState::Silent => 0, - AxisState::Agree => 1, - AxisState::Disagree => 2, - AxisState::Contested => 3, - }] += 1; - } - shannon(&counts) - } - - /// One-hop accumulation: per-axis exact `u32` sums PER SIDE, one clamp - /// to `0..=15` at pack — order-independent within one call (see the - /// module doc for the honest recursion caveat). A child's refutation - /// accumulates on the disagree side and can never cancel another - /// child's support — conflict is preserved, not netted away. - /// Empty → [`SILENT`](Self::SILENT). - #[must_use] - pub fn accumulate_children(children: &[Self]) -> Self { - let mut agree = [0u32; BASIS_AXES]; - let mut disagree = [0u32; BASIS_AXES]; - for c in children { - let (a, d) = (c.agree_counts(), c.disagree_counts()); - for i in 0..BASIS_AXES { - agree[i] += u32::from(a[i]); - disagree[i] += u32::from(d[i]); - } - } - let mut ap = [0u8; BASIS_AXES]; - let mut dp = [0u8; BASIS_AXES]; - for i in 0..BASIS_AXES { - ap[i] = agree[i].min(u32::from(AXIS_COUNT_MAX)) as u8; - dp[i] = disagree[i].min(u32::from(AXIS_COUNT_MAX)) as u8; - } - Self::pack(&ap, &dp) - } - - /// Axes with recorded SUPPORT (`agree > 0`, regardless of refutation), - /// one bit per axis in bits `0..24` of a `u64` — the first half of the - /// **ternary/Belnap mask encoding** (operator observation, 2026-09-01: - /// the state layer combines as booleans over (Wide)FieldMask-shaped - /// carriers). - /// - /// Together with [`refute_mask`](Self::refute_mask) this is exactly the - /// two-bit-per-axis Belnap/FDE encoding: `(0,0)` Neither = Silent, - /// `(1,0)` True = Agree, `(0,1)` False = Disagree, `(1,1)` Both = - /// Contested — the four [`AxisState`]s ARE Belnap's four values, and - /// dropping the Both cell restricts to Kleene's ternary K3. The - /// **knowledge-order join is bitwise OR of the two masks**, and that is - /// provably the state layer of - /// [`accumulate_children`](Self::accumulate_children) (counts only add, - /// so a parent side is nonzero iff some child's is — - /// `accumulations_state_layer_is_the_belnap_knowledge_join` pins it). - /// - /// Returned as `u64` so it composes directly with the shipped mask - /// algebra — `revision::EvidenceMask for u64` - /// (union/intersection/difference/subset) and `FieldMask`-style ops — - /// giving bit-parallel kind-3 question masking across all 24 axes. - #[must_use] - pub fn support_mask(&self) -> u64 { - let a = self.agree_counts(); - let mut m = 0u64; - for (i, &v) in a.iter().enumerate() { - if v > 0 { - m |= 1 << i; - } - } - m - } - - /// Axes with recorded REFUTATION (`disagree > 0`) — the second Belnap - /// bit. See [`support_mask`](Self::support_mask). - #[must_use] - pub fn refute_mask(&self) -> u64 { - let d = self.disagree_counts(); - let mut m = 0u64; - for (i, &v) in d.iter().enumerate() { - if v > 0 { - m |= 1 << i; - } - } - m - } - - /// Axes in the Belnap **Both** cell — support AND refutation recorded. - /// Derived: `support ∩ refute`. - #[must_use] - pub fn contested_mask(&self) -> u64 { - self.support_mask() & self.refute_mask() - } - - /// Axes in the Belnap **Neither** cell. Derived: - /// `¬(support ∪ refute)` over the 24 axis bits. - #[must_use] - pub fn silent_mask(&self) -> u64 { - !(self.support_mask() | self.refute_mask()) & AXIS_MASK_ALL - } - - /// Wire bytes: agree register, then disagree register. - #[must_use] - pub fn to_le_bytes(&self) -> [u8; BASIS_PAIR_BYTES] { - let mut b = [0u8; BASIS_PAIR_BYTES]; - b[..BASIS_REGISTER_BYTES].copy_from_slice(&self.agree); - b[BASIS_REGISTER_BYTES..].copy_from_slice(&self.disagree); - b - } - - /// Read a pair from its wire bytes — total, every pattern representable. - #[must_use] - pub fn from_le_bytes(b: &[u8; BASIS_PAIR_BYTES]) -> Self { - let mut agree = [0u8; BASIS_REGISTER_BYTES]; - let mut disagree = [0u8; BASIS_REGISTER_BYTES]; - agree.copy_from_slice(&b[..BASIS_REGISTER_BYTES]); - disagree.copy_from_slice(&b[BASIS_REGISTER_BYTES..]); - Self { agree, disagree } - } -} - -/// The canonical field-map queries as **8-bit ternlog truth tables** -/// (operator, 2026-09-01: ternary-mask SIMD ops execute any combination of -/// stacked masks). Convention matches ndarray's shipped W1a-#9 primitive -/// (`ndarray::simd::{U64x8::ternlog, U32x16::ternlog, ternlog::*}` — -/// native `VPTERNLOGQ` on AVX-512, portable elsewhere): IMM bit index = -/// `(a << 2) | (b << 1) | c`, here with `a` = support mask, `b` = refute -/// mask, `c` = the question mask. -/// -/// A field SWEEP over N nodes is three parallel u64-mask columns -/// (support, refute, question) — SoA — so every query below is ONE ternlog -/// per 8 nodes. The contract stays zero-dep: an IMM is a number, and -/// [`eval_ternlog`] is the scalar semantics ORACLE the SIMD path is -/// equivalence-tested against, mirroring the W1a parity discipline. -/// -/// Two of these are ndarray's own named tables (`ASKED_CONTESTED` = -/// `ternlog::AND3` = 0x80; `ASKED_ANSWERED` = `ternlog::OR2_AND` = 0xA8) — -/// pinned numerically here since the contract cannot depend on ndarray. -pub mod sweep_ternlog { - /// `support & refute & question` — contested AND asked (Belnap Both ∩ Q). - pub const ASKED_CONTESTED: u8 = 0x80; - /// `support & !refute & question` — cleanly supported AND asked. - pub const ASKED_SUPPORTED_ONLY: u8 = 0x20; - /// `!support & refute & question` — cleanly refuted AND asked. - pub const ASKED_REFUTED_ONLY: u8 = 0x08; - /// `!support & !refute & question` — **asked but SILENT: the missing - /// link**, the field map's highest-value cell, as one instruction. - pub const ASKED_SILENT: u8 = 0x02; - /// `(support | refute) & question` — asked and answered either way. - pub const ASKED_ANSWERED: u8 = 0xA8; -} - -/// **Axis-catalogue mirror** — the named 24-axis basis, v3 (authority: -/// `ogar-epistemic`, concept id `0x0334` in the reserved Ontology domain; -/// the facet classid names AND versions the basis, so a superseded -/// catalogue is a v4 mint, never a re-label). Zero-dep mirror, armed -/// parity on the lance-graph-ogar side once the authority is on OGAR main. -/// -/// Six groups of four, `group = axis >> 2`; every axis is a PRESSURE -/// grounded in a shipped surface (the authority crate lists the grounding -/// per row). `QUORUM`/`CONTRADICTION` are magnitude twins of the A9 loci -/// of the same names — the locus points at WHO, the axis carries HOW MUCH. -pub mod axes { - use super::BASIS_AXES; - - /// Group names, positional: `GROUPS[axis >> 2]`. - pub const GROUPS: [&str; 6] = [ - "set", - "evidence", - "derivation", - "field", - "circumstance", - "witness", - ]; - - /// The 24 axis names, index-aligned with the register's lanes. - pub const AXIS_LABELS: [&str; BASIS_AXES] = [ - "IS_A", - "PART_OF", - "TYPICALITY", - "MISSING_LINK", - "SUPPORT", - "REFUTE", - "PARTIAL", - "REPLICATION", - "PREMISE", - "DEDUCTION", - "FALSIFIER", - "COUNTERFACTUAL", - "INFO_GAIN", - "TENSION", - "COHERENCE", - "AMBIGUITY", - "TEMPORAL", - "KAUSAL", - "MODAL", - "LOKAL", - "PROVENANCE", - "REVISION", - "QUORUM", - "CONTRADICTION", - ]; - - /// The v3 basis concept id (mirror of the authority's reservation). - pub const BASIS_V3_CONCEPT_ID: u16 = 0x0334; -} - -/// **Loco band mirror** — the ogar-loco shared-core `FnIndex` assignments -/// for these primitives (minted 2026-09-01 into loco's reserved core slots; -/// the operator's essence-directive: primitives like Belnap become reusable -/// bytecode macros in loco, for literally everything). Zero-dep mirror, -/// same discipline as `dismech_evidence`'s palette mirror: the contract -/// cannot depend on ogar-loco, so the armed parity check in -/// `lance-graph-ogar` asserts both sides byte-for-byte. Hambly-Lyons has -/// NO index while jc Pillar 11 is red. -pub mod loco_band { - /// `(FnIndex, name, stack arity)` for each epistemic call, `0x86..=0x8B`. - pub const EPISTEMIC_CALLS: &[(u8, &str, u8)] = &[ - (0x86, "TERNLOG", 3), - (0x87, "BELNAP_JOIN", 2), - (0x88, "INFO_GAIN", 2), - (0x89, "SIGMA_TENSION", 2), - (0x8A, "ACCUMULATE", 1), - (0x8B, "STANCE_ENTROPY", 2), - ]; -} - -/// Scalar reference evaluation of a ternlog truth table over three masks — -/// the semantics oracle for the SIMD path (which lives in `ndarray::simd`, -/// never here). Bit `k` of the result is `(imm >> ((a_k << 2) | (b_k << 1) -/// | c_k)) & 1`. -#[must_use] -pub fn eval_ternlog(imm: u8, a: u64, b: u64, c: u64) -> u64 { - let mut out = 0u64; - for k in 0..64 { - let idx = (((a >> k) & 1) << 2) | (((b >> k) & 1) << 1) | ((c >> k) & 1); - out |= ((u64::from(imm) >> idx) & 1) << k; - } - out -} - -/// Shannon entropy in bits over a count distribution; `0.0` on an empty one. -fn shannon(counts: &[usize]) -> f32 { - let n: usize = counts.iter().sum(); - if n == 0 { - return 0.0; - } - let n = n as f32; - let mut h = 0.0f32; - for &k in counts { - if k > 0 { - let p = k as f32 / n; - h -= p * p.log2(); - } - } - h -} - -// ═══════════════════════════════════════════════════════════════════════════ -// Readout adapters — each lane family grounded in a SHIPPED certificate, -// checked against the tree rather than invented (2026-09-01): -// -// | lane family | exact carrier (stays exact) | lane carries (u4) | shipped surface | -// |---|---|---|---| -// | support / falsifier pressure (Tarski) | premise ancestry / W / CE64 | the pair's own counts | this type | -// | ΔH / expected info gain (Shannon) | the candidate distribution | `info_gain_u4` | `dismech_candidates::Evaluation` counts | -// | tension / residual (EWA) | `sigma_propagation::Spd2` Σ | `sigma_tension_u4` | `ewa_sandwich` + `log_norm_growth` + `pillar_5plus_bound` (Pillar 6, jc-verified 10000/10000 PSD) | -// | path-signature identity (Hambly-Lyons) | — | **NO LANE YET** | sigker's classification is ASSERTED, gated on jc Pillar 11 (DEFERRED) — same rule as the red-pillar mint gate: no lane while the pillar is red | -// ═══════════════════════════════════════════════════════════════════════════ - -/// Shannon expected-information-gain readout, quantized to a u4 lane: -/// `log2(before / after)` bits of candidate-set narrowing, floored per -/// whole bit, saturated at [`AXIS_COUNT_MAX`]. -/// -/// Proprioception, NOT evidence: the exact quantity stays derivable from -/// the candidate distribution itself (`dismech_candidates::Evaluation` -/// carries the counts); this lane only lets a node FEEL how sharply its -/// question is narrowing. `after == 0` — complete elimination, the -/// contradiction case — reads as maximal pressure (15). No narrowing -/// (`after >= before`) reads `0`. -#[must_use] -pub fn info_gain_u4(before: usize, after: usize) -> u8 { - if after == 0 { - return if before == 0 { 0 } else { AXIS_COUNT_MAX }; - } - if after >= before { - return 0; - } - let bits = (before as f64 / after as f64).log2(); - (bits.floor() as u64).min(u64::from(AXIS_COUNT_MAX)) as u8 -} - -/// EWA-tension readout, quantized to a u4 lane: `|log_norm_growth|` -/// measured in QUARTERS of the runtime concentration bound -/// (`sigma_propagation::pillar_5plus_bound`-derived), saturated at 15. -/// `4` = exactly at the certificate; `> 7` = past the 1.75× PASS slack -/// Pillar 6 rejects at. -/// -/// The exact Σ stays in the SPD carrier; this lane is transformed -/// tension/residual only. A degenerate (`<= 0`) bound with nonzero -/// growth reads as maximal pressure — fail-loud, never fail-silent. -#[must_use] -pub fn sigma_tension_u4(growth: f64, bound: f64) -> u8 { - let g = growth.abs(); - if bound <= 0.0 { - return if g == 0.0 { 0 } else { AXIS_COUNT_MAX }; - } - let quarters = (g / bound) * 4.0; - if !quarters.is_finite() { - return AXIS_COUNT_MAX; - } - (quarters.ceil() as u64).min(u64::from(AXIS_COUNT_MAX)) as u8 -} - -#[cfg(test)] -mod tests { - /// The universal form of `basin_lanes`' `+3 / −3` collapse pin: at EVERY - /// representable magnitude the pair keeps balanced conflict distinct from - /// silence, with the old carrier's whole output (`net == 0`) reproduced - /// and the added coordinate (`contest == x`) carrying the difference. - /// Disable arm: derive `axis_state` from `net` instead of the pair and - /// every iteration goes red — exactly the sweep the transcribed old - /// encoder produced (`x = 1..=7` all `SILENT`). - #[test] - fn the_pair_distinguishes_balanced_conflict_from_silence_at_every_magnitude() { - for x in 1u8..=AXIS_COUNT_MAX { - let mut sup = [0u8; BASIS_AXES]; - sup[5] = x; - let mut refu = [0u8; BASIS_AXES]; - refu[5] = x; - let p = EpistemicBassin24::accumulate_children(&[ - EpistemicBassin24::pack(&sup, &[0; BASIS_AXES]), - EpistemicBassin24::pack(&[0; BASIS_AXES], &refu), - ]); - assert_ne!(p, EpistemicBassin24::SILENT, "x={x}"); - assert_eq!(p.axis_state(5), AxisState::Contested, "x={x}"); - assert_eq!(p.net(5), 0, "x={x}"); - assert_eq!(p.contest(5), x, "x={x}"); - } - } - - /// `accumulate_children` is an MV-monoid on L₁₆ per side: exact - /// sum-then-clamp is associative and commutative (Chang 1958), so any - /// hop-tree fold order gives a bit-identical register. What the clamp - /// costs is CANCELLATIVITY (`15 ⊕ 1 == 15 ⊕ 2`), i.e. understated mass — - /// never order-dependence and never conflict turned into silence. - #[test] - fn accumulation_is_associative_and_non_cancellative_per_side() { - let lane = |a: u8, d: u8| { - let mut s = [0u8; BASIS_AXES]; - s[0] = a; - let mut r = [0u8; BASIS_AXES]; - r[0] = d; - EpistemicBassin24::pack(&s, &r) - }; - let acc = EpistemicBassin24::accumulate_children; - let mut non_cancel = 0usize; - for a in 0..=AXIS_COUNT_MAX { - for b in 0..=AXIS_COUNT_MAX { - for c in 0..=AXIS_COUNT_MAX { - let l = acc(&[acc(&[lane(a, c), lane(b, a)]), lane(c, b)]); - let r = acc(&[lane(a, c), acc(&[lane(b, a), lane(c, b)])]); - assert_eq!(l, r, "assoc a={a} b={b} c={c}"); - if b != c && acc(&[lane(a, 0), lane(b, 0)]) == acc(&[lane(a, 0), lane(c, 0)]) { - non_cancel += 1; - } - } - } - } - assert!( - non_cancel > 0, - "saturation must cost cancellativity somewhere" - ); - assert_eq!(non_cancel, 1360, "the L16 non-cancellation census"); - } - - use super::*; - - #[test] - fn round_trips_and_saturates_per_side() { - let mut a = [0u8; BASIS_AXES]; - let mut d = [0u8; BASIS_AXES]; - for i in 0..BASIS_AXES { - a[i] = (i % 16) as u8; - d[i] = ((i + 5) % 16) as u8; - } - let p = EpistemicBassin24::pack(&a, &d); - assert_eq!(p.agree_counts(), a); - assert_eq!(p.disagree_counts(), d); - assert_eq!(EpistemicBassin24::from_le_bytes(&p.to_le_bytes()), p); - // over-range saturates, never wraps - let over = EpistemicBassin24::pack(&[200; BASIS_AXES], &[16; BASIS_AXES]); - assert_eq!(over.agree_counts(), [15; BASIS_AXES]); - assert_eq!(over.disagree_counts(), [15; BASIS_AXES]); - } - - /// **The REVERSED falsifier** (operator co-architect ruling): equal - /// support and refutation MUST be distinguishable from silence. This is - /// the exact case the superseded signed net provably collapsed - /// (`basin_lanes`' pinned test) — here it is a first-class state. - #[test] - fn equal_support_and_refutation_is_distinguishable_from_silence() { - let mut a = [0u8; BASIS_AXES]; - let mut d = [0u8; BASIS_AXES]; - a[5] = 3; - d[5] = 3; - let p = EpistemicBassin24::pack(&a, &d); - assert!(!p.is_silent(), "balanced conflict is NOT silence"); - assert_eq!(p.axis_state(5), AxisState::Contested); - assert_eq!(p.net(5), 0, "the net projection still exists…"); - assert_eq!( - p.contest(5), - 3, - "…but no longer forgets the conflict under it" - ); - assert_eq!(p.axis_state(6), AxisState::Silent); - assert_ne!(p, EpistemicBassin24::SILENT); - } - - /// Conflict survives ACCUMULATION too — the children whose stances the - /// signed net cancelled at the parent now land as (3,3) Contested. - #[test] - fn accumulation_preserves_conflict_instead_of_netting_it_away() { - let mut sup = [0u8; BASIS_AXES]; - sup[5] = 3; - let mut refu = [0u8; BASIS_AXES]; - refu[5] = 3; - let child_a = EpistemicBassin24::pack(&sup, &[0; BASIS_AXES]); - let child_b = EpistemicBassin24::pack(&[0; BASIS_AXES], &refu); - let parent = EpistemicBassin24::accumulate_children(&[child_a, child_b]); - assert_eq!(parent.axis_state(5), AxisState::Contested); - assert_eq!(parent.agree_counts()[5], 3); - assert_eq!(parent.disagree_counts()[5], 3); - assert!(EpistemicBassin24::accumulate_children(&[]).is_silent()); - // order-independent within one call (exact sums, one clamp) - assert_eq!( - EpistemicBassin24::accumulate_children(&[child_a, child_b]), - EpistemicBassin24::accumulate_children(&[child_b, child_a]), - ); - } - - #[test] - fn entropy_is_zero_for_pure_and_maximal_at_the_uniform_four_state_mix() { - assert_eq!(EpistemicBassin24::SILENT.entropy_bits(), 0.0); - let mut a = [0u8; BASIS_AXES]; - let mut d = [0u8; BASIS_AXES]; - for i in 0..BASIS_AXES { - match i % 4 { - 1 => a[i] = 2, - 2 => d[i] = 2, - 3 => { - a[i] = 2; - d[i] = 2; - } - _ => {} - } - } - let p = EpistemicBassin24::pack(&a, &d); - assert_eq!(p.state_counts(), [6, 6, 6, 6]); - assert!( - (p.entropy_bits() - 2.0).abs() < 1e-6, - "uniform 4-state = 2 bits" - ); - // and Contested RAISES the readout instead of vanishing from it: - // the same axes under the signed net would read 6 agree, 6 disagree, - // 12 silent (the contested cell folded into silence). - } - - #[test] - fn stance_entropy_sees_a_two_way_contest_across_children() { - let mut sup = [0u8; BASIS_AXES]; - sup[7] = 1; - let mut refu = [0u8; BASIS_AXES]; - refu[7] = 1; - let kids = [ - EpistemicBassin24::pack(&sup, &[0; BASIS_AXES]), - EpistemicBassin24::pack(&[0; BASIS_AXES], &refu), - ]; - assert_eq!(EpistemicBassin24::stance_entropy_bits(&kids, 7), 1.0); - assert_eq!(EpistemicBassin24::stance_entropy_bits(&kids, 8), 0.0); - assert_eq!(EpistemicBassin24::stance_entropy_bits(&[], 0), 0.0); - } - - #[test] - fn info_gain_reads_whole_bits_of_narrowing_and_saturates() { - assert_eq!(info_gain_u4(8, 8), 0, "no narrowing"); - assert_eq!(info_gain_u4(8, 4), 1); - assert_eq!(info_gain_u4(8, 1), 3); - assert_eq!( - info_gain_u4(1 << 20, 1), - 15, - "saturates at the lane ceiling" - ); - assert_eq!( - info_gain_u4(5, 0), - 15, - "complete elimination = max pressure" - ); - assert_eq!(info_gain_u4(0, 0), 0, "no candidates, no gain"); - assert_eq!(info_gain_u4(4, 8), 0, "widening is not gain"); - } - - #[test] - fn sigma_tension_is_calibrated_to_the_pillar_bound() { - assert_eq!(sigma_tension_u4(0.0, 1.0), 0); - assert_eq!( - sigma_tension_u4(1.0, 1.0), - 4, - "at the certificate = 4 quarters" - ); - assert_eq!( - sigma_tension_u4(-1.0, 1.0), - 4, - "sign of growth is not tension" - ); - assert_eq!( - sigma_tension_u4(1.75, 1.0), - 7, - "exactly the 1.75x PASS slack = 7 quarters" - ); - assert!(sigma_tension_u4(1.76, 1.0) > 7, "past the slack"); - assert_eq!(sigma_tension_u4(100.0, 1.0), 15); - assert_eq!( - sigma_tension_u4(0.5, 0.0), - 15, - "degenerate bound fails loud" - ); - assert_eq!(sigma_tension_u4(0.0, 0.0), 0); - // The bound must actually DIVIDE — proven at bounds where skipping - // the division changes the answer (a first disable-run passed - // because every arm above uses bound = 1.0, where g/bound == g, and - // the real-bound arm at n=4 coincidentally rounded to the same - // quarter). - assert_eq!(sigma_tension_u4(2.0, 2.0), 4, "at a 2.0 certificate, not 8"); - assert_eq!( - sigma_tension_u4(0.5, 2.0), - 1, - "a quarter of a 2.0 bound, not 2" - ); - // grounded against the REAL shipped certificate, not a made-up bound - let b = crate::sigma_propagation::pillar_5plus_bound(100); - assert!(b > 0.0 && b < 0.5, "n=100 gives a decisively sub-1.0 bound"); - assert_eq!( - sigma_tension_u4(b, b), - 4, - "at the certificate regardless of its size" - ); - } - /// **The Belnap-join theorem, pinned.** The state layer of one-hop - /// accumulation IS the knowledge-order join of the children's states: - /// bitwise OR of the support masks, bitwise OR of the refute masks. - /// Holds because counts only ADD — a parent side is nonzero iff some - /// child's is. The netting disable (subtracting the overlap inside - /// accumulate) breaks exactly this: a contested axis would drop out of - /// the join. - #[test] - fn accumulations_state_layer_is_the_belnap_knowledge_join() { - let mut a1 = [0u8; BASIS_AXES]; - let mut d1 = [0u8; BASIS_AXES]; - let mut a2 = [0u8; BASIS_AXES]; - let mut d2 = [0u8; BASIS_AXES]; - a1[0] = 2; // pure agree in child 1 - d1[3] = 1; // pure disagree in child 1 - a2[3] = 4; // …axis 3 becomes Contested only at the JOIN - d2[9] = 15; // saturated refute - a1[9] = 1; - let kids = [ - EpistemicBassin24::pack(&a1, &d1), - EpistemicBassin24::pack(&a2, &d2), - ]; - let parent = EpistemicBassin24::accumulate_children(&kids); - assert_eq!( - parent.support_mask(), - kids[0].support_mask() | kids[1].support_mask() - ); - assert_eq!( - parent.refute_mask(), - kids[0].refute_mask() | kids[1].refute_mask() - ); - // and the join genuinely CREATED a Both cell neither child had: - assert_eq!(kids[0].contested_mask() | kids[1].contested_mask(), 0); - assert_eq!(parent.contested_mask(), (1 << 3) | (1 << 9)); - } - - #[test] - fn the_four_masks_partition_the_24_axes() { - let mut a = [0u8; BASIS_AXES]; - let mut d = [0u8; BASIS_AXES]; - a[1] = 3; // Agree - d[2] = 3; // Disagree - a[3] = 1; - d[3] = 1; // Contested - let p = EpistemicBassin24::pack(&a, &d); - let pure_agree = p.support_mask() & !p.refute_mask(); - let pure_disagree = p.refute_mask() & !p.support_mask(); - // pairwise disjoint, jointly exhaustive over the 24 bits - assert_eq!( - pure_agree | pure_disagree | p.contested_mask() | p.silent_mask(), - AXIS_MASK_ALL - ); - assert_eq!(pure_agree & p.contested_mask(), 0); - assert_eq!(pure_disagree & p.contested_mask(), 0); - assert_eq!(p.silent_mask() & (p.support_mask() | p.refute_mask()), 0); - // and the masks agree with axis_state, axis by axis - for axis in 0..BASIS_AXES { - let bit = 1u64 << axis; - let expect = match p.axis_state(axis) { - AxisState::Silent => p.silent_mask(), - AxisState::Agree => pure_agree, - AxisState::Disagree => pure_disagree, - AxisState::Contested => p.contested_mask(), - }; - assert_ne!(expect & bit, 0, "axis {axis} mask/state disagree"); - } - } - - /// The masks plug into the SHIPPED mask algebra — kind-3 question - /// masking bit-parallel over the axes, via `EvidenceMask for u64`. - #[test] - fn belnap_masks_compose_with_the_shipped_evidence_mask_algebra() { - use crate::revision::EvidenceMask; - let mut a = [0u8; BASIS_AXES]; - let mut d = [0u8; BASIS_AXES]; - a[0] = 1; - a[5] = 2; - d[5] = 2; - let p = EpistemicBassin24::pack(&a, &d); - let question: u64 = (1 << 5) | (1 << 7); // the axes this case asks about - assert!( - p.contested_mask().intersects(&question), - "axis 5 is contested AND asked" - ); - assert_eq!(p.support_mask().intersection(&question), 1 << 5); - assert!( - p.silent_mask().intersects(&(1u64 << 7)), - "asked but silent — a missing link, visible as such" - ); - } - /// Every sweep truth table is pinned EXHAUSTIVELY (all 8 input combos) - /// against its scalar formula — a wrong IMM cannot survive one row. - #[test] - fn sweep_ternlog_tables_match_their_formulas_on_all_eight_rows() { - for a in [0u64, 1] { - for b in [0u64, 1] { - for c in [0u64, 1] { - let row = |imm: u8| eval_ternlog(imm, a, b, c) & 1; - assert_eq!(row(sweep_ternlog::ASKED_CONTESTED), a & b & c); - assert_eq!(row(sweep_ternlog::ASKED_SUPPORTED_ONLY), a & !b & c & 1); - assert_eq!(row(sweep_ternlog::ASKED_REFUTED_ONLY), !a & b & c & 1); - assert_eq!(row(sweep_ternlog::ASKED_SILENT), !a & !b & c & 1); - assert_eq!(row(sweep_ternlog::ASKED_ANSWERED), (a | b) & c); - } - } - } - } - - /// …and the tables compute the SAME masks the direct expressions do on a - /// real register — the ternlog path and the mask-projection path can - /// never drift apart. - #[test] - fn sweep_ternlog_agrees_with_the_direct_mask_expressions() { - let mut ag = [0u8; BASIS_AXES]; - let mut di = [0u8; BASIS_AXES]; - ag[0] = 1; // supported - di[2] = 3; // refuted - ag[5] = 2; - di[5] = 2; // contested - let p = EpistemicBassin24::pack(&ag, &di); - let (s, r) = (p.support_mask(), p.refute_mask()); - let q: u64 = (1 << 0) | (1 << 2) | (1 << 5) | (1 << 9); // 9 = asked, silent - assert_eq!( - eval_ternlog(sweep_ternlog::ASKED_CONTESTED, s, r, q), - p.contested_mask() & q - ); - assert_eq!( - eval_ternlog(sweep_ternlog::ASKED_SILENT, s, r, q), - p.silent_mask() & q - ); - assert_eq!(eval_ternlog(sweep_ternlog::ASKED_SILENT, s, r, q), 1 << 9); - assert_eq!( - eval_ternlog(sweep_ternlog::ASKED_ANSWERED, s, r, q), - (s | r) & q - ); - assert_eq!( - eval_ternlog(sweep_ternlog::ASKED_SUPPORTED_ONLY, s, r, q), - 1 << 0 - ); - assert_eq!( - eval_ternlog(sweep_ternlog::ASKED_REFUTED_ONLY, s, r, q), - 1 << 2 - ); - } - #[test] - fn the_axis_catalogue_covers_the_register_exactly_once() { - use super::axes::{AXIS_LABELS, GROUPS}; - assert_eq!(AXIS_LABELS.len(), BASIS_AXES); - assert_eq!( - GROUPS.len() * 4, - BASIS_AXES, - "six groups of four, group = axis >> 2" - ); - for (i, a) in AXIS_LABELS.iter().enumerate() { - for b in AXIS_LABELS.iter().skip(i + 1) { - assert_ne!(a, b, "axis names are unique"); - } - } - // The whale axis is where the whole arc says it is. - assert_eq!(AXIS_LABELS[0], "IS_A"); - // The missing link pairs with its one-instruction sweep query. - assert_eq!(AXIS_LABELS[3], "MISSING_LINK"); - // The magnitude twins of the A9 pointer loci. - assert_eq!(AXIS_LABELS[22], "QUORUM"); - assert_eq!(AXIS_LABELS[23], "CONTRADICTION"); - } -} diff --git a/crates/lance-graph-contract/src/hhtl.rs b/crates/lance-graph-contract/src/hhtl.rs index 3331fd93a..b687928eb 100644 --- a/crates/lance-graph-contract/src/hhtl.rs +++ b/crates/lance-graph-contract/src/hhtl.rs @@ -510,9 +510,9 @@ impl NiblePath { /// leaf paths and this is the skeleton to spawn). /// /// Deliberately returns **addresses only**. Mechanical hydration mints -/// structure from the hierarchy; the epistemic value of every minted row is -/// [`BasinLanes::SILENT`](crate::basin_lanes::BasinLanes::SILENT) — this -/// function's signature cannot carry a lane value, which is the type-level +/// structure from the hierarchy; a minted row carries NO epistemic value +/// (its value register is zero until a producer writes one) — this +/// function's signature cannot carry a value at all, which is the type-level /// half of the mechanical-vs-epistemic split (a mint that writes knowledge /// is structure impersonating knowledge). /// diff --git a/crates/lance-graph-contract/src/lib.rs b/crates/lance-graph-contract/src/lib.rs index a495c783b..2337fb1a4 100644 --- a/crates/lance-graph-contract/src/lib.rs +++ b/crates/lance-graph-contract/src/lib.rs @@ -53,7 +53,6 @@ pub mod attention_facet; pub mod auth; pub mod awareness_facet; pub mod band_reading; -pub mod basin_lanes; pub mod callcenter; pub mod cam; pub mod canonical_node; @@ -96,7 +95,6 @@ pub mod emission_scan; /// D-ACR-6 — the LE codec for the `EpisodicBasin` rail (references, never content). pub mod episodic_basin; pub mod episodic_edges; -pub mod epistemic_bassin; pub mod escalation; pub mod exploration; pub mod external_membrane; diff --git a/crates/lance-graph-contract/tests/w2b_one_node_field.rs b/crates/lance-graph-contract/tests/w2b_one_node_field.rs deleted file mode 100644 index 0fe5a56bc..000000000 --- a/crates/lance-graph-contract/tests/w2b_one_node_field.rs +++ /dev/null @@ -1,284 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Copyright The Lance Authors - -//! **W2b — the ONE-NODE field falsifier** (`D-DCR-2b`). -//! -//! The W2b ruling says a position summarises its children in **24 signed -//! dimensions**, and that the SIGN collapses three states into one quantity: -//! `+` agreement, `−` disagreement, `0` silence. This file is the falsifier for -//! that carrier at the scale where it is decidable today: **one node**. -//! -//! # What this proves, and what it deliberately does NOT -//! -//! | | | -//! |---|---| -//! | **proves** | the 24×i4 carrier holds the three states distinguishably, round-trips through the 12-byte register, saturates instead of wrapping, and — the load-bearing one — that a negative lane is **not** a removal | -//! | **does NOT prove** | anything about a global sweep. Gap 3 (convergence semantics for a field-wide fixpoint) is fully open and one node cannot speak to it: one hop is a function, a field map is a fixpoint | -//! | **does NOT decide** | which value lane the summary occupies. That is an open operator decision; nothing here reserves a byte or mints a `ValueTenant` | -//! | **does NOT canonise** | the summarising *arithmetic* (gap 1). [`summarise`] below is this test's own minimal choice, marked as such — the ruling fixes the SIGN's meaning, not the sum | -//! -//! # Why it borrows `CausalWitnessFacet` as the codec -//! -//! The `G24N4` nibble codec already ships there, and this workspace consumes -//! rather than re-implements. That borrowing is **codec-only** and is not a -//! claim that W2b belongs in that lane — it does not: -//! -//! - `CausalWitness`'s value law is operator-locked to **loci, never -//! strength/magnitude**, and a W2b summary is the magnitude case; -//! - its slots `16..24` are RESERVE-DON'T-RECLAIM. -//! -//! Both are reasons W2b needs its own lane, recorded in the census beside this -//! file. Here the facet is used purely as "the shipped 24-nibble codec". -//! -//! **Post-census landing (parallel branch, reconciled on rebase):** the -//! magnitude sibling this file calls for now exists — -//! `lance_graph_contract::basin_lanes::BasinLanes` — together with the -//! one-hop accumulator `accumulate_children` (operator-ruled: a parent -//! expresses its DIRECT children only). [`summarise`] below and -//! `accumulate_children` arrived at the SAME per-lane shape (exact signed -//! sum, one clamp) independently — convergent evidence, kept in both places -//! deliberately: this file pins the register-level carrier through the -//! borrowed codec, `basin_lanes`' tests pin the production carrier. - -use lance_graph_contract::causal_witness::{CausalWitnessFacet, WITNESS_LOCI}; - -/// What one child says about one dimension. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum ChildVote { - Agrees, - Disagrees, - Silent, -} - -/// **This test's own arithmetic, NOT a ruling.** Gap 1 leaves the summarising -/// function open; the ruling fixes only what the sign MEANS. The minimal choice -/// that honours it: agreement counts up, disagreement counts down, silence -/// contributes nothing — then the carrier clamps. -fn summarise(votes: &[ChildVote]) -> i8 { - let mut acc: i32 = 0; - for v in votes { - acc += match v { - ChildVote::Agrees => 1, - ChildVote::Disagrees => -1, - ChildVote::Silent => 0, - }; - } - acc.clamp(-8, 7) as i8 -} - -/// **F1 — the three states are distinguishable through the register.** -/// -/// Anti-vacuity: round-tripping each value is not enough. An implementation -/// that stored the MAGNITUDE and dropped the sign would round-trip every -/// non-negative case, so this also pins that agreement and disagreement of the -/// same magnitude produce **different registers**. -/// -/// Disable: make `summarise` return `acc.abs()` ⇒ red. -#[test] -fn agreement_disagreement_and_silence_are_three_distinct_readings() { - let agree = summarise(&[ChildVote::Agrees; 3]); - let disagree = summarise(&[ChildVote::Disagrees; 3]); - let silent = summarise(&[ChildVote::Silent; 3]); - - assert_eq!(agree, 3, "three agreeing children read as +3"); - assert_eq!(disagree, -3, "three disagreeing children read as -3"); - assert_eq!(silent, 0, "silence is zero, not a weak agreement"); - - let mut a = CausalWitnessFacet::default(); - a.set(0, agree); - let mut d = CausalWitnessFacet::default(); - d.set(0, disagree); - let s = CausalWitnessFacet::default(); - - assert_eq!(a.get(0), 3, "the register must hand back what was written"); - assert_eq!(d.get(0), -3, "sign survives the nibble round-trip"); - assert_eq!(s.get(0), 0); - - // The half a magnitude-only implementation fails. - assert_ne!( - a.to_register(), - d.to_register(), - "same magnitude, opposite sign: the two must not share a register" - ); - assert_ne!( - d.to_register(), - s.to_register(), - "silence must be distinguishable from disagreement (the missing-link carrier)" - ); -} - -/// A node's membership in a neighbourhood, as asserted by the RAIL — a carrier -/// that is not the field lane. The whale case turns on these being separate -/// bytes, which is what F2 checks structurally. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -struct RailMembership { - is_a_mammal: bool, -} - -/// The write under test: record a per-dimension summary on the node. -/// -/// Correct behaviour takes `rail` by shared reference — a summary write has no -/// business touching membership. The signature is what makes F2 falsifiable: -/// change it to `&mut` and act on the lane's sign, and F2 goes red. -fn record_summary( - field: &mut CausalWitnessFacet, - slot: usize, - summary: i8, - _rail: &RailMembership, -) { - field.set(slot, summary); -} - -/// **F2 — THE WHALE CASE. A negative lane is a value, not a removal.** -/// -/// The ruling's own example: *"a whale records as a negative lane against the -/// mammal neighbourhood and stays a mammal, because a lane is a value and not a -/// removal."* This is the falsifier the kind-1/kind-3 ordering rests on — if -/// disagreement eliminated, the sweep would already be doing kind 3's job. -/// -/// **Why this is structural and not a restated constant.** A first version -/// asserted `whale.is_a_mammal` after building `RailMembership { is_a_mammal: -/// true }` — vacuous: nothing between the two lines could change it, and its -/// "disable" meant editing the test rather than the code. What is genuinely -/// falsifiable is that the two live in DIFFERENT carriers: sweeping every value -/// the lane can hold — including the whole negative half — must leave the rail -/// byte-identical, and must leave the disagreement legible afterwards. -/// -/// Disable: give [`record_summary`] `rail: &mut RailMembership` and let it -/// clear `is_a_mammal` when `summary < 0` ⇒ red. -#[test] -fn no_value_the_lane_can_hold_revokes_membership() { - let rail = RailMembership { is_a_mammal: true }; - let before = rail; - - // The whole i4 range, negative half included — not one sampled value. - let mut saw_negative = false; - for summary in -8i8..=7 { - let mut field = CausalWitnessFacet::default(); - record_summary(&mut field, 0, summary, &rail); - - assert_eq!( - rail, before, - "writing summary {summary} changed the membership carrier" - ); - assert_eq!( - field.get(0), - summary, - "and the summary must stay legible after the write, not be consumed by a decision" - ); - if summary < 0 { - saw_negative = true; - } - } - assert!( - saw_negative, - "the sweep must actually exercise the negative half, or it proves nothing about disagreement" - ); - - // The hard case named explicitly: maximal disagreement, still a mammal. - let votes = [ChildVote::Disagrees; 12]; - let summary = summarise(&votes); - assert_eq!(summary, -8, "the fixture must reach the floor"); - let mut field = CausalWitnessFacet::default(); - record_summary(&mut field, 0, summary, &rail); - assert_eq!(rail.is_a_mammal, before.is_a_mammal); - assert_eq!(field.get(0), -8); -} - -/// **F3 — saturation, never wrap.** A disagreement stronger than the carrier -/// can hold must clamp to `−8`. A wrap would turn strong disagreement into -/// agreement — silently, and in the direction that inverts the answer. -/// -/// Disable: replace the clamp in `summarise` with `acc as i8` ⇒ red (`-20 as -/// i8` is `-20`, which `set` then clamps, but `-24` wraps through the nibble to -/// `+8`-shaped garbage); the assertion below pins the sign, which is what -/// actually matters. -#[test] -fn overflowing_disagreement_saturates_and_never_flips_sign() { - for n in [9usize, 12, 40, 1000] { - let votes = vec![ChildVote::Disagrees; n]; - let summary = summarise(&votes); - assert_eq!( - summary, -8, - "{n} disagreeing children must clamp to the floor" - ); - - let mut f = CausalWitnessFacet::default(); - f.set(0, summary); - assert!( - f.get(0) < 0, - "{n} disagreeing children read back as {} — a sign flip is the silent catastrophe", - f.get(0) - ); - } - // The same on the agreement side, whose ceiling is +7 (asymmetric by i4). - let many = vec![ChildVote::Agrees; 1000]; - assert_eq!(summarise(&many), 7); -} - -/// **F4 — all 24 lanes are independently addressable.** -/// -/// The classic packed-nibble defect: writing lane `i` disturbs its byte-mate -/// `i^1`. Every lane gets a distinct value and all are read back. -/// -/// Disable: drop the `& 0xF0` / `& 0x0F` masking in the codec ⇒ red. -#[test] -fn every_one_of_the_twenty_four_lanes_holds_its_own_value() { - let mut f = CausalWitnessFacet::default(); - // A pattern where no two adjacent lanes share a value, so a bleed shows. - let value_for = |slot: usize| -> i8 { ((slot as i32 % 15) - 7) as i8 }; - - for slot in 0..WITNESS_LOCI { - f.set(slot, value_for(slot)); - } - for slot in 0..WITNESS_LOCI { - assert_eq!( - f.get(slot), - value_for(slot), - "lane {slot} was disturbed by a neighbour" - ); - } - // Anti-vacuity: the pattern must actually put different values in byte-mates. - assert_ne!( - value_for(0), - value_for(1), - "the fixture cannot detect bleed if byte-mates share a value" - ); - assert_eq!( - WITNESS_LOCI, 24, - "the ruling's width is 24 signed dimensions" - ); -} - -/// **F5 — the summary is derived, and re-deriving it is stable.** -/// -/// One node, same children, twice: identical register. Not a claim about a -/// sweep — a sweep's fixpoint is gap 3 — only that the per-node summarisation -/// is a function of its inputs and carries no hidden state. -#[test] -fn re_summarising_the_same_children_yields_the_same_register() { - let children = [ - ChildVote::Agrees, - ChildVote::Disagrees, - ChildVote::Silent, - ChildVote::Agrees, - ]; - let build = || { - let mut f = CausalWitnessFacet::default(); - for slot in 0..WITNESS_LOCI { - // Rotate the votes per lane so the register is not uniform. - let rotated: Vec = (0..children.len()) - .map(|i| children[(i + slot) % children.len()]) - .collect(); - f.set(slot, summarise(&rotated)); - } - f - }; - assert_eq!(build().to_register(), build().to_register()); - // Anti-vacuity: the register must not be all-zero, or equality is trivial. - assert_ne!( - build().to_register(), - [0u8; 12], - "an all-zero register would make this comparison vacuous" - ); -} diff --git a/crates/lance-graph-ogar/src/lib.rs b/crates/lance-graph-ogar/src/lib.rs index 28ae8cd90..8589fb6ed 100644 --- a/crates/lance-graph-ogar/src/lib.rs +++ b/crates/lance-graph-ogar/src/lib.rs @@ -261,53 +261,6 @@ pub mod parity { ogar_dismech::RELATIONS.len() } - /// Assert the contract's zero-dep loco-band mirror - /// ([`lance_graph_contract::epistemic_bassin::loco_band::EPISTEMIC_CALLS`]) - /// matches the authority — ogar-loco's shared-core table — name and - /// stack arity per index, `0x86..=0x8B`. Returns the number of calls - /// checked; panics on any divergence. - /// - /// Written against the TABLE (`shared_core::{name, stack_arity}` by raw - /// index), never against named constants, so this crate compiles against - /// any ogar-loco revision — the assertions, not the build, carry the - /// parity. Until the loco mint is on OGAR main this check FAILS, which - /// is the honest state of an unmerged band. - pub fn assert_epistemic_band_parity() -> usize { - use lance_graph_contract::epistemic_bassin::loco_band as mirror; - use ogar_loco::vocabulary::shared_core; - - for &(ord, name, arity) in mirror::EPISTEMIC_CALLS { - let f = ogar_loco::FnIndex(ord); - assert!( - f.is_shared_core(), - "{ord:#04x} must sit below DOMAIN_FLOOR — the band is CORE, not domain" - ); - assert_eq!( - shared_core::name(f), - Some(name), - "{ord:#04x}: loco name disagrees with the mirror" - ); - assert_eq!( - shared_core::stack_arity(f), - Some(arity), - "{ord:#04x}: loco arity disagrees with the mirror" - ); - assert_eq!( - shared_core::pushes_result(f), - Some(true), - "{ord:#04x}: every epistemic call pushes its result" - ); - } - // Reverse: nothing minted in the band that the mirror never learned. - for ord in 0x86u8..=0x8B { - assert!( - mirror::EPISTEMIC_CALLS.iter().any(|&(o, _, _)| o == ord), - "loco band slot {ord:#04x} is not mirrored in the contract" - ); - } - mirror::EPISTEMIC_CALLS.len() - } - #[cfg(test)] mod tests { use super::*; @@ -345,14 +298,6 @@ pub mod parity { } } - #[test] - fn the_contract_mirror_is_a_faithful_copy_of_the_epistemic_band() { - let n = assert_epistemic_band_parity(); - // Anti-vacuity: six green-certified atoms, no more (Hambly-Lyons - // stays bandless while jc Pillar 11 is red) and no fewer. - assert_eq!(n, 6, "the epistemic band mints 6 calls"); - } - #[test] fn the_contract_mirror_is_a_faithful_copy_of_the_dismech_palette() { let n = assert_dismech_palette_parity();