From 36cf0360568689b8c5f0225b36b9286022371daf Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 14:08:51 +0000 Subject: [PATCH 01/10] =?UTF-8?q?D-ACR-1:=20the=20attention=20basis=20is?= =?UTF-8?q?=20a=20REUSE=20=E2=80=94=20contract::attention=5Ffacet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The deliverable called RowFocusMask "the one missing primitive". Half of that is right, and the wrong half is the more useful one. THE ATOM ALREADY EXISTED, EXACTLY. contract::facet::FacetCascade is facet_classid(4) | 6x(8:8) = 16 B under CascadeShape::G6D2 -- literally the 6 x 2 x u8 shape asked for, with index/group_of/level_of/shift shipped, tier_bytes() ordered coarse-first, and two readings to copy the pattern from (awareness_facet::SpoFacet, tekamolo_facet::TekamoloFacet). Zero new bytes were needed and none were added: no tenant, no bit, no ENVELOPE_LAYOUT_VERSION bump. WHAT WAS ABSENT is narrower: a composition that is not a bitset union. Every union/intersect in the crate is a bit op over FIELD POSITIONS -- FieldMask (u64, MAX_FIELDS=64), WideFieldMask (u8 positions, capped at 256 with a loud UniverseExceedsSocCap), StepMask, rbac. None composes addresses, and a bit-OR of two addresses is a third address neither side ever visited. That is now a test: OR-ing 0b01 and 0b10 gives 0b11, and the union must not contain it. The right composition was already in the canon one type over. NiblePath::is_ancestor_of is coarse->fine prefix containment with an EXPLICIT depth. Reusing the rule gives `covers`; reusing the explicit-depth discipline avoids a real trap, since inferring the wildcard boundary from zero bytes collides with the zero-fallback ladder where 0 is a dormant tier, not a terminator. depth therefore lives OUTSIDE the 12 bytes and the wire shape stays exactly 6 x 2 x u8. That is also the >256-rows answer: the u8 per level bounds AXIS RESOLUTION, never population. A depth-2 focus is measured covering 65,536 addresses across only the two units varied, and the container holds 1000 entries where FieldMask stops at 64 and WideFieldMask at 256 -- no cap transfers, because the container does not index rows at all. CONTENT-BLINDNESS, defended twice. A first draft named the axes Heel/Hip/Twig/Leaf/Family/Identity, baking the cascade reading into the low-level type -- exactly what FacetCascade's own contract forbids ("only the CONSUMER projects meaning onto the bytes"). Corrected after the operator raised a second candidate reading (six ontology scopes: disease / anatomy / process / substance / evidence / context). FocusAxis is now Axis0..Axis5, a position, and BOTH readings appear only in a test over byte-identical input, proving the projection is free. The lesson: once a second plausible reading exists, any name in the substrate is a premature commitment to the first -- this would have been the fifth homonym collision of the arc, and the first one we minted ourselves. Vertical stacking was considered and rejected: six fixed rows inside one atom would mint a second addressing system for a dimension facet_classid already carries (plan §3: same NodeGuid, different thinking-table row, sparse occupancy), which HTT §2.2 withdrew. One atom, one (classid, rail). Deferred and named: difference() keeps a partially-overlapped entry whole, because subtracting a subtree from a prefix means enumerating siblings -- inventing addresses never visited. Composability with WideFieldMask stays §6 Y2's parked basis collision (positions <= 256 vs an unbounded population), per D-ACR-0's measurement. Does not, and structurally cannot, touch cognitive-shader-driver's attention_mask* -- the dependency edge runs the other way. 14 new tests, 1194 contract tests green, clippy clean, fmt clean. Board hygiene in the same commit: EPIPHANIES prepend, LATEST_STATE contract inventory, STATUS_BOARD D-ACR-1 -> Shipped. --- .claude/board/EPIPHANIES.md | 66 ++ .claude/board/LATEST_STATE.md | 26 + .claude/board/STATUS_BOARD.md | 2 +- .../src/attention_facet.rs | 789 ++++++++++++++++++ crates/lance-graph-contract/src/lib.rs | 1 + 5 files changed, 883 insertions(+), 1 deletion(-) create mode 100644 crates/lance-graph-contract/src/attention_facet.rs diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index f3913dbc4..66f003f9e 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -78,6 +78,72 @@ a hard, loud cap on one side**. D-ACR-1 must state its basis and its composition operator before composing, or it inherits one of two accidents: loud refusal past 256 (borrowing the wide cap) or silent truncation past 64 (borrowing the narrow rule). +## 2026-08-21 — E-THE-ATTENTION-ATOM-WAS-ALREADY-SHIPPED-WHAT-WAS-MISSING-WAS-A-COMPOSITION-THAT-IS-NOT-OR-1 — D-ACR-1's basis is a reuse, and the only real gap was that every set operation in the crate is a bitset union + +**Status:** FINDING (D-ACR-1, implemented; 14 new tests green, 1194 contract +tests green, clippy clean). **Confidence:** High — the atom's existence is a +read of `facet.rs`; the container's absence is a stated grep. + +D-ACR-1 was written as *"the one missing primitive"*. Half of that is right, +and the half that is wrong is the more useful half. + +**The atom already existed, exactly.** `contract::facet::FacetCascade` is +`facet_classid(4) | 6×(8:8) = 16 B` with `CascadeShape::G6D2` — literally the +`6 × 2 × u8` shape D-ACR-1 needed, with `index`/`group_of`/`level_of`/`shift` +shipped, `tier_bytes()` ordered coarse-first (`[t0.hi, t0.lo, …]`), and two +existing readings to copy the pattern from (`awareness_facet::SpoFacet`, +`tekamolo_facet::TekamoloFacet(pub FacetCascade)`). **Zero new bytes were +needed and none were added.** Proposing a new 12-byte type here would have been +the "type that already exists" rediscovery tax `CLAUDE.md` §Consult names. + +**What was genuinely absent is narrower and sharper: a composition that is not +a bitset union.** Measured — every `union`/`intersect` in the contract crate is +a bit operation over *field positions*: `FieldMask` (u64, `MAX_FIELDS = 64`), +`WideFieldMask` (u8 positions, capped at 256 with a loud +`UniverseExceedsSocCap`), `StepMask` (u64), `rbac`. **Not one of them composes +addresses.** And a bit-OR of two addresses is not a coarser focus — it is a +*third address neither side ever visited*. That is the defect the container had +to avoid, and it is now a test (`composition_is_not_a_blind_or`: OR-ing +`0b01` and `0b10` yields `0b11`, and the union must NOT contain it). + +**The composition that IS right was already in the canon, one type over.** +`NiblePath::is_ancestor_of` is coarse→fine prefix containment with an +**explicit** depth field. Reusing that rule on the 12-unit ladder gives +`covers`, and reusing its explicit-depth discipline avoids a real trap: +inferring the wildcard boundary from zero bytes would collide with the +zero-fallback ladder, where `0` is a legitimate *dormant tier*, not a +terminator. So `depth` lives OUTSIDE the 12 bytes and the wire shape stays +exactly `6 × 2 × u8`. + +**This is also the answer to ">256 rows".** The `u8` per level bounds *axis +resolution* (256 centroids), never population: one shallow facet is a wildcard +over an unbounded subtree. Measured in a test — a depth-2 focus covers 65,536 +addresses across only the two units varied, and a container holds 1000 distinct +entries where `FieldMask` would stop at 64 and `WideFieldMask` at 256. **The +container does not index rows at all**, which is why neither cap transfers. + +**Content-blindness had to be defended twice in one deliverable.** A first +draft named the axes `Heel/Hip/Twig/Leaf/Family/Identity` — baking the cascade +reading into the low-level type, which is exactly what `FacetCascade`'s own +contract forbids (*"only the CONSUMER projects meaning onto the bytes"*). The +operator caught it while raising a second candidate reading (six **ontology +scopes**: disease/anatomy/process/substance/evidence/context). Both readings +now live only in a test, over byte-identical input, proving the projection is +free — and `FocusAxis` is `Axis0..Axis5`, a position. The general lesson is the +one this arc keeps paying for: **the moment a second plausible reading appears, +any name in the substrate is a premature commitment to the first.** Four +homonym collisions (witness / nibble / hydration / attention-mask) were the +expensive form of this; naming the axes would have been the fifth, minted by +us rather than inherited. + +**Deferred, named rather than silently absent:** `RowFocusMask::difference` +keeps a partially-overlapped entry whole. Subtracting a subtree from a prefix +requires enumerating siblings — inventing addresses the focus never visited — +so splitting waits for a real consumer that needs it. `D-ACR-1`'s scope line +also asks for composability with `WideFieldMask`; per D-ACR-0's measurement +that is a **cardinality mismatch** (positions ≤ 256 vs an unbounded population) +and remains §6 **Y2**'s parked basis collision — this deliverable states its +own basis and does not cross into the other. ## 2026-08-21 — E-HHTL-IS-MINTED-IN-THE-ARTIFACT-NOBODY-CITES-1 — "zero on every baked row in both production bakes" is precise about the two it names and silent about the third, where the five OBO namespaces are 100% minted diff --git a/.claude/board/LATEST_STATE.md b/.claude/board/LATEST_STATE.md index bca850e75..8167caa14 100644 --- a/.claude/board/LATEST_STATE.md +++ b/.claude/board/LATEST_STATE.md @@ -18,6 +18,32 @@ ## 2026-08-20 — #974 MERGED (8a93423) — DisMech compact evidence vocabulary + citation sidecar +### Current Contract Inventory — 1 new zero-dep module (D-ACR-1) + +- **`lance_graph_contract::attention_facet`** (new, zero new bytes): + - `AttentionFocusFacet` — the **Attention reading** of the shipped + `FacetCascade` (`classid(4) | 6×(8:8)`) under `CascadeShape::G6D2`, plus an + explicit `depth: 0..=12` held OUTSIDE the 12 bytes (the `NiblePath` + precedent — inferring a wildcard from zero bytes would collide with the + zero-fallback ladder, where `0` is a dormant tier, not a terminator). + `exact` / `prefix` (loud refusal past 12) / `whole_class` / `coarse` / + `fine` / `axis` (all `None` past `depth` — never `0`, which is centroid + zero's value) / `covers` / `common_prefix`. + - `FocusAxis` — `Axis0..Axis5`, a **position, not a meaning**. The module + names no axis semantics: the cascade reading (HEEL·HIP·…) and a candidate + ontology-scope reading (disease/anatomy/process/substance/evidence/context) + are both ClassView-resolved projections, demonstrated over identical bytes + in `the_same_atom_reads_as_cascade_and_as_ontology_scope_without_changing_a_byte`. + - `RowFocusMask` — the sparse container. Membership is `covers`, set ops are + containment-shaped (`union` absorbs into a minimal antichain; `intersect` + yields the deeper of a covering pair; `difference` is deliberately + conservative). **Never a bit-OR**, and no `FieldMask`/`WideFieldMask` + cardinality is inherited — it does not index rows at all. + - `FOCUS_AXES = CASCADE_UNITS / 2` — derived, never a second literal. + - **Mints nothing**: no new tenant, no bit, no `ENVELOPE_LAYOUT_VERSION` bump. + Does not, and structurally cannot, reference `cognitive-shader-driver`'s + `attention_mask*` (the dependency edge runs the other way). + ### Current Contract Inventory — 1 new zero-dep module - **`lance_graph_contract::dismech_evidence`** (new): diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index 9fdcd9ba5..690f374c1 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -51,7 +51,7 @@ the rest. | D-id | Scope | Status | Falsifier | |---|---|---|---| | D-ACR-0 | Audit `attention_mask.rs`/`attention_mask_actor.rs`: residue carrier, or a name collision? Report only | **Shipped** 2026-08-21 — `.claude/ATTENTION_MASK_AUDIT_2026_08_21.md`. Verdict **EXISTS-UNCALLED** + a name collision: the shipped type is a *rename register file* (`causaledge64-mailbox-rename-soa-v1.md` §4), not a residue carrier | recorded EXISTS-UNCALLED (0 callers workspace-wide + 3 sibling repos); piece E regraded — not a basis for D-ACR-1 | -| D-ACR-1 | `RowFocusMask` — the one missing primitive (S3.1b names it; no crate contains it) | **Next** — D-ACR-0 cleared it; starts clean (no reuse of `AttentionMaskSoA`). Must state its basis first: `WideFieldMask` is `u8`-capped at 256 positions (loud refusal above), `FieldMask` silently drops `>= 64` — a row population is neither | can-fire AND can-stay-silent on non-trivial input | +| D-ACR-1 | `RowFocusMask` — the one missing primitive (S3.1b names it; no crate contains it) | **Shipped** 2026-08-21 — `contract::attention_facet`. Basis: **reuse** `FacetCascade` + `CascadeShape::G6D2`, zero new bytes; `FocusAxis` is content-blind (`Axis0..Axis5`), composition is **prefix containment**, never bit-OR; explicit `depth` outside the 12 bytes (`NiblePath` precedent) | can-fire AND can-stay-silent on non-trivial input — **both green**, plus a not-a-blind-OR negative and a 1000-entry container (no `FieldMask` 64 / `WideFieldMask` 256 cap inherited) | | D-ACR-3 | The one-way invariant as a test: no ontology-owned write traces to a patient-tagged read through ANY call path (corrected from write-authorization-only after CodeRabbit found a session-derived value can flow to the ontology owner via a shared parameter/return, then be written as the owner's own act) | Queued — gates on D-ACR-1 | a write whose call graph includes a session-tagged read is the bug, even if the write itself is authored by the ontology owner | | D-ACR-2 | Mint the Rung-ladder rail | Queued — gates on operator mint decision (HTT §8 Q3) | `rail_carving` gains its first non-default consumer | | D-ACR-4 | Second-order row at the same address, separate table | Queued | a rung-2 read reconstructs where rung-1 looked, on a fixture with an independent answer | diff --git a/crates/lance-graph-contract/src/attention_facet.rs b/crates/lance-graph-contract/src/attention_facet.rs new file mode 100644 index 000000000..c94f1857f --- /dev/null +++ b/crates/lance-graph-contract/src/attention_facet.rs @@ -0,0 +1,789 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +//! `attention_facet` — the **Attention** reading of the content-blind +//! `6×(8:8)` register, and the sparse container over it (`D-ACR-1`). +//! +//! This is a **reading, not a layout** — the discipline +//! [`crate::awareness_facet::SpoFacet`] and [`crate::tekamolo_facet`] already +//! follow. It re-labels the 12 payload bytes a +//! [`FacetCascade`](crate::facet::FacetCascade) already holds; it reserves, +//! moves and stores **nothing**. Zero new bytes enter the substrate. +//! +//! # The basis decision (`D-ACR-1`), and why it is not six vertical slots +//! +//! The attention atom is the SHIPPED atom: `FacetCascade` = +//! `facet_classid(4) | 6×(8:8) = 16 B`, read under +//! [`CascadeShape::G6D2`](crate::facet::CascadeShape::G6D2) — `6 groups × +//! 2 levels`, `group_of` a pure shift. Nothing is minted. +//! +//! The tempting alternative was a **matrix**: six *vertical* rows of the same +//! 12-byte atom, one per rung/layer/timestep. It is rejected, and the reason is +//! the plan's own §3 (`alpha-channel-rung-overlay-v1.md`): +//! +//! > a second-order thought at node `g` is the **same** `NodeGuid` … a +//! > **different thinking-table row** — a different `(classid, rail)` pairing +//! > resolved by the ClassView … occupancy **sparse**. +//! +//! So the vertical dimension is **already addressed** — it is the +//! `facet_classid` selecting which thinking-table row a focus belongs to. +//! Stacking six fixed rows inside one atom would (a) mint a second addressing +//! system for a dimension the classid already carries, which HTT §2.2 +//! explicitly withdrew (*"one fabric, several ClassView-resolved readings, +//! never alternative addressing systems"*), and (b) force an occupancy of +//! exactly six where §3 requires sparse. **One atom, one `(classid, rail)`; +//! the vertical stack is a set of atoms, not a field inside one.** +//! +//! # The six axes are CONTENT-BLIND — this type names none of them +//! +//! [`FacetCascade`]'s own contract is that *"the substrate is ALWAYS 8:8 … +//! only the CONSUMER projects meaning onto the bytes"*. This module keeps that +//! property: [`FocusAxis`] is `Axis0..Axis5`, a **position**, and +//! [`AttentionFocusFacet`] exposes only `coarse`/`fine` per axis. What an axis +//! MEANS is a **reading contract** resolved by `classid → ClassView`, never a +//! constant here. +//! +//! Two readings are sketched in the tests, over the SAME unchanged bytes, to +//! prove the projection is free: +//! +//! | reading | axis 0 … axis 5 | +//! |---|---| +//! | **cascade** (the key's own tiers) | HEEL · HIP · TWIG · LEAF · family · identity | +//! | **ontology scope** (a domain adapter's) | disease/phenotype · anatomy · process/mechanism · substance/intervention · evidence/provenance · context/time/population | +//! +//! Neither list is a type in this crate. The cascade reading is the canon's +//! own tier ladder; the ontology-scope reading is a *candidate* domain +//! adapter's projection (operator hypothesis, 2026-08-21) and belongs to that +//! adapter — a consumer stamps it via its ClassView. Baking either into +//! [`FocusAxis`] would make one domain's schema the substrate's, which is the +//! collision `E-ATTENTION-MASK-IS-A-RENAME-REGISTER-FILE-NOT-A-RESIDUE-CARRIER-1` +//! has this arc's fourth instance of. +//! +//! Each axis carries the always-8:8 pair — **level 0 = `hi` = coarse**, +//! **level 1 = `lo` = fine** (the shipped +//! [`FacetCascade::tier_bytes`](crate::facet::FacetCascade::tier_bytes) order, +//! `[t0.hi, t0.lo, t1.hi, …]`). The `u8`s bound **axis resolution** — 256 +//! centroids per level — and say nothing about how many graph rows a focus +//! covers. +//! +//! # Composition is PREFIX CONTAINMENT, never a blind OR +//! +//! `FieldMask` / `WideFieldMask` / `StepMask` are bitsets over **field +//! positions**, and their `union` is a bit-`OR`. **This type does not do +//! that**, and must not be made to: OR-ing two addresses yields a third +//! address neither side ever visited. The composition here is the one the +//! canon already uses for ancestry — +//! [`NiblePath::is_ancestor_of`](crate::hhtl::NiblePath::is_ancestor_of) — a +//! coarse→fine **prefix** test: +//! +//! ```text +//! a covers b ⟺ a.classid == b.classid +//! ∧ a.depth <= b.depth +//! ∧ the first a.depth cascade units agree +//! ``` +//! +//! A focus at depth 0 covers its whole class; at depth 12 it is one exact +//! address. **That is also how a focus over more than 256 rows is +//! representable**: one shallow facet is a wildcard over an unbounded subtree, +//! not 10⁶ bits. The `u8` per level caps the *branching a single level can +//! name*, never the population a facet covers. +//! +//! # Depth is explicit, never inferred from zero bytes +//! +//! [`AttentionFocusFacet`] carries `depth` **outside** the 12 bytes, exactly as +//! [`NiblePath`](crate::hhtl::NiblePath) carries its own. Inferring "the +//! wildcard starts where the bytes go zero" would collide with the +//! zero-fallback ladder, where `0` is a legitimate *dormant tier* rather than a +//! terminator — two meanings for one byte value, the ambiguity `NiblePath` +//! already refused. The wire shape stays exactly `6 × 2 × u8`; `depth` is a +//! **composition parameter**, not a stored byte. +//! +//! # What this module does NOT touch +//! +//! `cognitive-shader-driver`'s `attention_mask.rs` / `attention_mask_actor.rs` +//! are **not** used, and structurally cannot be: this is +//! `lance-graph-contract`, the zero-dep crate `cognitive-shader-driver` itself +//! depends on — the dependency edge runs only the other way, so the exclusion +//! is a compile-time property, not a convention. Per `D-ACR-0` +//! (`.claude/ATTENTION_MASK_AUDIT_2026_08_21.md`) those types are a finished +//! **rename register file** for a different contract (wide identity → scarce +//! narrow slot, LRU because slots are scarce), keyed by `MailboxId`, with no +//! address, no mask algebra and no trajectory. Do not build on them. + +use crate::facet::{CascadeShape, FacetCascade, FacetTier, CASCADE_UNITS}; + +/// The number of axes an [`AttentionFocusFacet`] carries — `CASCADE_UNITS / 2` +/// under [`CascadeShape::G6D2`]. Derived, never a second literal. +pub const FOCUS_AXES: usize = CASCADE_UNITS / 2; + +/// One of the six axes — a **position, not a meaning**. +/// +/// `FacetCascade`'s contract is that the substrate is content-blind and only the +/// consumer projects meaning; this enum keeps that. It names slots `Axis0..Axis5` +/// on purpose. A cascade reading calls axis 0 "HEEL"; an ontology-scope adapter +/// may call it "disease/phenotype". Both are ClassView-resolved projections of +/// the same byte pair — see the module docs' two-reading table. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(u8)] +pub enum FocusAxis { + /// Axis 0 — cascade units 0 (coarse) and 1 (fine). + Axis0 = 0, + /// Axis 1 — cascade units 2 and 3. + Axis1 = 1, + /// Axis 2 — cascade units 4 and 5. + Axis2 = 2, + /// Axis 3 — cascade units 6 and 7. + Axis3 = 3, + /// Axis 4 — cascade units 8 and 9. + Axis4 = 4, + /// Axis 5 — cascade units 10 and 11. + Axis5 = 5, +} + +impl FocusAxis { + /// All six axes, ascending. + pub const ALL: [FocusAxis; FOCUS_AXES] = [ + FocusAxis::Axis0, + FocusAxis::Axis1, + FocusAxis::Axis2, + FocusAxis::Axis3, + FocusAxis::Axis4, + FocusAxis::Axis5, + ]; + + /// The `CascadeShape::G6D2` group index this axis addresses. + #[inline] + #[must_use] + pub const fn group(self) -> u8 { + self as u8 + } + + /// Axis for a group index, or `None` past the sixth — a loud refusal rather + /// than a silent clamp (the `WideFieldMask` cap discipline). + #[inline] + #[must_use] + pub const fn from_group(g: u8) -> Option { + match g { + 0 => Some(FocusAxis::Axis0), + 1 => Some(FocusAxis::Axis1), + 2 => Some(FocusAxis::Axis2), + 3 => Some(FocusAxis::Axis3), + 4 => Some(FocusAxis::Axis4), + 5 => Some(FocusAxis::Axis5), + _ => None, + } + } +} + +/// **Where focus landed or was projected** — an [`FacetCascade`] read as +/// attention, plus the explicit prefix `depth` that makes it a wildcard. +/// +/// The bytes are the shipped atom, untouched. `depth ∈ 0..=CASCADE_UNITS` +/// counts significant cascade units coarse→fine; it lives OUTSIDE the 12 bytes +/// (see the module docs' depth section). +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct AttentionFocusFacet { + /// The content-blind carrier — `facet_classid(4) | 6×(8:8)`. + pub facet: FacetCascade, + /// Significant cascade units, coarse→fine, `0..=CASCADE_UNITS`. + /// `0` = the whole class; `CASCADE_UNITS` = one exact address. + depth: u8, +} + +impl AttentionFocusFacet { + /// The carving this reading uses. `G6D2` — six axes, coarse/fine, and + /// `group_of` is the shift `i >> 1`. + pub const SHAPE: CascadeShape = CascadeShape::G6D2; + + /// An exact focus: all 12 units significant. + #[inline] + #[must_use] + pub const fn exact(facet: FacetCascade) -> Self { + Self { + facet, + depth: CASCADE_UNITS as u8, + } + } + + /// A **wildcard** focus: only the first `depth` cascade units are + /// significant, the rest are "not consulted". Returns `None` if + /// `depth > CASCADE_UNITS` — a loud refusal, never a silent clamp. + #[inline] + #[must_use] + pub const fn prefix(facet: FacetCascade, depth: u8) -> Option { + if depth as usize > CASCADE_UNITS { + None + } else { + Some(Self { facet, depth }) + } + } + + /// The whole class — depth 0, the broadest focus expressible. + #[inline] + #[must_use] + pub const fn whole_class(facet_classid: u32) -> Self { + Self { + facet: FacetCascade { + facet_classid, + tiers: [FacetTier { lo: 0, hi: 0 }; FOCUS_AXES], + }, + depth: 0, + } + } + + /// Significant cascade units, coarse→fine. + #[inline] + #[must_use] + pub const fn depth(self) -> u8 { + self.depth + } + + /// The class this focus is scoped to. Focus never crosses classes — + /// [`covers`](Self::covers) requires equality. + #[inline] + #[must_use] + pub const fn classid(self) -> u32 { + self.facet.facet_classid + } + + /// The 12 payload bytes, coarse→fine per axis (`[a0.hi, a0.lo, a1.hi, …]`) + /// — the shipped [`FacetCascade::tier_bytes`] order, unchanged. + #[inline] + #[must_use] + pub const fn payload_bytes(self) -> [u8; CASCADE_UNITS] { + self.facet.tier_bytes() + } + + /// The coarse byte of `axis` (`hi`, level 0), or `None` if the axis lies + /// past this focus's [`depth`](Self::depth) — an unconsulted axis has no + /// value, and reporting `0` would conflate "not consulted" with "centroid + /// zero". + #[inline] + #[must_use] + pub fn coarse(self, axis: FocusAxis) -> Option { + let unit = Self::SHAPE.index(axis.group(), 0); + (unit < self.depth as usize).then(|| self.payload_bytes()[unit]) + } + + /// The fine byte of `axis` (`lo`, level 1), or `None` if past + /// [`depth`](Self::depth). + #[inline] + #[must_use] + pub fn fine(self, axis: FocusAxis) -> Option { + let unit = Self::SHAPE.index(axis.group(), 1); + (unit < self.depth as usize).then(|| self.payload_bytes()[unit]) + } + + /// Both bytes of `axis` as the shipped 8:8 tile, or `None` unless BOTH + /// levels are within [`depth`](Self::depth). + #[inline] + #[must_use] + pub fn axis(self, axis: FocusAxis) -> Option { + match (self.coarse(axis), self.fine(axis)) { + (Some(hi), Some(lo)) => Some(FacetTier { lo, hi }), + _ => None, + } + } + + /// **The composition primitive: prefix containment.** `self` covers `other` + /// iff they share a class, `self` is no deeper, and their first + /// `self.depth` cascade units agree coarse→fine. + /// + /// This is [`NiblePath::is_ancestor_of`](crate::hhtl::NiblePath::is_ancestor_of)'s + /// rule on the 12-unit ladder — reflexive (a focus covers itself), unlike + /// `NiblePath`'s empty-path case. It is **not** a bit test and has no + /// `OR`-like counterpart; see [`RowFocusMask`]. + #[must_use] + pub fn covers(self, other: Self) -> bool { + if self.facet.facet_classid != other.facet.facet_classid || self.depth > other.depth { + return false; + } + let (a, b) = (self.payload_bytes(), other.payload_bytes()); + a[..self.depth as usize] == b[..self.depth as usize] + } + + /// The **meet**: the deepest focus that covers both — their shared + /// coarse→fine prefix. `None` across classes (no common ancestor exists; + /// a focus never crosses a class). + /// + /// This is what replaces a union: two focuses generalise to the subtree + /// containing both, never to an invented address. It is lossy on purpose — + /// [`RowFocusMask::union`] keeps both entries instead when exactness + /// matters. + #[must_use] + pub fn common_prefix(self, other: Self) -> Option { + if self.facet.facet_classid != other.facet.facet_classid { + return None; + } + let (a, b) = (self.payload_bytes(), other.payload_bytes()); + let limit = self.depth.min(other.depth) as usize; + let mut shared = 0usize; + while shared < limit && a[shared] == b[shared] { + shared += 1; + } + let mut facet = self.facet; + // Units past the shared prefix are not consulted; zero them so the + // carrier cannot be misread as an exact address by a reader that + // ignores `depth`. + let mut unit = shared; + while unit < CASCADE_UNITS { + let (g, l) = (Self::SHAPE.group_of(unit), Self::SHAPE.level_of(unit)); + let tier = &mut facet.tiers[g as usize]; + if l == 0 { + tier.hi = 0; + } else { + tier.lo = 0; + } + unit += 1; + } + Some(Self { + facet, + depth: shared as u8, + }) + } +} + +/// A **sparse set of focuses** — the container half of `D-ACR-1`. +/// +/// # Why this is not a bitmask +/// +/// A row population is unbounded; `FieldMask` tops out at 64 positions and +/// `WideFieldMask` at 256, both by construction. Neither cardinality is +/// inherited here, because this container does not index rows at all — it +/// holds **wildcards over address subtrees**. One [`AttentionFocusFacet`] at a +/// shallow depth covers an arbitrarily large population; membership is +/// [`AttentionFocusFacet::covers`], not a bit lookup. +/// +/// # The set operations, named +/// +/// | op | meaning | NOT | +/// |---|---|---| +/// | [`union`](Self::union) | both focuses retained; entries covered by a shallower entry are absorbed | never a bit-`OR` of addresses | +/// | [`intersect`](Self::intersect) | the deeper of each covering pair | never `AND` of bytes | +/// | [`difference`](Self::difference) | entries not covered by the other side | never `ANDNOT` of bytes | +/// +/// `union` is **absorbing, not accumulating**: inserting a focus already +/// covered by a member is a no-op, and inserting one that covers members +/// replaces them. That keeps the set minimal (an antichain) without ever +/// inventing an address. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct RowFocusMask { + entries: Vec, +} + +impl RowFocusMask { + /// The empty focus — covers nothing. Distinct from a depth-0 focus, which + /// covers a whole class (the can-stay-silent / can-fire pair). + #[must_use] + pub const fn empty() -> Self { + Self { + entries: Vec::new(), + } + } + + /// Is this focus empty? An empty mask covers nothing at all. + #[must_use] + pub fn is_empty(&self) -> bool { + self.entries.is_empty() + } + + /// Number of retained entries — the antichain size, NOT a row count. A + /// single entry may cover an unbounded population. + #[must_use] + pub fn len(&self) -> usize { + self.entries.len() + } + + /// The retained entries, in insertion order after absorption. + #[must_use] + pub fn entries(&self) -> &[AttentionFocusFacet] { + &self.entries + } + + /// Insert a focus, keeping the set a minimal antichain: a no-op if already + /// covered; otherwise it replaces every entry it covers. Returns `true` iff + /// the set changed. + pub fn insert(&mut self, f: AttentionFocusFacet) -> bool { + if self.entries.iter().any(|e| e.covers(f)) { + return false; + } + self.entries.retain(|e| !f.covers(*e)); + self.entries.push(f); + true + } + + /// Does any entry cover `f`? — membership, by containment. + #[must_use] + pub fn contains(&self, f: AttentionFocusFacet) -> bool { + self.entries.iter().any(|e| e.covers(f)) + } + + /// Every focus of either side, absorbed to a minimal antichain. + #[must_use] + pub fn union(&self, other: &Self) -> Self { + let mut out = self.clone(); + for e in &other.entries { + out.insert(*e); + } + out + } + + /// The overlap: for each covering pair, the **deeper** focus — the region + /// both sides actually agree on. Disjoint pairs contribute nothing. + #[must_use] + pub fn intersect(&self, other: &Self) -> Self { + let mut out = Self::empty(); + for a in &self.entries { + for b in &other.entries { + if a.covers(*b) { + out.insert(*b); + } else if b.covers(*a) { + out.insert(*a); + } + } + } + out + } + + /// Entries of `self` not covered by any entry of `other`. + /// + /// **Deliberately conservative:** an entry only PARTIALLY overlapped is + /// kept whole, because subtracting a subtree from a prefix would require + /// enumerating siblings — inventing addresses the focus never visited. + /// Splitting is deferred (see the module's deferred note). + #[must_use] + pub fn difference(&self, other: &Self) -> Self { + Self { + entries: self + .entries + .iter() + .filter(|a| !other.entries.iter().any(|b| b.covers(**a))) + .copied() + .collect(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const CLASS: u32 = 0x0301_0000; + + fn facet_of(bytes: [u8; CASCADE_UNITS], classid: u32) -> FacetCascade { + // `tier_bytes()` is [t0.hi, t0.lo, t1.hi, t1.lo, …] — coarse first. + let mut tiers = [FacetTier { lo: 0, hi: 0 }; FOCUS_AXES]; + for (g, tier) in tiers.iter_mut().enumerate() { + tier.hi = bytes[g * 2]; + tier.lo = bytes[g * 2 + 1]; + } + FacetCascade { + facet_classid: classid, + tiers, + } + } + + fn exact(bytes: [u8; CASCADE_UNITS]) -> AttentionFocusFacet { + AttentionFocusFacet::exact(facet_of(bytes, CLASS)) + } + + // ── the atom: exact 12-byte round-trip ──────────────────────────────── + + #[test] + fn twelve_byte_payload_round_trips_exactly() { + let bytes: [u8; CASCADE_UNITS] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + let f = exact(bytes); + assert_eq!( + f.payload_bytes(), + bytes, + "the 12 payload bytes are the atom" + ); + // And the carrier is the shipped 16-byte facet, unchanged. + assert_eq!( + FacetCascade::from_bytes(&f.facet.to_bytes()), + f.facet, + "the FacetCascade carrier round-trips through its own 16 bytes" + ); + assert_eq!(core::mem::size_of::(), 16); + } + + #[test] + fn per_axis_coarse_and_fine_round_trip() { + let bytes: [u8; CASCADE_UNITS] = [10, 11, 20, 21, 30, 31, 40, 41, 50, 51, 60, 61]; + let f = exact(bytes); + for (g, axis) in FocusAxis::ALL.into_iter().enumerate() { + assert_eq!(f.coarse(axis), Some(bytes[g * 2]), "coarse = hi, level 0"); + assert_eq!(f.fine(axis), Some(bytes[g * 2 + 1]), "fine = lo, level 1"); + let tile = f.axis(axis).expect("exact focus has every axis"); + assert_eq!((tile.hi, tile.lo), (bytes[g * 2], bytes[g * 2 + 1])); + } + } + + #[test] + fn an_axis_past_the_prefix_depth_reads_none_not_zero() { + // depth 4 ⇒ axes 0 and 1 significant, axes 2..6 not consulted. + let f = AttentionFocusFacet::prefix(facet_of([7; CASCADE_UNITS], CLASS), 4).unwrap(); + assert_eq!(f.coarse(FocusAxis::Axis1), Some(7)); + assert_eq!( + f.coarse(FocusAxis::Axis2), + None, + "unconsulted axis must not report 0 — that is centroid zero's value" + ); + assert_eq!(f.axis(FocusAxis::Axis2), None); + } + + #[test] + fn prefix_refuses_out_of_range_depth_loudly() { + let c = facet_of([0; CASCADE_UNITS], CLASS); + assert!(AttentionFocusFacet::prefix(c, CASCADE_UNITS as u8).is_some()); + assert!( + AttentionFocusFacet::prefix(c, CASCADE_UNITS as u8 + 1).is_none(), + "past the ladder is a refusal, never a silent clamp" + ); + } + + // ── composition: containment, and demonstrably NOT a blind OR ───────── + + #[test] + fn covers_is_prefix_containment_and_is_reflexive() { + let deep = exact([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + let shallow = + AttentionFocusFacet::prefix(facet_of([1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0], CLASS), 4) + .unwrap(); + assert!(shallow.covers(deep), "a prefix covers what extends it"); + assert!(!deep.covers(shallow), "and containment is asymmetric"); + assert!(deep.covers(deep), "reflexive"); + } + + #[test] + fn a_focus_never_covers_across_classes() { + let a = AttentionFocusFacet::whole_class(CLASS); + let b = AttentionFocusFacet::exact(facet_of([1; CASCADE_UNITS], 0x0302_0000)); + assert!( + !a.covers(b), + "depth 0 covers its OWN class only — focus never crosses a class" + ); + } + + /// The load-bearing negative: bitwise-OR of two focuses would produce an + /// address neither visited, and the container must not behave that way. + #[test] + fn composition_is_not_a_blind_or() { + let a = exact([0b0000_0001, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + let b = exact([0b0000_0010, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + + // What a bit-OR WOULD produce — a third, never-visited address. + let or_byte = 0b0000_0001u8 | 0b0000_0010u8; + let phantom = exact([or_byte, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + assert_ne!(or_byte, 0b0000_0001); + assert_ne!(or_byte, 0b0000_0010); + + let u = RowFocusMask::empty().union(&{ + let mut m = RowFocusMask::empty(); + m.insert(a); + m.insert(b); + m + }); + + assert!( + u.contains(a) && u.contains(b), + "both originals are retained" + ); + assert!( + !u.contains(phantom), + "the OR-address was never visited and must not become a member" + ); + assert_eq!(u.len(), 2, "union keeps both, it does not fuse them"); + } + + #[test] + fn common_prefix_generalises_and_never_invents_an_address() { + let a = exact([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + let b = exact([1, 2, 3, 99, 0, 0, 0, 0, 0, 0, 0, 0]); + let m = a.common_prefix(b).expect("same class"); + assert_eq!(m.depth(), 3, "they agree on exactly three cascade units"); + assert!(m.covers(a) && m.covers(b), "the meet covers both"); + assert_eq!( + &m.payload_bytes()[3..], + &[0u8; CASCADE_UNITS - 3], + "units past the shared prefix are zeroed, not left as stale bytes" + ); + assert!( + exact([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) + .common_prefix(AttentionFocusFacet::exact(facet_of( + [1; CASCADE_UNITS], + 0x0999_0000 + ))) + .is_none(), + "no meet across classes" + ); + } + + // ── the container: cardinality is NOT inherited ─────────────────────── + + #[test] + fn container_holds_far_more_than_the_fieldmask_and_widefieldmask_caps() { + // FieldMask::MAX_FIELDS == 64 and WideFieldMask's u8 universe caps at + // 256. Neither bound applies here: this is not a position bitset. + let mut m = RowFocusMask::empty(); + for i in 0..1000u32 { + let b = i.to_le_bytes(); + // Distinct in the FINE units so no entry absorbs another. + m.insert(exact([0, 0, 0, 0, b[0], b[1], b[2], b[3], 0, 0, 0, 0])); + } + assert_eq!(m.len(), 1000, "1000 > 256 > 64 — no cap inherited"); + } + + #[test] + fn one_shallow_focus_covers_an_unbounded_population() { + // The >256-rows requirement: coverage comes from the PREFIX, not from + // one bit per row. A depth-2 focus covers every address sharing axis 0. + let wildcard = + AttentionFocusFacet::prefix(facet_of([9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], CLASS), 2) + .unwrap(); + let mut covered = 0usize; + for i in 0..=u16::MAX { + let b = i.to_le_bytes(); + let addr = exact([9, 9, b[0], b[1], 0, 0, 0, 0, 0, 0, 0, 0]); + if wildcard.covers(addr) { + covered += 1; + } + } + assert_eq!( + covered, 65_536, + "one 12-byte atom covers 2^16 distinct addresses — and that is only \ + the two units varied here, not the ceiling" + ); + } + + #[test] + fn insert_absorbs_into_a_minimal_antichain() { + let mut m = RowFocusMask::empty(); + let deep = exact([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + assert!(m.insert(deep)); + // A shallower focus covering it replaces it. + let shallow = + AttentionFocusFacet::prefix(facet_of([1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], CLASS), 2) + .unwrap(); + assert!(m.insert(shallow)); + assert_eq!(m.len(), 1, "the covered entry was absorbed"); + assert!(m.contains(deep), "and is still a member by containment"); + assert!(!m.insert(deep), "re-inserting a covered focus is a no-op"); + } + + #[test] + fn empty_covers_nothing_but_depth_zero_covers_its_class() { + // The can-fire / can-stay-silent pair on NON-trivial input. + let some = exact([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + assert!( + !RowFocusMask::empty().contains(some), + "an empty focus must stay silent" + ); + let mut all = RowFocusMask::empty(); + all.insert(AttentionFocusFacet::whole_class(CLASS)); + assert!(all.contains(some), "and a depth-0 focus must fire"); + // Distinguishable from each other AND from a real, narrow focus. + let mut narrow = RowFocusMask::empty(); + narrow.insert(some); + assert!(!narrow.contains(exact([9; CASCADE_UNITS]))); + assert_ne!(narrow, all); + assert_ne!(narrow, RowFocusMask::empty()); + } + + // ── two readings, ONE unchanged 12-byte atom ───────────────────────── + + /// The operator's 2026-08-21 hypothesis: the six axes may also carry six + /// **ontology scopes** in interplay. This test is the whole argument that + /// the hypothesis costs nothing — it is a *projection*, so both readings + /// address the identical bytes and neither is a type in this crate. + /// + /// A domain adapter would resolve these labels through its ClassView; they + /// appear here as local `const`s precisely to prove they need not exist in + /// the substrate. + #[test] + fn the_same_atom_reads_as_cascade_and_as_ontology_scope_without_changing_a_byte() { + // One adapter's projection (a candidate MedCare-shaped scope reading). + const SCOPE: [&str; FOCUS_AXES] = [ + "disease/phenotype", + "anatomy", + "process/mechanism", + "substance/intervention", + "evidence/provenance", + "context/time/population", + ]; + // The canon's own reading of the very same positions. + const CASCADE: [&str; FOCUS_AXES] = ["HEEL", "HIP", "TWIG", "LEAF", "family", "identity"]; + + let bytes: [u8; CASCADE_UNITS] = [3, 30, 4, 40, 5, 50, 6, 60, 7, 70, 8, 80]; + let f = exact(bytes); + let before = f.facet.to_bytes(); + + // Read it as ontology scope: (scope centroid, local refinement). + let scoped: Vec<(&str, u8, u8)> = FocusAxis::ALL + .into_iter() + .map(|a| { + ( + SCOPE[a.group() as usize], + f.coarse(a).unwrap(), + f.fine(a).unwrap(), + ) + }) + .collect(); + assert_eq!(scoped[1], ("anatomy", 4, 40)); + assert_eq!(scoped[4], ("evidence/provenance", 7, 70)); + + // Read the SAME facet as the cascade tiers. + let cascaded: Vec<(&str, u8, u8)> = FocusAxis::ALL + .into_iter() + .map(|a| { + ( + CASCADE[a.group() as usize], + f.coarse(a).unwrap(), + f.fine(a).unwrap(), + ) + }) + .collect(); + assert_eq!(cascaded[1], ("HIP", 4, 40)); + + // The projections differ only in the LABEL; every byte is identical. + for (s, c) in scoped.iter().zip(cascaded.iter()) { + assert_ne!(s.0, c.0, "the two readings name the axis differently"); + assert_eq!((s.1, s.2), (c.1, c.2), "and read the identical bytes"); + } + assert_eq!( + f.facet.to_bytes(), + before, + "reading a facet never mutates it — a projection is free" + ); + + // Containment is label-blind too: a scope-shaped wildcard ("any + // anatomy under disease centroid 3") is an ordinary depth-2 prefix. + let any_under_disease_3 = + AttentionFocusFacet::prefix(facet_of([3, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], CLASS), 2) + .unwrap(); + assert!(any_under_disease_3.covers(f)); + } + + #[test] + fn intersect_and_difference_are_containment_shaped() { + let deep = exact([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + let shallow = + AttentionFocusFacet::prefix(facet_of([1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], CLASS), 2) + .unwrap(); + let other = exact([9; CASCADE_UNITS]); + + let (mut a, mut b) = (RowFocusMask::empty(), RowFocusMask::empty()); + a.insert(deep); + a.insert(other); + b.insert(shallow); + + let i = a.intersect(&b); + assert_eq!(i.len(), 1, "only the covering pair contributes"); + assert!(i.contains(deep), "and it yields the DEEPER side"); + assert!(!i.contains(other), "the disjoint entry contributes nothing"); + + let d = a.difference(&b); + assert!(!d.contains(deep), "covered entry removed"); + assert!(d.contains(other), "uncovered entry kept"); + } +} diff --git a/crates/lance-graph-contract/src/lib.rs b/crates/lance-graph-contract/src/lib.rs index c1c88b5ce..37870aeed 100644 --- a/crates/lance-graph-contract/src/lib.rs +++ b/crates/lance-graph-contract/src/lib.rs @@ -49,6 +49,7 @@ pub mod a2a_blackboard; pub mod action; pub mod aiwar; pub mod atoms; +pub mod attention_facet; pub mod auth; pub mod awareness_facet; pub mod callcenter; From 44fb19b92baacace397178dae707a5bf50de6559 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 14:35:14 +0000 Subject: [PATCH 02/10] Plan + handover: the known-unknown handover network (the connective plan) Two arcs were being built separately -- the alpha-channel attention overlay and the DisMech Hole. This plan is the claim that they are one loop, and that exactly one fibre of it is missing. THE FENCE, MEASURED TWICE. "Expanding CE64 59..63" cannot mean widening: layout.rs:93-111's _LAYOUT_COVERAGE const-asserts all 64 bits covered exactly once, and dismech-causality-v3-v1.md:503 states verbatim "Not in CE64 -- it has zero free bits." The operator's own notation is the answer: ^n is STACKING, the canon's "scale is the next cascade level, never field-widening" applied to the awareness axis. Three stages, none of them a bit: readable (D-ACR-7's reading contract, spanning CausalEdge64 AND CausalEdgeV3) -> discriminating (WitnessKind -> Hole) -> granular (HoleV3). THE MISSING FIBRE. Every other link is shipped or designed: the pothole opens via QueryReference::at, the stamp is with_reasoning_band (the only writer), locating is RowFocusMask (shipped today), exploring is the 14 delta_conf-capable recipes, closing is Revision + span, rewiring is plasticity at bits 50-52. Handover is neither -- D-ACR-16 is NOT DESIGNED with zero precedent. This plan's contribution is naming what handover IS so it has a shape to be designed against: a Hole is a kanban card for a missing link, an owned row moving between mailboxes -- never a message, never a shared log. Self-organization then follows from existence-not-command, not from a scheduler. BLOCKER STATED AS A PREREQUISITE, NOT A QUEUE POSITION. HoleV3 = ValueTenant 16 is blocked on BoardAggregates = 15 resolving, because the discriminant to VALUE_TENANTS index requires contiguous descriptors. GROUND TRUTH STATED HONESTLY. The public DisMech corpus marks its own gaps, but only 27 named intermediates match an actual graph node (113 have a real 2-hop path). A supervised evaluation has 27-113 usable cases, not thousands -- the gap count says how much work exists, the match count how much is checkable today. The handover doc is cold-start: it carries D-ACR-0's audit result (do not build on attention_mask -- it is a rename register file, EXISTS-UNCALLED), D-ACR-1's reuse finding (the atom already existed; what was missing was a composition that is not a bitset union), the mid-flight council state (v3 NOT ratified, reviewers 1 and 3 outstanding, and the rule that they see draft v2 only), the CausalEdgeV3 scope correction no savant could have caught, and the ogar-loco measurement -- already the same 6x2x8bit format, deliberately mirrored with a documented reason to stay separate, so do NOT "fix" it by importing. Board hygiene same-commit: INTEGRATION_PLANS prepend, STATUS_BOARD D-KUH-1..4. --- .claude/board/INTEGRATION_PLANS.md | 41 ++++ .claude/board/STATUS_BOARD.md | 4 + .../2026-08-21-2330-session-to-next.md | 177 +++++++++++++++++ .../known-unknown-handover-network-v1.md | 180 ++++++++++++++++++ 4 files changed, 402 insertions(+) create mode 100644 .claude/handovers/2026-08-21-2330-session-to-next.md create mode 100644 .claude/plans/known-unknown-handover-network-v1.md diff --git a/.claude/board/INTEGRATION_PLANS.md b/.claude/board/INTEGRATION_PLANS.md index 6e4cce31f..7dc8d0021 100644 --- a/.claude/board/INTEGRATION_PLANS.md +++ b/.claude/board/INTEGRATION_PLANS.md @@ -1,3 +1,44 @@ +## 2026-08-21 — KNOWN-UNKNOWN HANDOVER NETWORK (the connective plan, not a new arc) + +`.claude/plans/known-unknown-handover-network-v1.md` — the operator's framing +the same day: *"an awareness to hand over missing links in a 6×2×8bit growing +BNN network as a self-organizing AGI-aspiring network … that includes expanding +CE64 59..63 as a known-unknowns exploring brain plasticity."* Scraped onto +existing homes; **mints no type**. + +**Headline: the loop's links are individually shipped or individually designed, +except exactly one — handover.** `D-ACR-16` (nested kanban cascade) is NOT +DESIGNED with zero shipped precedent, and this plan's contribution is naming +what handover IS so it has a shape to be designed against: **a Hole is a kanban +card for a missing link — an owned row moving between mailboxes**, never a +message and never a shared log (`E-CE64-MB-4` one-writer; +`E-AGENT-LOG-SHARED-SINK-ANTIPATTERN-1`). Self-organization follows from +existence-not-command (`E-PROGRESSION-IS-EXISTENCE-NOT-COMMAND-1`), not from a +scheduler. + +**The fence, measured twice:** *"expanding 59..63"* **cannot** mean widening. +`causal-edge/src/layout.rs:93-111`'s `_LAYOUT_COVERAGE` const-asserts all 64 +bits covered exactly once, and `dismech-causality-v3-v1.md:503` states verbatim +*"Not in CE64 — it has zero free bits."* So `↑n` is **stacking** — the canon's +*"scale is the next cascade level, never field-widening"* applied to the +awareness axis. Three stages, none of them a bit: readable (D-ACR-7's reading +contract, spanning CE64 **and** `CausalEdgeV3`) → discriminating (`WitnessKind` +→ Hole) → granular (`HoleV3`). + +**Hard blocker recorded as such:** `HoleV3 = ValueTenant 16` is blocked on +`BoardAggregates = 15` resolving, because the discriminant→`VALUE_TENANTS` +index requires contiguous descriptors — a prerequisite, not a queue position. + +**Ground truth stated honestly:** the public DisMech corpus marks its own gaps, +but only **27** named intermediates match an actual graph node (113 have a real +2-hop path) — so a supervised evaluation has 27–113 usable cases, not +thousands. The gap count says how much work exists; the match count says how +much is checkable today. + +Deliverables D-KUH-1..4. Only **D-KUH-1** (handover design) is unblocked. +Grade discipline inherited from the parent plan: **a pruner, not a proof**; +"AGI-aspiring" names a direction, not a property any deliverable asserts. + ## 2026-08-21 — ALPHA-CHANNEL RUNG OVERLAY (scraping, not a new arc) `.claude/plans/alpha-channel-rung-overlay-v1.md` — the design for the ONE diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index 690f374c1..3a19ca720 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -50,6 +50,10 @@ the rest. | D-id | Scope | Status | Falsifier | |---|---|---|---| +| D-KUH-1 | Name the Hole's handover shape: an owned row moving between mailboxes, lifecycle `Open -> Proposed -> {Resolved \| Refuted}`. Design only — feeds D-ACR-16 | **Next** — the only unblocked row in `known-unknown-handover-network-v1.md` | a design needing a scheduler / broadcast / shared log has rebuilt what `E-PROGRESSION-IS-EXISTENCE-NOT-COMMAND-1` and `E-AGENT-LOG-SHARED-SINK-ANTIPATTERN-1` deleted | +| D-KUH-2 | Wire `WitnessKind` -> `HoleV3` | **BLOCKED** on `BoardAggregates = 15` resolving (contiguous `ValueTenant` discriminants) — a prerequisite, not a queue position | a KNOWN-UNKNOWN band read resolves to a Hole AND a low-trust KNOWN does not — both asserted | +| D-KUH-3 | Plasticity as the rewire signal — does `plasticity()` move when a Hole closes? | BLOCKED on D-KUH-2 | two-sided: moves on a real close, does NOT move on a refuted one; firing on both carries no information | +| D-KUH-4 | Growth = minting rows, never widening fields: add N Holes, assert `ENVELOPE_LAYOUT_VERSION` and every field width unchanged | Queued — cheap, any time after D-KUH-2 | any width change is the failure (the §0 fence as a test) | | D-ACR-0 | Audit `attention_mask.rs`/`attention_mask_actor.rs`: residue carrier, or a name collision? Report only | **Shipped** 2026-08-21 — `.claude/ATTENTION_MASK_AUDIT_2026_08_21.md`. Verdict **EXISTS-UNCALLED** + a name collision: the shipped type is a *rename register file* (`causaledge64-mailbox-rename-soa-v1.md` §4), not a residue carrier | recorded EXISTS-UNCALLED (0 callers workspace-wide + 3 sibling repos); piece E regraded — not a basis for D-ACR-1 | | D-ACR-1 | `RowFocusMask` — the one missing primitive (S3.1b names it; no crate contains it) | **Shipped** 2026-08-21 — `contract::attention_facet`. Basis: **reuse** `FacetCascade` + `CascadeShape::G6D2`, zero new bytes; `FocusAxis` is content-blind (`Axis0..Axis5`), composition is **prefix containment**, never bit-OR; explicit `depth` outside the 12 bytes (`NiblePath` precedent) | can-fire AND can-stay-silent on non-trivial input — **both green**, plus a not-a-blind-OR negative and a 1000-entry container (no `FieldMask` 64 / `WideFieldMask` 256 cap inherited) | | D-ACR-3 | The one-way invariant as a test: no ontology-owned write traces to a patient-tagged read through ANY call path (corrected from write-authorization-only after CodeRabbit found a session-derived value can flow to the ontology owner via a shared parameter/return, then be written as the owner's own act) | Queued — gates on D-ACR-1 | a write whose call graph includes a session-tagged read is the bug, even if the write itself is authored by the ontology owner | diff --git a/.claude/handovers/2026-08-21-2330-session-to-next.md b/.claude/handovers/2026-08-21-2330-session-to-next.md new file mode 100644 index 000000000..a09918ac1 --- /dev/null +++ b/.claude/handovers/2026-08-21-2330-session-to-next.md @@ -0,0 +1,177 @@ +# Session handover — 2026-08-21 (D-ACR-0 / D-ACR-1 / D-ACR-7 council) + +**Why this exists:** this session took over the alpha-channel arc from the +prior session's token wall (`2026-08-21-2200-session-to-next.md`), shipped two +deliverables, and left a 5+3 council mid-flight. Assume the next session has +none of this context. + +## What shipped + +| branch | deliverable | state | +|---|---|---| +| `claude/d-acr-0-attention-mask-audit` | **D-ACR-0** — the attention_mask audit | pushed, no PR yet | +| `claude/d-acr-1-row-focus-mask` | **D-ACR-1** — `contract::attention_facet` | pushed, no PR yet; 14 tests, 1194 contract tests green, clippy+fmt clean | + +Both carry board hygiene in-commit (EPIPHANIES prepend, STATUS_BOARD flip, +LATEST_STATE contract inventory for D-ACR-1). + +### D-ACR-0 — the result is load-bearing for everything after it + +`attention_mask.rs` / `attention_mask_actor.rs` are **EXISTS-UNCALLED** (three +hits workspace-wide, all non-consumers; one is a doc comment saying *"NO +AttentionMask/LRU"*) **and are a different mechanism wearing the name**: a +finished **rename register file** (`causaledge64-mailbox-rename-soa-v1.md` §4) +— wide identity → scarce narrow slot, LRU because slots are scarce, keyed by +`MailboxId`, `Vec` + linear scan, no address, no mask algebra, no trajectory. + +**Do not build on them.** Piece E regrades from *"shipped; unaudited for this +use"* to *"shipped for a DIFFERENT use; uncalled; not a basis for piece D"*. +Also measured: `plasticity_residual` is write-once-zero (2 grep hits total), +`BindReply` is a NoOp handler, and the originating §4's **singleton** actor +would rebuild what the V3 mailbox ruling removed — treat "sprint-12+ work" as +superseded, not pending. + +### D-ACR-1 — the basis was a REUSE, not a new type + +`contract::facet::FacetCascade` already **is** `6 × 2 × u8` under +`CascadeShape::G6D2`. Zero new bytes were added. What was genuinely missing was +narrower: **a composition that is not a bitset union**. Every `union`/ +`intersect` in the crate is a bit op over *field positions* (`FieldMask` u64/64, +`WideFieldMask` u8/256, `StepMask`, `rbac`) — and a bit-OR of two addresses is a +third address neither side visited. + +Shipped instead: **prefix containment**, reusing `NiblePath::is_ancestor_of`'s +rule and its **explicit depth** (inferring the wildcard from zero bytes would +collide with the zero-fallback ladder, where `0` is a dormant tier). `depth` +lives OUTSIDE the 12 bytes; the wire shape stays exactly `6 × 2 × u8`. + +**`FocusAxis` is `Axis0..Axis5` — a position, not a meaning.** A first draft +named them `Heel/Hip/Twig/…` and the operator caught it while raising a second +candidate reading (six ontology scopes). Both readings now live only in a test, +over byte-identical input. **This would have been the fifth homonym collision +of the arc and the first we minted ourselves.** + +## The council, mid-flight — READ THIS BEFORE TOUCHING D-ACR-7 + +`/5plus3` was convened for **D-ACR-7** (the 59..63 reading contract). State: + +- **Phase 0** SPEC v1 — written, 6 sections, inventory measured +- **Phase 1** the 5 savants — **all returned**, 39 findings +- **Phase 2** draft v2 — **consolidated** +- **Phase 3** the 3 reviewers — **reviewer 2 returned (8 PASS / 1 FIX(P2) on + L6); reviewers 1 and 3 were still running when this handover was written** +- **Phase 4/5** — NOT DONE. **v3 is not ratified.** + +Artifacts (scratchpad, ephemeral — re-derive from this handover if gone): +`dacr7-spec-v1.md`, `dacr7-spec-v2.md`. + +**The sequencing is the point.** Do not ratify v3 without reviewers 1 and 3. +Do not let them see the raw savant output — draft v2 only. If a `BLOCK(P0)` +lands, return to Phase 0 rather than arguing it away in a commit message. + +### What the council found that matters most + +1. **Two savants converged independently on the same defect.** The spec's + `Result`-returning resolver is the **opposite** convention from every + shipped `ClassView` lens selector (`rail_carving`, `edge_codec_flavor`, + `value_schema` — all infallible with zero-fallback defaults), AND a hot-path + batch scan needs a total function. **Resolved by splitting:** the + *declaration lookup* is total and sibling-consistent; the *projection of raw + bits* is fallible, because that is where mismatch and stale-v1 bits actually + live. They were two operations, conflated in v1. +2. **Code truth returned 6/6 CONFIRMS.** The v1 inventory is measured, not + assumed — which was the one thing this council most needed to verify. +3. **The scope was wrong, and no savant could have caught it** (my spec did not + mention V3, so no question set asked). See below. + +## The operator input that changed the spec's scope (L0) + +*"causaledge64 is the muscle memory / causaledgev3 for granularity."* Verified: + +- `edge_v3.rs:96-103` — `CausalEdgeV3 { payload: [u8; 12] }`, const-asserted; + *"`classid(4) | payload(12)` = the canonical 16-byte facet, the payload half"* +- `edge_v3.rs:49-50` — the SAME two fields: `[8] w_slot(6) | truth/topology + RAW(2)`, `[9] spare/ReasoningBand RAW(3) | reserved(5)` +- `edge_v3.rs:199,206` — `truth_raw()` / `spare_raw()` +- `edge_v3.rs:16-26` — V3 **rehydrates into CE64 to reason** (`syllogize` reads + only SPO + freq/conf + causal_mask). The muscle-memory framing is literal. + +**And the V3 module doc already states D-ACR-7's problem, unsolved** +(`edge_v3.rs:86-90`): *"Which lens the ordinal was written through is **the +producer's knowledge, not the conversion's**."* The reading contract is exactly +what supplies that. So v2 spans **both carriers, one contract** — with an +honest asymmetry: the v1-provenance trap (a v1 edge with `temporal >= 512` +reads a non-zero band, `layout.rs:74-76`) does **not** apply to V3, whose bytes +were never temporal. + +## Deferred with its tension stated — do not silently adopt + +The operator also raised *"causaledgev3 can even use 6×2×8bit ↑n as BNN +planning equivalent"*. **Not taken into v2**, because the shipped doc forbids +that reading today (`edge_v3.rs:29-36`): *"a packed EDGE REGISTER, **NOT** a +slot-pure §3 facet … Do not read this as a content-blind facet."* Adopting it +would also be a **sixth** homonym against `attention_facet`'s `6×2×8bit` +(landed today). It needs its own deliverable and its own resolution of the +typed-register-vs-content-blind-facet contradiction. + +## `ogar-loco` resonance — measured, and the answer is DON'T wire + +Asked whether the same `6×2×8bit` should be wired into `ogar-loco`. It is +**already the same format, independently**: `ogar-loco/src/lib.rs:267-276` — +`Pairs = 6 × (u8:u8)`, `Triples = 4 × (u8:u8:u8)`, `Quads = 3 × (u8:u8:u8:u8)`, +`PAYLOAD_BYTES_PER_SLOT = SLOT_STRIDE(16) − CLASSID_BYTES(4) = 12`, selected +**per classid**, with `const _` asserts pinning `calls_per_lane × N == 12`. + +And `lib.rs:256-258` states the decision already: *"**Mirrors** the LE +contract's `CascadeShape` … **defined locally so this crate keeps its +plug-and-play posture and takes no substrate dependency**."* `ogar-loco`'s only +dep is optional `serde`. + +**So: deliberate mirroring, not overlooked duplication** — the opposite of the +five homonym collisions (those shared a *name* with different meanings; this +shares an *algebra* with identical meaning and a documented reason to stay +separate). Do not "fix" it by importing. + +The real finding: the 12-unit algebra has now appeared at **six independent +sites** (`FacetCascade`, `TekamoloFacet` G4D3, `AttentionFocusFacet` G6D2, +`CausalEdgeV3`'s 12-byte payload, le-contract §3, `ogar-loco::LaneShape`), two +of them carrying explicit "mirrors" notes. That is evidence `12 = 6·2 = 4·3 = +3·4` is **forced**, not chosen. + +## New plan written this session + +`.claude/plans/known-unknown-handover-network-v1.md` — the operator's +*"awareness to hand over missing links in a 6×2×8bit growing BNN network"* +framing, scraped onto existing homes. Headline results: + +- **`↑n` is stacking, never widening.** Two independent measurements forbid + widening: `_LAYOUT_COVERAGE` const-asserts all 64 bits covered exactly once, + and D-CV3-3 says verbatim *"Not in CE64 — it has zero free bits."* +- The loop's links are **individually shipped or individually designed, except + one**: **handover** (`D-ACR-16`, NOT DESIGNED, zero precedent). The plan's + contribution is naming what handover IS — a Hole as a kanban card moving as + an **owned row** between mailboxes, never a message, never a shared log. +- `HoleV3 = ValueTenant 16` is **hard-blocked** on `BoardAggregates = 15` being + resolved (contiguous discriminants), not merely queued. +- Ground truth is **27–113 usable cases**, not thousands — the gap count says + how much work exists, the match count says how much is checkable today. + +## Next steps, in order + +1. **Finish the council**: collect reviewers 1 and 3, apply Phase 4 fixes + (reviewer 2's L6 FIX(P2) — the "accepted-as-stated" bullets need the same + file:line rigor L0/L5 use; evidence for the weakest of them is already + gathered: no glob imports of `lance_graph_contract::*` exist, so a new + module is additive-only), ratify v3, implement, run gates G1–G10. +2. **Open PRs** for D-ACR-0 and D-ACR-1 (both pushed, neither has a PR). +3. **D-KUH-1** (handover design) is unblocked and is the only thing in the new + plan that can start today. + +## Standing discipline this session reinforced + +Content-blindness had to be defended twice in one deliverable, and the operator +caught it both times. **Once a second plausible reading of a byte exists, any +name in the substrate is a premature commitment to the first.** The five +homonym collisions of this arc (witness / nibble / hydration / attention-mask / +TrustTexture ×4) were the expensive form; naming the focus axes would have been +the first self-inflicted one. diff --git a/.claude/plans/known-unknown-handover-network-v1.md b/.claude/plans/known-unknown-handover-network-v1.md new file mode 100644 index 000000000..ac2831fc5 --- /dev/null +++ b/.claude/plans/known-unknown-handover-network-v1.md @@ -0,0 +1,180 @@ +# Known-unknown handover network — v1 + +> **Status:** PROPOSED. No code. Every "exists" claim below was verified by +> reading the file named, this session; every "absent" by a grep that returned +> nothing. Register-before-code, per the dialectic build order. +> +> **What this plan is:** the operator's 2026-08-21 framing — *"an awareness to +> hand over missing links in a 6×2×8bit growing BNN network as a +> self-organizing AGI-aspiring network … that includes expanding CE64 59..63 as +> a known-unknowns exploring brain plasticity"* — scraped onto homes that +> already exist, plus the short list of what genuinely does not. +> +> It mints no type. It is the **connective plan** between two arcs that were +> being built separately: `alpha-channel-rung-overlay-v1.md` (the attention +> overlay) and `dismech-causality-v3-v1.md` (the Hole). The claim of this plan +> is that they are one loop, and that exactly one fibre of it is missing. + +## §0 — The fence, first, because it is the load-bearing constraint + +**"Expanding 59..63" cannot mean widening.** Two independent measurements +forbid it: + +1. `causal-edge/src/layout.rs:93-111` — `_LAYOUT_COVERAGE` const-asserts that + all 64 bits are covered **exactly once**. There is no slack to take. +2. `.claude/plans/dismech-causality-v3-v1.md:503` (D-CV3-3), verbatim: + *"`awareness_state` ⟂ `unknown_kind`. **Not in CE64 — it has zero free + bits.**"* + +So the operator's own notation is the answer: **`↑n` is stacking, not +widening** — the canon's *"scale is the next cascade level, never +field-widening"* applied to the awareness axis. Every deliverable below adds a +**level**, never a bit. Any future session reading "expand 59..63" as "take +some spare bits" is reading it wrong, and this section is the record. + +## §1 — The three-stage expansion, and where each already lives + +| stage | what it buys | carrier | state (verified) | +|---|---|---|---| +| **1. readable** | the 5 bits stop being ambiguous — which lens wrote them, and is the provenance trustworthy | D-ACR-7 reading contract, spanning `CausalEdge64::{truth, reasoning_band}` **and** `CausalEdgeV3::{truth_raw, spare_raw}` | **in council now** (spec v2 consolidated) | +| **2. discriminating** | a known-unknown is *distinguishable* from a weak known | F5: the band **grades**, the **witness reference discriminates** — `WitnessKind` points at a Hole | `WitnessKind` is in D-ACR-7 spec v2; its Hole target is stage 3 | +| **3. granular** | the unknown gets its own row, with lifecycle | `HoleV3` as `ValueTenant = 16`; `CausalEdgeV3`'s 12-byte register (bytes `[10..12]` still dormant) | **BLOCKED** — see §4 | + +**Why stage 1 is not optional plumbing.** `causal-edge/src/edge_v3.rs:86-90` +already states the gap in shipped code and leaves it open: + +> *"`w_slot` / truth / spare are preserved as **RAW ORDINALS** … **Which lens +> the ordinal was written through is the producer's knowledge, not the +> conversion's**."* + +A network that cannot tell which lens wrote a bit cannot tell a known-unknown +from a low-trust known. Stage 1 supplies exactly the producer knowledge the +conversion structurally cannot carry. + +## §2 — The loop, with every link's real status + +```text + pothole OPENS QueryReference::at(v, rung) bounds what is derivable + │ → "not yet knowing" is manufactured honestly + ▼ + STAMP with_reasoning_band() — explicit, never derived (F2) + │ + ▼ + DISCRIMINATE WitnessKind → the Hole (awareness × unknown_kind) + │ + ▼ + LOCATE RowFocusMask — where attention actually went + │ + ▼ + HAND OVER the Hole as an owned row in another mailbox + │ (one-writer-per-mailbox preserved) + ▼ + EXPLORE the 14 delta_conf-capable recipes + │ (the 20 mute ones are eigenvalue 1 by construction) + ▼ + CLOSE Revision fires; the pothole-open SPAN is the measurement + │ + ▼ + REWIRE plasticity (CE64 bits 50-52 / V3 byte [3]) + growth = minting new rows, never widening a field +``` + +| link | home | state | +|---|---|---| +| pothole opens | `temporal.rs` `QueryReference::at` | **SHIPPED**, unbuilt as a live horizon (D-ACR-15) | +| stamp | `edge.rs:1056` `with_reasoning_band` | **SHIPPED**; the ONLY writer (F2) | +| discriminate | `WitnessKind` (D-ACR-7 v2) | in council | +| locate | `contract::attention_facet::RowFocusMask` | **SHIPPED 2026-08-21** (D-ACR-1) | +| **hand over** | — | **THE MISSING FIBRE — see §3** | +| explore | `recipe_kernels` `delta_conf` (14/34) | **SHIPPED**; filter is D-ACR-7's acceptance condition | +| close | `RecipeInference::Revision` + span | design in `alpha-channel` §3p; probe is D-ACR-10 | +| rewire | `PLAST_SHIFT = 50` (`layout.rs:37`) | **SHIPPED as a field.** Measured: no external consumer reads `plasticity()` for rewiring — `high_heel.rs:236,589,845` reads it for basin state, which is a different use | + +## §3 — The one missing fibre: handover + +Everything else above exists or is designed. **Handover does not**, and the +plan that would own it says so plainly — `alpha-channel-rung-overlay-v1.md` +§4, `D-ACR-16`: + +> *"Nested kanban cascade for awareness build-up — **NOT DESIGNED** — zero +> shipped precedent."* + +and §3m's grep receipt: *"Grepped `lance-graph-supervisor/src/*.rs` for +`nested.*kanban|kanban.*cascade` — zero. No near-miss found this time, +foveated or otherwise."* + +**This plan's contribution is to name what handover IS, so D-ACR-16 has a +shape to be designed against:** a Hole is a **kanban card for a missing link**. +It is not a message and not a shared log — it is an owned row that moves +between mailboxes, which is the only handover shape this substrate permits +(`E-CE64-MB-4` one-writer-per-mailbox; `E-AGENT-LOG-SHARED-SINK-ANTIPATTERN-1` +for why a shared append-log is pseudo-handover with a race). + +The self-organizing property follows from that and nothing more exotic: a +mailbox that cannot close its own Hole hands it to one that might. No +scheduler, no central planner — `E-PROGRESSION-IS-EXISTENCE-NOT-COMMAND-1` +already deleted the per-owner `advance()` RPC shape, so handover must be +existence (the row is now yours), never command. + +## §4 — Blockers, named with their real cause + +| # | blocked | real blocker | not the blocker | +|---|---|---|---| +| B1 | `HoleV3 = ValueTenant 16` | `BoardAggregates = 15` is a **gated reservation only**; `ValueTenant`'s discriminant→`VALUE_TENANTS` index requires **contiguous** descriptors, so 16 has no valid slot until 15 resolves (`dismech-causality-v3-v1.md:503,509-510`) | not the benchmark, and not D-CV3-0..2 — CodeRabbit corrected exactly this on 2026-08-21 | +| B2 | `D-ACR-16` handover | no design, no precedent (§3) | not a mint decision — nothing to mint yet | +| B3 | 64k parallel exploration | `D-ACR-5` gates on dialectic V4: V0–V3 green at small scale first | not this plan's to unblock | +| B4 | live horizon (pothole opening) | `D-ACR-15` — `WorkflowDAG::plan()` is a registered stub whose body is comments | not absent; specified but unbuilt | + +**B1 is the sharp one.** The Hole is stage 3's whole content, and it is blocked +on an unrelated mint's width being decided. That is worth stating loudly +because it looks like a queue position and is actually a hard prerequisite. + +## §5 — First real corpus, and the honest size of its ground truth + +The public DisMech transcode (`AdaWorldAPI/dismech-rs`) is the first corpus +where this loop has something to run on: its `causal_link_type` field marks +**INDIRECT_UNKNOWN_INTERMEDIATES** and **UNKNOWN** edges explicitly — a +knowledge base that publishes its own gaps, which is rare and is exactly what +a handover network needs as input. + +**But the usable ground truth is far smaller than the gap count, and this plan +must not overstate it.** Measured on the upstream corpus by the parallel +session (`dismech-rs/bakes/stage3-current-truth-2026-08-20/mediator-feasibility.tsv`): + +| | | +|---|---| +| edges labelled `INDIRECT_KNOWN` (pathophysiology) | 3,844 | +| …of which name **zero** intermediates | 1,466 (38 %) | +| named intermediate strings | 3,465 | +| …that **match an actual graph node** | **27** | +| …with a real 2-hop path in the graph | 113 | + +So a supervised evaluation has **27–113 usable cases**, not thousands. Any +claim that this corpus validates the loop must cite that number, not the gap +count. The gap count says how much work there is; the match count says how much +of it is *checkable today*. + +## §6 — Deliverables + +| D-id | Scope | Falsifier | +|---|---|---| +| **D-KUH-1** | Name the Hole's handover shape: an owned row moving between mailboxes, with the lifecycle `Open → Proposed → {Resolved \| Refuted}`. Design only — feeds `D-ACR-16`. | a design that requires a scheduler, a broadcast, or a shared log has rebuilt what `E-PROGRESSION-IS-EXISTENCE-NOT-COMMAND-1` and `E-AGENT-LOG-SHARED-SINK-ANTIPATTERN-1` deleted | +| **D-KUH-2** | Wire `WitnessKind` → `HoleV3` once B1 clears. | a `KNOWN-UNKNOWN` band read must resolve to a Hole row, and a low-trust KNOWN must **not** — both asserted (fire + stay-silent) | +| **D-KUH-3** | Plasticity as the rewire signal: measure whether `plasticity()` moves when a Hole closes. | **BLOCKED on B1.** Two-sided: it must move on a real close AND must NOT move on a refuted one; a signal that fires on both carries no information | +| **D-KUH-4** | Growth = minting rows, never widening fields: a probe that adds N Holes and asserts `ENVELOPE_LAYOUT_VERSION` and every field width are unchanged. | any width change is the failure; this is §0's fence as a test | + +**Sequencing:** D-KUH-1 (design, unblocked) → B1 clears → D-KUH-2 → D-KUH-3. +D-KUH-4 can run at any point after D-KUH-2 and is cheap. + +## §7 — What this plan does NOT claim + +It does not claim the network learns, that handover improves resolution, that +plasticity currently rewires anything, or that 27 checkable cases validate a +method. It claims one thing: **the loop's links are individually shipped or +individually designed, and exactly one — handover — is neither.** Every number +that would justify more has to be measured after D-KUH-1. + +The grade discipline of the parent plan applies verbatim +(`alpha-channel-rung-overlay-v1.md` §0 piece 7): this is **a pruner, not a +proof**. "AGI-aspiring" names a direction; it is not a property any deliverable +here asserts. From 7c290285e7a3b0c41629da94a036dc616c084667 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 14:40:07 +0000 Subject: [PATCH 03/10] =?UTF-8?q?plan(known-unknown-handover):=20=C2=A78?= =?UTF-8?q?=20=E2=80=94=20the=20arithmetic=20of=20=E2=86=91n=20(exponentia?= =?UTF-8?q?l=20space,=20logarithmic=20path;=20stacking=20multiplies=20expo?= =?UTF-8?q?nents=20while=20paths=20add;=20the=20pothole=20is=20the=20desce?= =?UTF-8?q?nt=20selector).=20Radix=20half=20graded=20[G],=20descent-select?= =?UTF-8?q?or=20half=20carried=20by=20D-KUH-1=20as=20design=20intent.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../known-unknown-handover-network-v1.md | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.claude/plans/known-unknown-handover-network-v1.md b/.claude/plans/known-unknown-handover-network-v1.md index ac2831fc5..e06f12ed2 100644 --- a/.claude/plans/known-unknown-handover-network-v1.md +++ b/.claude/plans/known-unknown-handover-network-v1.md @@ -178,3 +178,40 @@ The grade discipline of the parent plan applies verbatim (`alpha-channel-rung-overlay-v1.md` §0 piece 7): this is **a pruner, not a proof**. "AGI-aspiring" names a direction; it is not a property any deliverable here asserts. + +## §8 — The arithmetic of ↑n (operator sharpening, 2026-08-21, appended while the council ran) + +Operator: *"6×2×8bit ↑n is making n^n ⇒ n↑log(n) in any given thinking space — +we take universes of rabbitholes one epistemic pothole at a time."* + +Translated into the measured property rather than left as poetry, this is the +radix arithmetic the canon already pins (*"the key prerenders nodes with zero +value decode"*), applied to the awareness axis: + +- One 12-byte atom under `G6D2` addresses `256^12 ≈ 7.9×10^28` distinct points + per class at full depth. A focus at depth `d` covers `256^(12−d)` of them — + measured in D-ACR-1's own test suite (`one_shallow_focus_covers_an_unbounded_ + population`: depth 2, 65,536 addresses across the two units varied). +- **The space is exponential in depth; the path is linear in depth.** Reaching + any specific address costs at most 12 refinement steps = `log₂₅₆(space)`. + That is the `n^n ⇒ n↑log(n)` claim in checkable form: exploration cost grows + with the *logarithm* of the space explored, because each step is one prefix + level, never a scan. +- **Stacking (`↑n`) multiplies exponents while paths add.** A second register + (a V3 stack level, another of the node's 32 facet slots) squares the + addressable space and adds 12 to the worst-case path — exponent + multiplication bought at additive path cost. This is why growth is minting + rows/levels and never widening fields (§0): widening buys linear space at + layout-break cost; stacking buys exponential space at logarithmic + navigation cost. +- **"One epistemic pothole at a time" is the descent rule.** The pothole marks + WHICH subtree to refine next; each handover/exploration step descends exactly + one prefix level of one Hole. The rabbit-hole universe is never entered + whole — it is entered one level of one hole at a time, which is what keeps + the sweep O(holes × depth) instead of O(space). + +Grade: the radix arithmetic is [G] (it is what a 256-ary prefix tree is); the +identification of "pothole" with "descent selector" is design intent carried +by D-KUH-1, not yet a measured behaviour. The `n↑log(n)` notation is the +operator's shorthand for exponential-space/logarithmic-path and is recorded as +such, not as a formal tetration claim. From 5981432b08c9785b0447e3929205ef0cd8efa4be Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 14:47:51 +0000 Subject: [PATCH 04/10] =?UTF-8?q?plan(known-unknown-handover):=20=C2=A79?= =?UTF-8?q?=20=E2=80=94=20the=20handover=20mechanism=20(operator=20design?= =?UTF-8?q?=20input)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handover = ownership transfer IN PLACE of the shared classid+6x2x8bit atom. Verified against the substrate: zero-copy is not an optimization but the compliance condition -- the V3 tombstone ruling removed every inter-mailbox handoff type ("nothing is serialized or transmitted between mailboxes"), so a copying handover would be forbidden, not slow. The classid IS the briefing (ClassView resolves the reading at the receiver; no protocol negotiation). The narrow gap, measured: SoaEnvelope::mailbox_owner exists (soa_envelope.rs:195) but NO ownership-transfer operation exists anywhere -- ownership is static today. The design therefore reduces D-KUH-1 + D-ACR-16 from "design a handover protocol" to "design the owner-change operation": format, self-description and zero-copy already exist. One open detail named rather than smoothed: prefix depth lives outside the 16 bytes (D-ACR-1's explicit-depth rule), so a raw handover would silently read as depth-12 -- every wildcard focus would look like a pinpoint claim. The depth byte's home in the Hole row's value slab is D-KUH-1's to fix. STATUS_BOARD D-KUH-1 row updated in the same commit. --- .claude/board/STATUS_BOARD.md | 2 +- .../known-unknown-handover-network-v1.md | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index 3a19ca720..4a0c643fc 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -50,7 +50,7 @@ the rest. | D-id | Scope | Status | Falsifier | |---|---|---|---| -| D-KUH-1 | Name the Hole's handover shape: an owned row moving between mailboxes, lifecycle `Open -> Proposed -> {Resolved \| Refuted}`. Design only — feeds D-ACR-16 | **Next** — the only unblocked row in `known-unknown-handover-network-v1.md` | a design needing a scheduler / broadcast / shared log has rebuilt what `E-PROGRESSION-IS-EXISTENCE-NOT-COMMAND-1` and `E-AGENT-LOG-SHARED-SINK-ANTIPATTERN-1` deleted | +| D-KUH-1 | Name the Hole's handover shape: an owned row moving between mailboxes, lifecycle `Open -> Proposed -> {Resolved \| Refuted}`. Design only — feeds D-ACR-16 | **Next** — design core operator-supplied 2026-08-21 (plan §9): handover = ownership transfer in place of the shared `classid+6×2×8bit` atom; the ONLY legal shape under the tombstone ruling. Remaining: owner-change op, depth byte's home, lifecycle stamps | a design needing a scheduler / broadcast / shared log has rebuilt what `E-PROGRESSION-IS-EXISTENCE-NOT-COMMAND-1` and `E-AGENT-LOG-SHARED-SINK-ANTIPATTERN-1` deleted | | D-KUH-2 | Wire `WitnessKind` -> `HoleV3` | **BLOCKED** on `BoardAggregates = 15` resolving (contiguous `ValueTenant` discriminants) — a prerequisite, not a queue position | a KNOWN-UNKNOWN band read resolves to a Hole AND a low-trust KNOWN does not — both asserted | | D-KUH-3 | Plasticity as the rewire signal — does `plasticity()` move when a Hole closes? | BLOCKED on D-KUH-2 | two-sided: moves on a real close, does NOT move on a refuted one; firing on both carries no information | | D-KUH-4 | Growth = minting rows, never widening fields: add N Holes, assert `ENVELOPE_LAYOUT_VERSION` and every field width unchanged | Queued — cheap, any time after D-KUH-2 | any width change is the failure (the §0 fence as a test) | diff --git a/.claude/plans/known-unknown-handover-network-v1.md b/.claude/plans/known-unknown-handover-network-v1.md index e06f12ed2..23d33c3f3 100644 --- a/.claude/plans/known-unknown-handover-network-v1.md +++ b/.claude/plans/known-unknown-handover-network-v1.md @@ -215,3 +215,58 @@ identification of "pothole" with "descent selector" is design intent carried by D-KUH-1, not yet a measured behaviour. The `n↑log(n)` notation is the operator's shorthand for exponential-space/logarithmic-path and is recorded as such, not as a formal tetration claim. + +## §9 — The handover mechanism (operator design input, 2026-08-21) + +Operator: *"Handover soll dadurch ermöglicht werden, dass jedes Glied 6×2×8bit +kann — mit einer classid — und Übergabe von attention durchgereicht werden +kann, zero copy."* + +This supplies D-KUH-1's design core, and it survives verification against the +substrate's own rulings — in fact it turns out to be the **only legal shape**: + +**1. Every link already speaks the payload.** That is what §Handover's parent +finding measured: the `classid(4) | 6×(8:8)(12) = 16 B` atom is the shared +format at six independent sites. A handover therefore needs NO new wire type, +no message schema, no serialization — the thing being handed over is a facet +every receiver can already read. + +**2. The classid IS the briefing.** The receiver needs no protocol negotiation: +`classid → ClassView` resolves which reading applies to the 12 bytes (the +canon's *"the key prerenders nodes with zero value decode"*). A handed-over +attention facet is **self-describing** — the receiving mailbox reads the same +bytes under its ClassView, full stop. + +**3. Zero-copy is not an optimization here — it is the compliance condition.** +The V3 tombstone ruling (`soa-three-tier-model.md`, `CLAUDE.md` 2026-06-11 +supersession) states there is **no inter-mailbox handoff type at all**: +*"nothing is serialized or transmitted between mailboxes"* — the Baton was +removed from source. So a handover that copied or transmitted would not be +slow, it would be **forbidden**. The only legal handover is **ownership +transfer in place**: the bytes never move; what changes is who may write. + +**4. The narrow gap, measured this session:** `SoaEnvelope::mailbox_owner` +exists (`soa_envelope.rs:195`, default `0` = bootstrap/unowned, overridable) — +but **no ownership-transfer operation exists anywhere** (grep for +`transfer`/`reassign`/`set_owner`/`change_owner` across contract + supervisor: +zero hits). Ownership is static today. **The operator's design therefore +reduces D-KUH-1 + D-ACR-16 from "design a handover protocol" to "design the +owner-change operation"** — format, self-description and zero-copy all already +exist; only the stamp-change is unbuilt. That operation must respect +write-on-behalf (the current owner writes the new owner in; the receiver never +grabs) and existence-not-command (receiving ownership IS the notification; +there is no `advance()` call). + +**5. One open detail, named rather than smoothed:** the prefix `depth` lives +OUTSIDE the 16 bytes (D-ACR-1's explicit-depth rule — zero bytes are dormant +tiers, not terminators). A handed-over focus must carry its depth somewhere; +the natural home is the Hole row's own value slab (480 B, `GUIDS_PER_NODE=32` +slots of which the facet occupies one — const-asserted, +`canonical_node.rs:805-808`). **D-KUH-1's design doc must fix that byte's +position**; until then a raw 16-byte handover silently reads as depth-12 +(exact), which would make every wildcard focus look like a pinpoint claim. + +**Consequence for the deliverable table:** D-KUH-1's scope line stands, its +design core is now operator-supplied; what remains is (a) the owner-change +operation's contract, (b) the depth byte's home, (c) the lifecycle stamps — +all three inside the shape fixed here, none of them a new format. From 97ba45417f47d2360314ddeaecf2efd67fec136a Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 14:52:56 +0000 Subject: [PATCH 05/10] =?UTF-8?q?plan(known-unknown-handover):=20=E2=8A=98?= =?UTF-8?q?=20=C2=A79=20corrected=20=E2=80=94=20no=20owner=20change;=20han?= =?UTF-8?q?dover=20is=20a=20focus=20entry=20in=20the=20next=20rung=20layer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Operator correction: "kein Owner-Wechsel, nur Focus der Aufmerksamkeit ueber rung levels mit Alpha layer fuer thinking about thinking." §9 point 4 had read the static ownership as the gap; that was backwards. Static ownership is the design -- an owner change would be a substrate mutation, the command-shaped intervention existence-not-command deleted and the contamination boundary exists to prevent. Handover = the appearance of an AttentionFocusFacet entry in the NEXT rung layer of the alpha overlay, at the same address. The Hole never moves. Rung n+1 writes its own layer (one-writer holds trivially). Escalation is a second-order read plus an own-layer focus entry -- exactly D-ACR-4's read path. Collapses three things: the remaining work is an overlay operation (RowFocusMask::insert, shipped today) rather than a substrate one; the depth detail dissolves (depth travels in the overlay entry, raw 16-byte transport never happens); and D-ACR-16's cascade IS the stack of rung layers, the card being the focus entry, never the row. Point 4's measurement stays true (no transfer op exists) but regrades GAP -> CONFIRMS: its absence is the design. Handover doc + STATUS_BOARD D-KUH-1 corrected in the same commit, append-only. --- .claude/board/STATUS_BOARD.md | 2 +- .../2026-08-21-2330-session-to-next.md | 14 +++++++ .../known-unknown-handover-network-v1.md | 39 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index 4a0c643fc..929f19378 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -50,7 +50,7 @@ the rest. | D-id | Scope | Status | Falsifier | |---|---|---|---| -| D-KUH-1 | Name the Hole's handover shape: an owned row moving between mailboxes, lifecycle `Open -> Proposed -> {Resolved \| Refuted}`. Design only — feeds D-ACR-16 | **Next** — design core operator-supplied 2026-08-21 (plan §9): handover = ownership transfer in place of the shared `classid+6×2×8bit` atom; the ONLY legal shape under the tombstone ruling. Remaining: owner-change op, depth byte's home, lifecycle stamps | a design needing a scheduler / broadcast / shared log has rebuilt what `E-PROGRESSION-IS-EXISTENCE-NOT-COMMAND-1` and `E-AGENT-LOG-SHARED-SINK-ANTIPATTERN-1` deleted | +| D-KUH-1 | Name the Hole's handover shape: an owned row moving between mailboxes, lifecycle `Open -> Proposed -> {Resolved \| Refuted}`. Design only — feeds D-ACR-16 | **Next** — design core operator-supplied 2026-08-21, then ⊘-corrected same day (plan §9+⊘): **no owner change** — handover = a focus entry in the NEXT rung layer of the alpha overlay at the same address (thinking-about-thinking IS the mechanism, D-ACR-4's read path). Remaining: the n+1-layer insert convention + lifecycle stamps; depth detail dissolved (travels in the overlay entry) | a design needing a scheduler / broadcast / shared log has rebuilt what `E-PROGRESSION-IS-EXISTENCE-NOT-COMMAND-1` and `E-AGENT-LOG-SHARED-SINK-ANTIPATTERN-1` deleted | | D-KUH-2 | Wire `WitnessKind` -> `HoleV3` | **BLOCKED** on `BoardAggregates = 15` resolving (contiguous `ValueTenant` discriminants) — a prerequisite, not a queue position | a KNOWN-UNKNOWN band read resolves to a Hole AND a low-trust KNOWN does not — both asserted | | D-KUH-3 | Plasticity as the rewire signal — does `plasticity()` move when a Hole closes? | BLOCKED on D-KUH-2 | two-sided: moves on a real close, does NOT move on a refuted one; firing on both carries no information | | D-KUH-4 | Growth = minting rows, never widening fields: add N Holes, assert `ENVELOPE_LAYOUT_VERSION` and every field width unchanged | Queued — cheap, any time after D-KUH-2 | any width change is the failure (the §0 fence as a test) | diff --git a/.claude/handovers/2026-08-21-2330-session-to-next.md b/.claude/handovers/2026-08-21-2330-session-to-next.md index a09918ac1..862e13222 100644 --- a/.claude/handovers/2026-08-21-2330-session-to-next.md +++ b/.claude/handovers/2026-08-21-2330-session-to-next.md @@ -175,3 +175,17 @@ name in the substrate is a premature commitment to the first.** The five homonym collisions of this arc (witness / nibble / hydration / attention-mask / TrustTexture ×4) were the expensive form; naming the focus axes would have been the first self-inflicted one. + +--- + +## ⊘ Nachtrag (später am selben Tag): das Handover-Framing oben ist superseded + +The line above — *"a Hole as a kanban card moving as an **owned row between +mailboxes**"* — is corrected by the operator: **kein Owner-Wechsel.** Handover +is a focus-of-attention entry in the NEXT rung layer of the alpha overlay at +the same address; the Hole never moves and ownership stays static (its absence +of a transfer operation is the design, not a gap). Full correction: +`known-unknown-handover-network-v1.md` §9's ⊘ block. The remaining work is an +overlay operation (`RowFocusMask::insert` in the n+1 layer — shipped, D-ACR-1), +and D-ACR-16's cascade is the stack of rung layers, the card being the focus +entry, never the row. diff --git a/.claude/plans/known-unknown-handover-network-v1.md b/.claude/plans/known-unknown-handover-network-v1.md index 23d33c3f3..b6b516484 100644 --- a/.claude/plans/known-unknown-handover-network-v1.md +++ b/.claude/plans/known-unknown-handover-network-v1.md @@ -270,3 +270,42 @@ position**; until then a raw 16-byte handover silently reads as depth-12 design core is now operator-supplied; what remains is (a) the owner-change operation's contract, (b) the depth byte's home, (c) the lifecycle stamps — all three inside the shape fixed here, none of them a new format. + +> **⊘ §9 KORRIGIERT (Operator, 2026-08-21): "kein Owner-Wechsel, nur Focus der +> Aufmerksamkeit über rung levels mit Alpha layer für thinking about +> thinking."** §9 point 4 read the static ownership as the gap ("the +> owner-change operation is unbuilt"). That was backwards: **static ownership +> is CORRECT and stays.** Nothing that moves ownership is to be built — an +> owner change would be a substrate mutation, i.e. exactly the command-shaped +> intervention `E-PROGRESSION-IS-EXISTENCE-NOT-COMMAND-1` deleted and the +> alpha-channel plan's §2 contamination boundary exists to prevent. +> +> **Handover = the appearance of a focus entry in the NEXT rung layer of the +> alpha overlay, at the same address.** The Hole does not move, does not change +> owner, is not transmitted. Rung n could not close it → rung n+1's overlay +> layer gains an `AttentionFocusFacet` entry covering the Hole's address — +> written by that layer's own owner into its own layer (one-writer holds +> trivially: every rung layer has exactly one owner, itself). Thinking about +> thinking IS the handover mechanism: escalation is a second-order read plus an +> own-layer focus entry, which is precisely `D-ACR-4`'s read path (*"a rung-2 +> read reconstructs where rung-1 looked"*). +> +> Three things this collapses: +> +> 1. **The remaining work is an OVERLAY operation, not a substrate one.** +> `RowFocusMask::insert` in the n+1 layer — shipped today (D-ACR-1) — with +> the Hole's focus facet. No new operation on rows, envelopes, or owners. +> 2. **§9 point 5's open detail dissolves.** Depth travels in the overlay entry +> (`AttentionFocusFacet` carries it outside the 16 bytes, by design) — raw +> 16-byte transport never happens, so the depth-12 misread cannot occur. +> 3. **D-ACR-16's shape sharpens further:** the nested kanban cascade IS the +> stack of rung layers; the card is the focus entry, never the row. Zero +> copy is exceeded, not merely met — not even an ownership stamp changes, +> the graph stays untouched (the contamination boundary), and the overlay +> stays discardable whole (a lost handover costs a re-search, never +> correctness). +> +> §9's points 1–3 stand unchanged (shared format, classid-as-briefing, +> zero-copy as compliance condition). Point 4's measurement stays true — no +> transfer operation exists — but is regraded from GAP to **CONFIRMS**: its +> absence is the design, not the debt. From 1e59d340c871afaf5804b032cda0f1061f5c94f7 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 14:54:23 +0000 Subject: [PATCH 06/10] =?UTF-8?q?plan(known-unknown-handover):=20=E2=8A=98?= =?UTF-8?q?=E2=8A=98=20handover=20is=20two-armed=20by=20substrate=20nature?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Operator refinement interrupting the prior push: "in dynamischem Substrat in place mit Lance versioning, in statischem Ontologie-Substrat Alpha layer." The first correction made the overlay THE mechanism everywhere; refined: static ontology -> alpha-layer entry at the same address (contamination boundary, overlay discardable whole); dynamic substrate -> in-place write by the row's one owner, with Lance versioning as the residue carrier -- episodic = Lance versions (E-MARKOV-TEMPORAL-STREAM-1), rung n+1 reads "where rung n looked" as a version-range read via QueryReference::at(v, rung): a projection, zero copies, replayable (alpha-plan §3c said it: a read at a version, never a stored history column). One invariant, two implementations: attention leaves a replayable trace without contaminating what it observed. Static side: the trace is a separate layer, because the substrate must not move. Dynamic side: the trace is the substrate's own motion, because it moves anyway and Lance keeps every version. The dynamic arm needs NOTHING built. Remaining work is solely the overlay arm's n+1-layer insert convention (RowFocusMask shipped) + lifecycle stamps. D-ACR-16's cascade is now fully shaped: rung layers over the static substrate, version-range reads over the dynamic one. Handover doc + STATUS_BOARD updated same commit, append-only throughout. --- .claude/board/STATUS_BOARD.md | 2 +- .../2026-08-21-2330-session-to-next.md | 7 ++++++ .../known-unknown-handover-network-v1.md | 25 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index 929f19378..ca60d6ec6 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -50,7 +50,7 @@ the rest. | D-id | Scope | Status | Falsifier | |---|---|---|---| -| D-KUH-1 | Name the Hole's handover shape: an owned row moving between mailboxes, lifecycle `Open -> Proposed -> {Resolved \| Refuted}`. Design only — feeds D-ACR-16 | **Next** — design core operator-supplied 2026-08-21, then ⊘-corrected same day (plan §9+⊘): **no owner change** — handover = a focus entry in the NEXT rung layer of the alpha overlay at the same address (thinking-about-thinking IS the mechanism, D-ACR-4's read path). Remaining: the n+1-layer insert convention + lifecycle stamps; depth detail dissolved (travels in the overlay entry) | a design needing a scheduler / broadcast / shared log has rebuilt what `E-PROGRESSION-IS-EXISTENCE-NOT-COMMAND-1` and `E-AGENT-LOG-SHARED-SINK-ANTIPATTERN-1` deleted | +| D-KUH-1 | Name the Hole's handover shape: an owned row moving between mailboxes, lifecycle `Open -> Proposed -> {Resolved \| Refuted}`. Design only — feeds D-ACR-16 | **Next** — design core operator-supplied 2026-08-21, then ⊘-corrected same day (plan §9+⊘): **no owner change** — handover = a focus entry in the NEXT rung layer of the alpha overlay at the same address (thinking-about-thinking IS the mechanism, D-ACR-4's read path). ⊘⊘ two-armed by substrate: static ontology → alpha layer; dynamic substrate → in place with Lance versioning (`QueryReference::at(v, rung)` replays — nothing to build on that arm). Remaining: the n+1-layer insert convention + lifecycle stamps | a design needing a scheduler / broadcast / shared log has rebuilt what `E-PROGRESSION-IS-EXISTENCE-NOT-COMMAND-1` and `E-AGENT-LOG-SHARED-SINK-ANTIPATTERN-1` deleted | | D-KUH-2 | Wire `WitnessKind` -> `HoleV3` | **BLOCKED** on `BoardAggregates = 15` resolving (contiguous `ValueTenant` discriminants) — a prerequisite, not a queue position | a KNOWN-UNKNOWN band read resolves to a Hole AND a low-trust KNOWN does not — both asserted | | D-KUH-3 | Plasticity as the rewire signal — does `plasticity()` move when a Hole closes? | BLOCKED on D-KUH-2 | two-sided: moves on a real close, does NOT move on a refuted one; firing on both carries no information | | D-KUH-4 | Growth = minting rows, never widening fields: add N Holes, assert `ENVELOPE_LAYOUT_VERSION` and every field width unchanged | Queued — cheap, any time after D-KUH-2 | any width change is the failure (the §0 fence as a test) | diff --git a/.claude/handovers/2026-08-21-2330-session-to-next.md b/.claude/handovers/2026-08-21-2330-session-to-next.md index 862e13222..03b18e345 100644 --- a/.claude/handovers/2026-08-21-2330-session-to-next.md +++ b/.claude/handovers/2026-08-21-2330-session-to-next.md @@ -189,3 +189,10 @@ of a transfer operation is the design, not a gap). Full correction: overlay operation (`RowFocusMask::insert` in the n+1 layer — shipped, D-ACR-1), and D-ACR-16's cascade is the stack of rung layers, the card being the focus entry, never the row. + +**⊘⊘ Second refinement, same day:** handover is TWO-ARMED by substrate nature — +static ontology → alpha-layer entry (contamination boundary); **dynamic +substrate → in place with Lance versioning** (episodic = Lance versions; +rung n+1 reads "where rung n looked" via `QueryReference::at(v, rung)` — zero +copies, replayable). The dynamic arm needs nothing built. Full table: +`known-unknown-handover-network-v1.md` §9 ⊘⊘. diff --git a/.claude/plans/known-unknown-handover-network-v1.md b/.claude/plans/known-unknown-handover-network-v1.md index b6b516484..19197a9c7 100644 --- a/.claude/plans/known-unknown-handover-network-v1.md +++ b/.claude/plans/known-unknown-handover-network-v1.md @@ -309,3 +309,28 @@ all three inside the shape fixed here, none of them a new format. > zero-copy as compliance condition). Point 4's measurement stays true — no > transfer operation exists — but is regraded from GAP to **CONFIRMS**: its > absence is the design, not the debt. + +> **⊘⊘ REFINED (operator, same day, interrupting the push): "in dynamischem +> Substrat in place mit Lance versioning, in statischem Ontologie-Substrat +> Alpha layer."** The ⊘ block above made the overlay THE mechanism everywhere. +> Refined: **handover has TWO ARMS, selected by the substrate's nature** — +> +> | substrate | attention travels as | residue/history carrier | ruling it lands on | +> |---|---|---|---| +> | **static ontology** (shared, durable, cacheable — must stay uncontaminated) | an alpha-layer entry (`AttentionFocusFacet` in the next rung layer) at the same address | the overlay itself — discardable whole | alpha-plan §2 contamination boundary | +> | **dynamic substrate** (session / patient / working rows — allowed to change) | an **in-place write by the row's one owner** | **Lance versioning** — episodic = Lance versions; rung n+1's *"where did rung n look"* is a version-range read, `QueryReference::at(v, rung)`: a projection, zero copies, replayable | `E-MARKOV-TEMPORAL-STREAM-1`; alpha-plan §3c (*"a read at a version, never a stored history column"*) | +> +> The symmetry is the principle stated once: **attention leaves a replayable +> trace without contaminating what it observed.** On the static side the trace +> is a separate layer, because the substrate must not move; on the dynamic side +> the trace is the substrate's own motion, because it moves anyway and Lance +> keeps every version. Two implementations, one invariant — and both arms are +> zero-copy readings, both one-writer-clean (the layer's owner writes its +> layer; the row's owner writes its row). +> +> **The dynamic arm needs NOTHING built.** Lance versioning and +> `QueryReference::at` are shipped; the version stream IS the alpha channel of +> the dynamic side. What remains is solely the overlay arm's n+1-layer insert +> convention (`RowFocusMask` shipped today) plus the Hole lifecycle stamps — +> and D-ACR-16's cascade is now fully shaped: rung layers stacked over the +> static substrate, version-range reads over the dynamic one. From 16210458013d45b3fedd863711cea6d58e7753b5 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 14:56:04 +0000 Subject: [PATCH 07/10] =?UTF-8?q?plan(known-unknown-handover):=20=C2=A710?= =?UTF-8?q?=20=E2=80=94=20Gestalt,=20meta-awareness,=20self-organization:?= =?UTF-8?q?=20where=20each=20word=20cashes=20out?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The operator's closing claim, recorded with each term bound to its mechanism. Gestalt = the shallow-prefix read (a whole apprehended without enumerating members; RowFocusMask's minimal antichain as a mechanical Praegnanz operation, marked observation-not-doctrine; multistability via classid-selected readings). Meta-awareness = rung n+1 entries about rung n's at the same addresses, QueryReference::at as replayable self-history, the Hole as the metacognitive act made a row, MUL confidence-invariance as awareness of awareness's reliability. Self-organizing "durch die Architektur" made precise: the SO literature's preconditions map one-to-one onto RULINGS, not onto code that could rot -- local rules (one-writer, no scheduler), a stigmergic medium (the two-armed trace: alpha layers static, Lance versions dynamic), a gradient (potholes, one at a time), accretion (minting, never widening), terrain protection (contamination boundary). The stigmergy identification is graded [H]: exact structural match, no collective behaviour measured. The emergence claim itself stays CONJECTURE and already has its falsifiers -- D-ACR-8, D-ACR-10, D-ACR-11, D-KUH-3 ARE the emergence tests; none new needed. The preconditions are architectural invariants, the emergence is a prediction, and the prediction's tests are pre-registered. --- .../known-unknown-handover-network-v1.md | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/.claude/plans/known-unknown-handover-network-v1.md b/.claude/plans/known-unknown-handover-network-v1.md index 19197a9c7..6c3ad66a7 100644 --- a/.claude/plans/known-unknown-handover-network-v1.md +++ b/.claude/plans/known-unknown-handover-network-v1.md @@ -334,3 +334,71 @@ all three inside the shape fixed here, none of them a new format. > convention (`RowFocusMask` shipped today) plus the Hole lifecycle stamps — > and D-ACR-16's cascade is now fully shaped: rung layers stacked over the > static substrate, version-range reads over the dynamic one. + +## §10 — Gestalt, meta-awareness, self-organization: where each word cashes out (operator closing claim, 2026-08-21) + +Operator: *"Dadurch entsteht Gestalt und Meta-Awareness — und durch die +Architektur wird es self-organizing."* Recorded with each term bound to its +mechanism, because an emergence claim without a mechanism is the exact +overclaim shape this board fences. + +**Gestalt** — apprehending a whole without enumerating its parts: + +- A shallow focus facet IS the gestalt read: one 16-byte atom at depth `d` + covers `256^(12−d)` addresses — the subtree perceived as a unit, no member + visited (§8's radix arithmetic, D-ACR-1's measured wildcard). +- `RowFocusMask`'s absorption into a **minimal antichain** is a mechanical + Prägnanz operation: the mask converges to the simplest description of where + attention went (covered entries absorbed, never enumerated). *(Observation, + not doctrine — the correspondence is structural, not measured.)* +- Multistability is architectural: the same 12 bytes read as different wholes + per `classid → ClassView` — the duck-rabbit resolved by declaration instead + of ambiguity (D-ACR-1's two-readings test proves the projection is free). + +**Meta-awareness** — awareness whose OBJECT is awareness: + +- Rung n+1's layer entries are *about* rung n's entries at the same addresses + (D-ACR-4: *"a rung-2 read reconstructs where rung-1 looked"*). On the dynamic + arm, `QueryReference::at(v, rung)` is awareness of one's own past states — + replayable, zero copies. +- The Hole is the core metacognitive act as a ROW: knowing that one does not + know, with lifecycle. +- MUL's confidence-invariance (§3i of the parent plan) is awareness of the + *reliability* of one's own awareness — with the 20/34 mute tactics as + measurable overconfidence-by-construction. + +**Self-organizing — and why "durch die Architektur" is the precise phrase.** +The self-organization literature's preconditions map one-to-one onto RULINGS +here, not onto code that could rot: + +| SO precondition | its ruling / mechanism | +|---|---| +| local rules, no central controller | one-writer-per-mailbox (`E-CE64-MB-4`); no scheduler (`E-PROGRESSION-IS-EXISTENCE-NOT-COMMAND-1`) | +| a **stigmergic medium** (activity leaves traces that guide later activity) | the two-armed trace: alpha layers over the static substrate, Lance versions over the dynamic one (§9 ⊘⊘) | +| gradient to descend | potholes; one epistemic pothole at a time (§8's descent rule) | +| structure accretes where activity accumulates | growth = minting rows/levels, never widening (§0) | +| the medium must not corrupt the terrain | contamination boundary (alpha-plan §2); overlay discardable whole | + +The stigmergy identification is the load-bearing one: ants organize through +pheromone traces in the environment, not through messages to each other — and +this substrate's attention traces are exactly that, with the refinement that +the static terrain gets a separate trace layer while the dynamic terrain's own +version history IS its trace. *(Grade: [H] — the structural match is exact; +no collective behaviour has been measured yet.)* + +**The emergence claim itself stays CONJECTURE, and it already has its +falsifiers — no new ones needed:** + +| claim | existing gate that tests it | +|---|---| +| the awareness discriminates (is not a watcher that cannot dissent) | D-ACR-8 (focus measurably broader in `Planning` than `CognitiveWork`, AND indistinguishable on a no-deliberation task) | +| the suspense is real (not lookup wearing NARS vocabulary) | D-ACR-10 (pothole-open SPANS must vary and eventually close) | +| the rewiring responds to resolution, not to noise | D-KUH-3 (plasticity moves on a real close, NOT on a refuted one) | +| the overconfidence measurement is real | D-ACR-11 (the 20 must be invariant AND at least one of the 14 must move) | + +That is the honest form of the closing claim: **the preconditions are +architectural invariants (rulings, ownership, const-asserts), the emergence is +a prediction, and the prediction's tests are already pre-registered.** A +system that manages its known-unknowns as first-class rows carries its own +"does this become what it aspires to?" as the largest of them — §7's +discipline, unchanged. From f3d9ff83b3b72b568fc3c13218e444e1d00efe7d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 15:08:29 +0000 Subject: [PATCH 08/10] =?UTF-8?q?D-ACR-7:=20band-reading=20contract=20RATI?= =?UTF-8?q?FIED=20via=205+3=20council=20=E2=80=94=203=20BLOCK(P0)=20raised?= =?UTF-8?q?,=203=20resolved?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The council ran the full iron sequence: spec v1 -> 5 savants (39 findings; code-truth 6/6 CONFIRMS) -> draft v2 -> 3 reviewers on v2 only -> Phase-4 fixes -> v3. Reviewers 1+3 were lost to a container restart and recast against the surviving draft; their verdicts were never invented in the gap. The three BLOCKs, each verified at source before acceptance: 1 (overclaim). "V3's bytes were never temporal" is FALSE for populated instances: from_v1(e, target) has no provenance parameter and raw-copies truth/spare (edge_v3.rs:117,138-139). The zero-stub comment above it is a compile-time feature condition, not a runtime guarantee -- a v1/unknown CE64 with temporal >= 512 lifted under a v2 build carries stale band bits into V3 byte 9, indistinguishable from clean. V3Register is redefined as a caller assertion ("minted clean"), never an inference; unstated origin = Unknown = refuse. The v1 trap applies to BOTH carriers, on V3 transitively through the lift. The from_v1 provenance drop is filed as a causal-edge follow-up. 2 (overclaim). G5 contradicted the total-lookup fix (split into G5a zero-fallback / G5b must-error), and G10a was a same-function-same-input tautology -- deleted under the falsifiability rule. G10b survives: compare CausalEdge64::truth() vs CausalEdgeV3::truth_raw() on the same edge post-from_v1, hosted in causal-edge (both crates' zero-dep postures hold, measured on both Cargo.tomls), closing a measured missing test: the module doc claims truth/spare survive the round trip and no test asserts it. 3 (firewall). Draft v2 contained zero occurrences of any board file -- the Phase-2 consolidation dropped Savant 4's complete hygiene answer. Restored as the spec's committed board-hygiene table. Post-ratification operator addendum: all V3-generation carriers confirmed unwired/planned (the contract is prescriptive by construction AND by ruling); the temporal doctrine -- time is implicit in the epistemic pothole (Lance versions, QueryReference::at), explicit ONLY in Rubikon revision / CausalEdgeV3's TE byte / a future attention-v3 reading; EdgeProvenance is layout epoch, never time. The v1 trap generalized: temporal went stale because it lived implicitly in a reclaimable field. Board hygiene same-commit: EPIPHANIES prepend (the from_v1 finding + the TrustTexture x4 homonym folded in), STATUS_BOARD D-ACR-7 row (Spec RATIFIED, gates G1..G10b), INTEGRATION_PLANS prepend, AGENT_LOG council entry (orchestrator sole writer), handover superseded-note. Implementation of contract::band_reading against G1..G10b is the next unit. --- .claude/board/AGENT_LOG.md | 43 ++ .claude/board/EPIPHANIES.md | 66 ++ .claude/board/INTEGRATION_PLANS.md | 15 + .claude/board/STATUS_BOARD.md | 1 + .../2026-08-21-2330-session-to-next.md | 14 + .../plans/dacr7-band-reading-contract-v1.md | 649 ++++++++++++++++++ 6 files changed, 788 insertions(+) create mode 100644 .claude/plans/dacr7-band-reading-contract-v1.md diff --git a/.claude/board/AGENT_LOG.md b/.claude/board/AGENT_LOG.md index 0644cf751..f47a71290 100644 --- a/.claude/board/AGENT_LOG.md +++ b/.claude/board/AGENT_LOG.md @@ -1,3 +1,46 @@ +## 2026-08-21 — 5+3 council: D-ACR-7 (the 59..63 reading contract) — RATIFIED + +**Run shape:** Phase 0 spec (main thread) → 5 savants parallel (all Sonnet: +prior-art-savant, iron-rule-savant, code-truth via general-purpose, +cascade-impact-savant, creative-explorer-savant) → consolidate to draft v2 → +3 reviewers parallel on v2 ONLY (Sonnet: overclaim-auditor, +dilution-collapse-sentinel, firewall-warden) → Phase-4 fixes → v3 ratified. +**Incident:** reviewers 1+3 lost to a container restart (no notification, +`ListAgents` empty) and recast against the surviving draft v2 — verdicts were +never invented in the gap. + +**Verdict counts:** savants 39 findings (code-truth 6/6 CONFIRMS — the +inventory held). Reviewer 2: 8 PASS, 1 FIX(P2). Reviewer 3: 6 PASS, 1 +BLOCK(P0), 1 FIX(P1). Reviewer 1: 4 PASS, 2 BLOCK(P0), 2 FIX(P1), 1 FIX(P2). +**3 BLOCKs raised, 3 resolved in Phase 4, none argued away, none needing a +Phase-0 re-spec.** + +**v1→v2:** dual-carrier scope (operator: CE64 = muscle memory, CausalEdgeV3 = +granularity — a gap no savant could catch, the v1 spec never named V3); +Result-vs-total split (total `ClassView::band_reading` lookup / fallible +`BandReading::project`); `Option` for the opted-out-vs-never- +considered audit; writer-side `admits()`; G3′/G4′ can-stay-silent halves; G9 +(AP1 watch), G10 (carrier parity). + +**v2→v3 (the fixes):** §6′ board-hygiene commitment restored (firewall BLOCK — +Phase 2 had dropped Savant 4's answer entirely); `V3Register` redefined as a +caller assertion after `from_v1`'s provenance-agnostic bit copy falsified +"V3's bytes were never temporal" for populated instances (overclaim BLOCK); +G5 split G5a/G5b + tautological G10a deleted, G10b (lift-fidelity test in +`causal-edge`, a measured missing test) kept (overclaim BLOCK); L6 bullets +given file:line rigor; `admits()`-"cannot" grounded in +`causal-edge/Cargo.toml:20-23`; the 319-322 citation actually applied. + +**Post-ratification operator addendum:** all V3-generation carriers confirmed +unwired/planned; the temporal doctrine (implicit in the epistemic pothole via +Lance versions; explicit ONLY in Rubikon revision / CausalEdgeV3 TE byte / +future attention-v3; `EdgeProvenance` is layout epoch, never time). + +**Artifacts:** `.claude/plans/dacr7-band-reading-contract-v1.md` (v1+v2+v3 +ledgers, single file). Board: EPIPHANIES +E-FROM-V1-DROPS-PROVENANCE-…-1, STATUS_BOARD D-ACR-7 → Spec RATIFIED. +Implementation (the `band_reading` module + gates G1..G10b) is the next unit. + ## 2026-08-19 — oracle-boundary reassessment sweep (6 agents, workflow, orchestrator-consolidated) Operator-directed after two superseding architecture rulings (the diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index 66f003f9e..7fe35cffc 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -78,6 +78,72 @@ a hard, loud cap on one side**. D-ACR-1 must state its basis and its composition operator before composing, or it inherits one of two accidents: loud refusal past 256 (borrowing the wide cap) or silent truncation past 64 (borrowing the narrow rule). +## 2026-08-21 — E-FROM-V1-DROPS-PROVENANCE-AND-THE-COUNCIL-CAUGHT-THE-CONTRACT-ABOUT-TO-TRUST-IT-1 — three BLOCK(P0)s in one 5+3 run, and the sharpest one falsified the spec's own asymmetry claim against the code it cited + +**Status:** FINDING (D-ACR-7 council, spec ratified as +`.claude/plans/dacr7-band-reading-contract-v1.md`; every claim verified at +source in Phase 4). **Confidence:** High. + +The D-ACR-7 reading contract (which lens wrote CE64 bits 59..63 — +`TrustTexture` vs `CausalTopology` on 59-60, `ReasoningBand` presence on +61-63) went through the full 5+3 council. Three BLOCK(P0)s were raised and all +three survived verification; none was argued away. + +**BLOCK 1 (overclaim, the consequential one): "V3's bytes were never temporal" +is false for populated instances.** `CausalEdgeV3::from_v1(e, target)` +(`edge_v3.rs:117`) has **no provenance parameter** and copies truth/spare as a +raw bit copy (`:138-139`). The reassuring comment above it — "under the v1 +layout every one of these accessors is a documented zero stub" — is a +**compile-time feature condition, not a runtime provenance guarantee**: a CE64 +of v1/unknown provenance (bits 61-63 aliasing `temporal >= 512`) lifted under +a v2 build carries the stale bits into V3 byte 9, and the result is +**indistinguishable from a clean register**. The draft had declared +`EdgeProvenance::V3Register` "always readable" — the exact plausible-wrong- +answer the contract exists to refuse, at its own core. Resolution: +`V3Register` means *"the caller asserts this register was minted clean"*, +never *"V3 registers are clean"*; unstated origin = `Unknown` = refuse; the +`from_v1` provenance drop is filed as a `causal-edge` follow-up, not patched +from the contract side. The v1 trap thus applies to BOTH carriers — on CE64 +directly, on V3 **transitively through the lift**. + +**BLOCK 2 (overclaim): a gate contradicted the fix that preceded it, and +another gate was a tautology.** After the council split the resolver +(total `ClassView::band_reading` lookup / fallible `BandReading::project`), +G5's carried-forward text ("an undeclared one errors") contradicted the total +half — split into G5a (total: zero-fallback, no error) / G5b (fallible: +`Err(UndeclaredClass)` must fire). And G10a ("project is a pure function of +its arguments") fed the same input to the same function twice — the +vacuous-assertion house pattern, deleted. Only G10b survives: compare +`CausalEdge64::truth()` vs `CausalEdgeV3::truth_raw()` on the same edge +post-`from_v1` — which tests the exact bit-copy site BLOCK 1 indicted, is +hosted in `causal-edge` (both crates' zero-dep postures hold — measured: +BOTH refuse the other as a dependency, `causal-edge/Cargo.toml:20-23` +explicitly), and closes a **measured missing test**: `edge_v3.rs`'s module doc +claims truth/spare survive the round trip byte-exact, and no test asserts it. + +**BLOCK 3 (firewall): the consolidation dropped board hygiene entirely.** +Draft v2 contained zero occurrences of any board file — Savant 4 had answered +the hygiene question in full, and Phase-2 consolidation lost the finding. The +loss-prevention phase lost a finding; recorded as such, restored as the spec's +§6′ commitment table. + +**Meta-catch worth keeping:** the same reviewer caught the fix ledger claiming +a citation correction it had not applied ("fixed in §2.5" while §2.5 still +read the old span) — the draft's own "measured, not assumed" standard applied +to the draft. And the council's scope itself was corrected mid-flight by the +operator (CE64 = muscle memory, `CausalEdgeV3` = granularity), a gap **no +savant could have caught** because the v1 spec never mentioned V3 and savants +answer only their question sets — the spec-writing phase is the only place +scope errors can be prevented, which is why Phase 0 is "where the real work +happens." + +Sibling finding folded in rather than given its own id: `TrustTexture` is a +**×4 homonym with three arities** (4/4/5/3 — `causal-edge/layout.rs:141`, +`contract/mul.rs:82`, `planner/mul/trust.rs:30` with `Dissonant` +unrepresentable in 2 bits, `arigraph/orchestrator.rs:114`), against +`TYPE_DUPLICATION_MAP.md:9`'s stale "×2"; lines 9, 16 and 19 of that doc must +change together. + ## 2026-08-21 — E-THE-ATTENTION-ATOM-WAS-ALREADY-SHIPPED-WHAT-WAS-MISSING-WAS-A-COMPOSITION-THAT-IS-NOT-OR-1 — D-ACR-1's basis is a reuse, and the only real gap was that every set operation in the crate is a bitset union **Status:** FINDING (D-ACR-1, implemented; 14 new tests green, 1194 contract diff --git a/.claude/board/INTEGRATION_PLANS.md b/.claude/board/INTEGRATION_PLANS.md index 7dc8d0021..f4db6516f 100644 --- a/.claude/board/INTEGRATION_PLANS.md +++ b/.claude/board/INTEGRATION_PLANS.md @@ -1,3 +1,18 @@ +## 2026-08-21 — D-ACR-7 BAND-READING CONTRACT (council-ratified spec) + +`.claude/plans/dacr7-band-reading-contract-v1.md` — the 59..63 reading +contract, ratified through a full 5+3 council (3 BLOCK(P0) raised and +resolved; run details in AGENT_LOG same date). One contract, TWO carriers: +`CausalEdge64` (muscle memory) and `CausalEdgeV3` (granularity — whose own +module doc states the problem the contract solves: *"which lens the ordinal +was written through is the producer's knowledge, not the conversion's"*). +Design: total `ClassView::band_reading` declaration lookup (sibling-consistent +with `rail_carving`/`edge_codec_flavor`) + fallible `BandReading::project` +(mismatch and stale-v1 bits must FAIL, never return a plausible value). +Headline finding: `from_v1` drops provenance, so the v1 temporal trap reaches +V3 transitively — `V3Register` is a caller assertion, never an inference. +Gates G1..G10b pre-registered. Implementation next. + ## 2026-08-21 — KNOWN-UNKNOWN HANDOVER NETWORK (the connective plan, not a new arc) `.claude/plans/known-unknown-handover-network-v1.md` — the operator's framing diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index ca60d6ec6..51432bff3 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -56,6 +56,7 @@ the rest. | D-KUH-4 | Growth = minting rows, never widening fields: add N Holes, assert `ENVELOPE_LAYOUT_VERSION` and every field width unchanged | Queued — cheap, any time after D-KUH-2 | any width change is the failure (the §0 fence as a test) | | D-ACR-0 | Audit `attention_mask.rs`/`attention_mask_actor.rs`: residue carrier, or a name collision? Report only | **Shipped** 2026-08-21 — `.claude/ATTENTION_MASK_AUDIT_2026_08_21.md`. Verdict **EXISTS-UNCALLED** + a name collision: the shipped type is a *rename register file* (`causaledge64-mailbox-rename-soa-v1.md` §4), not a residue carrier | recorded EXISTS-UNCALLED (0 callers workspace-wide + 3 sibling repos); piece E regraded — not a basis for D-ACR-1 | | D-ACR-1 | `RowFocusMask` — the one missing primitive (S3.1b names it; no crate contains it) | **Shipped** 2026-08-21 — `contract::attention_facet`. Basis: **reuse** `FacetCascade` + `CascadeShape::G6D2`, zero new bytes; `FocusAxis` is content-blind (`Axis0..Axis5`), composition is **prefix containment**, never bit-OR; explicit `depth` outside the 12 bytes (`NiblePath` precedent) | can-fire AND can-stay-silent on non-trivial input — **both green**, plus a not-a-blind-OR negative and a 1000-entry container (no `FieldMask` 64 / `WideFieldMask` 256 cap inherited) | +| D-ACR-7 | The 59..63 reading contract (§3b): name per `(classid, rail)` which lens applies and which witness carrier discriminates evidence-kind. Acceptance: tactic sampling filters on `delta_conf` capability (14/34), never on `maturity().is_production()` (31/34) | **Spec RATIFIED** 2026-08-21 via 5+3 council — `.claude/plans/dacr7-band-reading-contract-v1.md`. **3×BLOCK(P0) raised, 3 resolved in Phase 4, none argued away.** Scope grew to BOTH carriers (CE64 + `CausalEdgeV3`); the v1-provenance trap applies to V3 **transitively through `from_v1`**, which drops provenance. Implementation next | G1, G2, G3′, G4′, G5a, G5b, G6, G7′, G8, G9, G10b — pre-registered before any agent ran | | D-ACR-3 | The one-way invariant as a test: no ontology-owned write traces to a patient-tagged read through ANY call path (corrected from write-authorization-only after CodeRabbit found a session-derived value can flow to the ontology owner via a shared parameter/return, then be written as the owner's own act) | Queued — gates on D-ACR-1 | a write whose call graph includes a session-tagged read is the bug, even if the write itself is authored by the ontology owner | | D-ACR-2 | Mint the Rung-ladder rail | Queued — gates on operator mint decision (HTT §8 Q3) | `rail_carving` gains its first non-default consumer | | D-ACR-4 | Second-order row at the same address, separate table | Queued | a rung-2 read reconstructs where rung-1 looked, on a fixture with an independent answer | diff --git a/.claude/handovers/2026-08-21-2330-session-to-next.md b/.claude/handovers/2026-08-21-2330-session-to-next.md index 03b18e345..13cf9408d 100644 --- a/.claude/handovers/2026-08-21-2330-session-to-next.md +++ b/.claude/handovers/2026-08-21-2330-session-to-next.md @@ -196,3 +196,17 @@ substrate → in place with Lance versioning** (episodic = Lance versions; rung n+1 reads "where rung n looked" via `QueryReference::at(v, rung)` — zero copies, replayable). The dynamic arm needs nothing built. Full table: `known-unknown-handover-network-v1.md` §9 ⊘⊘. + +--- + +## ⊘ Council COMPLETED — v3 RATIFIED (later the same day) + +The mid-flight state above is superseded: reviewers 1+3 were lost to a +container restart, recast against the surviving draft v2, and delivered. +**3 BLOCK(P0) raised, 3 resolved, v3 ratified** — +`.claude/plans/dacr7-band-reading-contract-v1.md` (single file: spec v1 + +v2 ledger + v3 fix ledger + post-ratification operator addendum on the +temporal doctrine). Headline: `from_v1` drops provenance, so the v1 temporal +trap reaches V3 transitively; `V3Register` = caller assertion. Next unit: +implement `contract::band_reading` against gates G1..G10b (G10b lives in +`causal-edge` — a measured missing lift-fidelity test). diff --git a/.claude/plans/dacr7-band-reading-contract-v1.md b/.claude/plans/dacr7-band-reading-contract-v1.md new file mode 100644 index 000000000..2dc3669f8 --- /dev/null +++ b/.claude/plans/dacr7-band-reading-contract-v1.md @@ -0,0 +1,649 @@ +# SPEC v1 — D-ACR-7: the CE64 59..63 reading contract + +> **Council:** 5+3, convened 2026-08-21. Orchestrator = main thread. +> **Qualified because:** `5plus3-council.md` §When to convene — *"a spec whose +> wrong resolution silently corrupts downstream sessions (canon entries, +> LE-layout adjacent decisions, classid/mask semantics)"*. §3b fence 3 states +> the failure verbatim: *"A consumer that reads one while a producer wrote the +> other gets a plausible wrong answer, silently."* +> **Deliverable:** `alpha-channel-rung-overlay-v1.md` §4 `D-ACR-7`. + +## §1 FROZEN DECISIONS (cite-or-VIOLATES; never re-opened on taste) + +| # | Frozen | Source | +|---|---|---| +| F1 | Bits 59-60 = `TRUTH_SHIFT` (2 bits), bits 61-63 = `SPARE_SHIFT` (3 bits). All 64 bits covered exactly once (`_LAYOUT_COVERAGE` const-assert). | `causal-edge/src/layout.rs:65,77,93-111` | +| F2 | **Nothing derives the band.** `ReasoningBand` is set ONLY by an explicit `with_reasoning_band()` — never from `CausalMask`, `InferenceType`, NARS, MUL, `ReasoningGap`, potholes or `ThinkingStyle`. | plan §3b fence 1; `layout.rs:70-72` | +| F3 | `ReasoningBand` is **never** `RungLevel`. Unrelated enums sharing variant names at different ordinals. | plan §3b fence 2; `contract/cognitive_shader.rs:157` | +| F4 | `TrustTexture` and `CausalTopology` are **the same 2 bits read differently**; the reading must be named per `(classid, rail)`, never assumed. | plan §3b fence 3; `layout.rs:58-63` | +| F5 | The band **grades**; the **witness reference discriminates** evidence-kind. No new bit is minted for the episodic-vs-epistemic axis. | plan §3b | +| F6 | Acceptance: any tactic-sampling pass filters on **`delta_conf` capability (14/34)**, never on `maturity().is_production()` (31/34). | plan §4 D-ACR-7 row; §3g; `contract/recipe_kernels.rs:319-322` | +| F7 | **No new CE64 bit, no `ENVELOPE_LAYOUT_VERSION` bump, no new address type.** | plan §5 Non-goals | +| F8 | `I-LEGACY-API-FEATURE-GATED`: a v1 path under the v2 feature must route to the canonical mapping OR be a documented feature-gated no-op. Field-isolation matrix mandatory when layout bits are touched. | `CLAUDE.md` iron rule | +| F9 | A guard needs BOTH a can-it-fire and a can-it-STAY-SILENT test on non-trivial input. | `CLAUDE.md` falsifiability rule | + +## §2 INPUT INVENTORY (measured this session; exact file:line) + +### 2.1 The bits and their accessors + +| item | file:line | shape | +|---|---|---| +| `TRUTH_SHIFT = 59` | `causal-edge/src/layout.rs:65` | 2 bits, `TRUTH_MASK = BITS2_MASK << 59` (`:87`) | +| `SPARE_SHIFT = 61` | `causal-edge/src/layout.rs:77` | 3 bits, `SPARE_MASK = BITS3_MASK << 61` (`:88`) | +| `TrustTexture` (CE64) | `causal-edge/src/layout.rs:141` | **4** variants: `Crystalline=0 / Solid=1 / Fuzzy=2 / Murky=3` | +| `CausalTopology` | `causal-edge/src/layout.rs:239` | **4** variants | +| `ReasoningBand` | `causal-edge/src/layout.rs:353` | **8** variants (fills 3 bits exactly) | +| `trust_texture()` | `edge.rs:927-928` | reads `(self.0 >> TRUTH_SHIFT) & BITS2_MASK` | +| `truth()` raw ordinal | `edge.rs:935-936` | same bits, un-projected | +| `topology()` | `edge.rs:951-953` (v2) / `edge.rs:1144` (v1 arm) | **same bits**, other projection | +| `reasoning_band()` | `edge.rs:978` (v2) / `edge.rs:1154` (v1 arm) | bits 61-63 | +| `with_topology()` | `edge.rs:1008` (v2) / `edge.rs:1166` (v1 no-op) | | +| `with_reasoning_band()` | `edge.rs:1056` (v2) / `edge.rs:1178` (v1 no-op) | the ONLY writer (F2) | + +### 2.2 The v1 provenance trap — measured, and NOT covered by the plan's §3b + +`layout.rs:74-76` states it directly: + +> *"**v1 provenance:** bits 61-63 were temporal bits 9-11, so a v1 edge with +> `temporal >= 512` reads a NON-ZERO band. Apply a version gate on edges of +> unknown provenance — the same rule `truth()` states for bits 59-60."* + +So a reading contract that only answers *"which lens"* is **incomplete**: on an +edge of unknown provenance the bits may be **stale v1 temporal payload**, and +every lens then returns a confidently wrong value. §3b names the lens ambiguity +and is silent on this one. + +### 2.3 Consumers of the BITS: **zero** + +Grep across the workspace, both sibling repos, excluding `causal-edge` itself: +no call site of `reasoning_band()` / `topology()` / `with_topology()` / +`with_reasoning_band()` exists. The contract is being written **before its +first consumer** — prescriptive, not retrofitted. + +`TrustTexture` as a **type** does have consumers (`contract/sensorium.rs:132`, +`contract/mul.rs:320-327`, `contract/benches/i4_batch.rs:71-88`) — **but none +of them reads it out of CE64 bits.** Type-consumed ≠ bit-consumed; the spec +must not conflate the two. + +### 2.4 ⚠ `TrustTexture` is a FOUR-way homonym with THREE arities + +`docs/TYPE_DUPLICATION_MAP.md:9` records *"TrustTexture (×2)"*. **Measured: ×4.** + +| # | file:line | variants | arity | +|---|---|---|---| +| 1 | `causal-edge/src/layout.rs:141` | `Crystalline / Solid / Fuzzy / Murky` | **4** | +| 2 | `lance-graph-contract/src/mul.rs:82` | `Calibrated / Overconfident / Uncertain / Underconfident` | **4** | +| 3 | `lance-graph-planner/src/mul/trust.rs:30` | `Crystalline / Solid / Fuzzy / Murky / **Dissonant**` | **5** | +| 4 | `lance-graph/src/graph/arigraph/orchestrator.rs:114` | `Crystalline / **Fibrous** / Fuzzy` | **3** | + +Two consequences the spec must carry: + +- **#3 cannot fit the bits at all.** 2 bits hold 4 values; the planner enum has + **5**. `Dissonant` is unrepresentable in CE64 — a producer holding a planner + `TrustTexture` and writing bits 59-60 either truncates or is undefined. +- **#1 and #2 share a name AND an arity but have disjoint meanings.** A + consumer resolving "TrustTexture" to the MUL one gets a plausible wrong + answer with no type error — F4's failure mode, one layer deeper than F4 + describes it. + +### 2.5 The `delta_conf` acceptance surface (F6) + +`contract/recipe_kernels.rs:319-322` (the doc on the capability method): +*"no kernel declares `ThoughtField::Confidence` in `writes`, and only **14** +can move `delta_conf` — while 31 are `Operational`."* +`contract/recipe_dispatch.rs:188` — `dispatch_order() -> [u8; 34]`. + +## §3 THE PROPOSED RESOLUTION (fully committed) + +**One new zero-dep contract module: `lance_graph_contract::band_reading`.** +It mints no bit, no tenant, no layout version (F7). It is a *declaration table +plus a resolver*, not a new carrier. + +### 3.1 The three things a reading must declare + +A `(classid, rail)` pair resolves to a `BandReading`: + +```rust +pub struct BandReading { + /// WHICH projection of bits 59-60 this class wrote. + pub truth_lens: TruthLens, // Trust | Topology + /// Whether bits 61-63 carry a ReasoningBand at all. + pub band: BandPresence, // Absent | Present + /// WHICH evidence-kind carrier discriminates (F5) — a reference, not a bit. + pub witness: WitnessKind, // None | Table | CausalFacet | EpisodicBasin +} +``` + +- `TruthLens` resolves F4: the reading is **named**, never assumed. +- `BandPresence::Absent` is the zero-fallback default — an unstamped class + declares no band, and reading one is a refusal (§3.3), not a `Surface(0)`. +- `WitnessKind` carries F5's evidence-kind axis as a *reference discriminator*; + no bit is minted. + +### 3.2 Provenance is part of the contract, not an afterthought (§2.2) + +```rust +pub enum EdgeProvenance { V2Stamped, V1Legacy, Unknown } +``` + +The resolver's entry point takes provenance and **refuses** on +`V1Legacy | Unknown` for bits 61-63, because a v1 `temporal >= 512` aliases a +non-zero band. This is F8 applied to a *read* path: the v1 arm is a documented +refusal, not a plausible value. + +### 3.3 Reads return `Result`, never a plausible fallback + +```rust +pub enum BandReadError { + LensMismatch { declared: TruthLens, requested: TruthLens }, + BandAbsent, + UnknownProvenance, + UndeclaredClass(u32), +} +``` + +A producer/consumer disagreement is an **error**, satisfying D-ACR-7's own +falsifier (*"must FAIL, not return a plausible value"*). No `unwrap_or(Surface)` +anywhere. + +### 3.4 The `TrustTexture` homonym (§2.4) + +**In scope:** the contract's `TruthLens::Trust` documents that it means +`causal_edge::layout::TrustTexture` (4 variants) and **explicitly not** +`contract::mul::TrustTexture`, `planner::mul::trust::TrustTexture` (5 — +unrepresentable) or the arigraph one (3). A `debug_assert`-style const check +pins the arity at 4. + +**Out of scope:** renaming any of the four (that is a cross-crate refactor with +its own blast radius). The spec records the collision and updates +`TYPE_DUPLICATION_MAP.md`'s stale "×2" to "×4" — a doc correction, not a +rename. + +### 3.5 The `delta_conf` acceptance condition (F6) + +A `SamplingPolicy` helper the overlay must route through: +`fn admits(kernel) -> bool` returns `delta_conf`-capability, and the module doc +states why `maturity().is_production()` is the wrong filter (a watcher that +cannot dissent). Enforced by a test, not a comment. + +## §4 NON-GOALS (each with its why) + +| Out of scope | Why | +|---|---| +| Renaming any `TrustTexture` | cross-crate refactor, own blast radius; §3.4 records + documents instead | +| Writing any band anywhere | F2 — only `with_reasoning_band()` writes; this deliverable is the READING contract | +| A registry populated with real classes | D-ACR-2's rail mint is a separate, operator-gated decision the plan does not pre-empt | +| Touching `RungLevel` | F3 — unrelated enum; naming it here would invite the conflation the fence forbids | +| `WideFieldMask` composition | §6 Y2's parked basis collision (D-ACR-0 measured the cardinality mismatch) | +| Any `attention_mask*` reuse | D-ACR-0: rename register file, different contract, EXISTS-UNCALLED | + +## §5 PRE-REGISTERED GATES (decided BEFORE any agent runs) + +| # | Gate | Pass | +|---|---|---| +| G1 | `cargo test -p lance-graph-contract` | ≥ 1194 (current) + new, 0 failed | +| G2 | `cargo clippy -p lance-graph-contract --all-targets` | 0 warnings from this module | +| G3 | Lens-mismatch test | a `Topology`-declared class read as `Trust` returns `Err(LensMismatch)` — **not** a value | +| G4 | Provenance refusal | `V1Legacy` + `Unknown` both `Err(UnknownProvenance)` on bits 61-63 | +| G5 | Can-fire AND can-stay-silent (F9) | a declared class resolves; an undeclared one errors; both on non-trivial input | +| G6 | `delta_conf` filter (F6) | a mute kernel (one of the 20) is REJECTED and a capable one (of the 14) is ADMITTED — both asserted | +| G7 | Arity pin | a const/test asserts the CE64 `TrustTexture` arity is 4, so a 5-variant sibling cannot be silently substituted | +| G8 | No bit written | the module contains no `with_*` call and no `<<`/`&` against CE64 masks (it declares, it does not stamp) | + +## §6 PER-SAVANT QUESTION SETS + +Answer each `CONFIRMS / VIOLATES / GAP / PRIOR-ART-AT / RISK` + `file:line` + +≤2 sentences. ≤10 findings total. **Do not redesign** — a redesign urge is one +`RISK` finding, then stop. + +### Savant 1 — prior art +1. Does a `(classid, rail) → reading` declaration table already exist anywhere (contract, planner, v3 docs)? +2. Is `BandReading`/`TruthLens`/`WitnessKind` (or a synonym) already a shipped type? +3. Does an E-id already record the 59..63 lens ambiguity, or the `TrustTexture` ×4 collision? +4. Is there prior art for a Result-returning reading resolver in this crate? +5. Does `ClassView` already carry a per-class lens selector this should extend instead? + +### Savant 2 — iron rules +1. Does §3 VIOLATE `I-LEGACY-API-FEATURE-GATED` anywhere (esp. the v1 arm treatment)? +2. Does anything in §3 derive a band, violating F2? +3. Does §3 mint a bit / tenant / layout version, violating F7? +4. Does the module stay zero-dep (contract crate constraint)? +5. Do G3–G7 satisfy the falsifiability rule's fire/silent pair on NON-trivial input? +6. Does §3.4's arity pin conflict with any AP1–AP9 anti-pattern? + +### Savant 3 — code truth (CODED / CLAIMED / ABSENT per claim) +1. Are all §2.1 file:line references real and correctly quoted? +2. Is §2.2's v1-provenance quote verbatim from `layout.rs`? +3. Is §2.3's "zero bit-consumers" claim reproducible by grep? +4. Is §2.4's ×4 / arity 3-4-5 table correct, and is `TYPE_DUPLICATION_MAP.md:9` really "×2"? +5. Is §2.5's 14/34 and 31/34 quote verbatim from `recipe_kernels.rs`? +6. Do `with_topology`/`with_reasoning_band` really have v1 no-op arms at the lines cited? + +### Savant 4 — cascade impact +1. Every file/test/doc/board row that MUST change if §3 lands (mandatory vs follow-up)? +2. Does adding a contract module force any downstream crate change? +3. Which board files does hygiene require in the same commit? +4. Does `TYPE_DUPLICATION_MAP.md`'s correction pull in other stale rows? +5. Does this create work for D-ACR-2 (rail mint) that the plan says it must not pre-empt? + +### Savant 5 — different views +1. What is the strongest alternative reading of "the reading contract" that §3 forecloses? +2. Is `Result`-on-mismatch the right severity, or does some call site need a total function? +3. Second-order: what does declaring `BandPresence::Absent` as default cost a future consumer? +4. Is there a reading of F5 under which `WitnessKind` belongs on the witness side, not here? +5. Does the provenance enum belong in `causal-edge` (where the bits live) rather than `contract`? + +--- +--- + +# DRAFT v2 — change ledger over SPEC v1 + +> Phase 2 consolidation, orchestrator only. 5 savants returned 39 findings. +> Raw output banked in the task transcripts; never forwarded to the reviewers. +> **Reviewers see this document ONLY.** + +## L0 — SCOPE CORRECTION (operator input mid-Phase-2, NOT a savant finding) + +**SPEC v1 addressed `CausalEdge64` only. That was a Phase-0 gap, and it is the +largest change in this ledger.** Operator, 2026-08-21: *"causaledge64 is the +muscle memory / causaledgev3 for granularity."* Verified against source: + +| | file:line | shape | +|---|---|---| +| `CausalEdgeV3` | `causal-edge/src/edge_v3.rs:96-103` | `payload: [u8; 12]`, `const _: () = assert!(size_of == 12)`; *"`classid(4) \| payload(12)` = the canonical 16-byte facet, the payload half"* | +| the SAME two fields | `edge_v3.rs:49-50` | `[8] w_slot(6 low) \| truth/topology RAW(2 high)` · `[9] spare/ReasoningBand RAW(3 low) \| reserved(5 high)` | +| their accessors | `edge_v3.rs:199` `truth_raw()`, `:206` `spare_raw()` | RAW ordinals, un-projected | + +**The V3 module doc already states D-ACR-7's problem, and leaves it open** +(`edge_v3.rs:86-90`): + +> *"`w_slot` / truth / spare are preserved as **RAW ORDINALS**. Copying a CE64 +> topology/truth ordinal `01` into V3 means "ordinal 01 preserved" — it is NOT +> an assertion that `IndirectKnown` (or `Solid`) is now source-authoritative for +> that row. **Which lens the ordinal was written through is the producer's +> knowledge, not the conversion's**."* + +That sentence IS the deliverable's justification, written in shipped code by a +prior session. **The reading contract is what supplies the producer knowledge +the conversion cannot carry.** Consequence: + +- **§3 now spans BOTH carriers.** `BandReading` is keyed by `(classid, rail)` + and is carrier-agnostic; the projection function takes the raw ordinals, so + it serves `CausalEdge64::{truth, reasoning_band}` and + `CausalEdgeV3::{truth_raw, spare_raw}` identically. One contract, two + carriers — never two contracts. +- **The v1-provenance trap (§2.2) does NOT apply to V3.** V3 has no v1 history; + its bytes were never temporal. `EdgeProvenance` gains a `V3Register` arm that + is always readable. This is a real asymmetry the spec must state, not smooth. +- **The muscle-memory/granularity split is now recorded**: CE64 reasons + (`syllogize` reads SPO + freq/conf + causal_mask); V3 **rehydrates into CE64** + to reason (`edge_v3.rs:16-26`). The reading contract sits above both and + privileges neither. + +**DEFERRED, with its tension stated rather than silently adopted.** The +operator also raised *"causaledgev3 can even use 6×2×8bit ↑n as BNN planning +equivalent."* This is NOT taken into v2, because the shipped module doc +explicitly forbids that reading today (`edge_v3.rs:29-36`): + +> *"a packed EDGE REGISTER, **NOT** a slot-pure §3 facet … Do not read this as a +> content-blind facet: it is a typed edge register whose carving is its own +> contract."* + +So "V3 as a `6×2×8bit` content-blind facet" contradicts a shipped doc-comment +**and** would be a sixth homonym against `attention_facet`'s `6×2×8bit` reading +(D-ACR-1, landed today). It is recorded as an open question for the operator — +a stacked (`↑n`) BNN-planning ladder over repeated 12-byte registers is a +plausible and interesting direction, but it needs its own deliverable and its +own resolution of the "typed register vs content-blind facet" contradiction. +**Not decided here.** + +## L1 — the Result-vs-total fork (Savant 1 #8 + Savant 5 Q2, independently) + +Two savants converged on the same defect from different lenses, which is the +strongest signal this council produced. + +- Savant 1: *"`Result`-returning refusal is the **opposite** convention from + every existing `ClassView` lens selector (`rail_carving`, + `edge_codec_flavor`, `value_schema`) — all infallible with a documented + zero-fallback default"* (`class_view.rs:1109-1111, 1127-1133`). +- Savant 5: a hot-path SIMD/batch scan needs a **total** function; per-row + `Result` unwrapping is exactly what the infallible accessors exist to avoid. + +**RESOLUTION — the spec conflated two different operations. They split:** + +| operation | shape | why | +|---|---|---| +| **declaration lookup** — *what did class C declare?* | `ClassView::band_reading(class) -> BandReading`, **total**, zero-fallback default | sibling-consistent with `rail_carving` / `edge_codec_flavor`; hot-path safe | +| **projection** — *read THESE raw bits under that declaration* | `BandReading::project(raw, provenance) -> Result` | this is where mismatch and stale-v1 bits actually live; D-ACR-7's falsifier demands a failure, not a plausible value | + +The lookup can never fail (an undeclared class yields the zero-fallback +`BandReading`). The projection can, and must. **This is not a compromise +between the two savants — it is the recognition that they were describing +different functions.** Savant 1's finding is recorded as ACCEPTED-AND-SPLIT; +neither finding is discarded. + +## L2 — writer-side is only half-closed (Savant 5 Q1) + +Accepted. §3 policed readers only, so nothing stops a producer stamping bits +inconsistent with its own declaration — leaving F4's failure mode half open. +**Added:** `BandReading::admits(lens) -> bool`, the cheap pre-write check a +producer calls before `with_topology` / `with_reasoning_band`. It does not (and +cannot) *enforce* — enforcement at the write site is `causal-edge`'s to add and +is recorded as a follow-up, not smuggled in here. + +## L3 — `Absent` vs never-declared (Savant 5 Q3) + +Accepted. An audit that asks *"who opted out vs who never considered this"* was +unrepresentable. **Fixed by shape:** the registry returns +`Option` — `None` = never declared, `Some(BandReading { band: +Absent, .. })` = explicitly declared no band. The total lookup in L1 folds +`None` to the zero-fallback default for callers who do not care; the audit path +reads the `Option` directly. + +## L4 — gate repairs (Savant 2 Q5 ×2) + +- **G3, G4** gained their can-stay-silent halves explicitly (F9 requires BOTH + stated): G3 now also asserts a correctly-matched lens returns `Ok`; G4 now + also asserts `V2Stamped` **and** `V3Register` resolve without error. +- **G7** regime named: it is a **compile-time type-level assertion**, explicitly + exempt from F9's runtime fire/silent duality. Stated rather than left + ambiguous. +- Savant 2 Q6's AP1 watch accepted as a **gate**, not prose: **G9** — no + `#[cfg(feature = ...)]` branch may change `BandReading` semantics under one + name; a feature split must error, never re-mean. + +## L5 — doc corrections (Savant 3, Savant 4 Q4) + +- Savant 3: the `recipe_kernels` quote spans **319-322**, not 320-322. Fixed in + §2.5. All other 5 code-truth questions returned CONFIRMS — **the v1 inventory + is measured, not assumed**, which is the one thing this council most needed to + verify. +- Savant 4 RISK accepted: correcting `TYPE_DUPLICATION_MAP.md:9` "×2"→"×4" also + requires fixing **line 16** (its per-copy table lists 2 of 4) and **line 19** + (*"rename one … in causal-edge"* is no longer a coherent single target across + arities 4/4/5/3). All three lines change together or the doc stays internally + inconsistent. + +## L6 — accepted-as-stated (no change needed) + +- Savant 2 Q1–Q4, Q6: no iron-rule violation. Zero-dep confirmed honest — + `TruthLens::Trust` is a **doc-comment pointer**, not an import + (`lance-graph-contract/Cargo.toml:10-17`). +- Savant 4 Q2: additive-only; no downstream crate is forced to change. +- Savant 4 Q5: D-ACR-2 is not pre-empted — `BandReading` is keyed by + caller-supplied `(classid, rail)`, so no class-population work is created. +- Savant 5 Q5: `EdgeProvenance` stays in `contract`; moving it to `causal-edge` + would force a dependency in one direction or the other. Cost recorded: it + lives one crate from the bits it describes. + +## L7 — recorded, deliberately NOT actioned (anti-collapse) + +- **Savant 1 Q5 / #7** — *should `band_reading` be a third method on `ClassView` + rather than a new module?* L1 adopts the `ClassView::band_reading` **method** + for the lookup half, so this is partially taken. The **types** stay in their + own module because `class_view.rs` is already 2000+ lines and the projection + half has no `ClassView` analogue. The losing half of the finding is recorded, + not deleted. +- **Savant 5 Q4** — *does `WitnessKind` belong on `Locus`/`WitnessEntry`?* + (`causal_witness.rs:116-134` has `Locus::BasinAnchor = 8`.) Fork named and + NOT taken: `WitnessKind` here is the *reference discriminator*, not the + witness. If a later deliverable moves it, this line is the record that the + alternative was seen and declined with a reason. + +## §5′ — GATES, amended (supersedes §5) + +G1, G2, G5, G6, G8 unchanged. G3, G4, G7 amended per L4. New: G9, G10. + +| # | Gate | Pass | +|---|---|---| +| G3′ | Lens mismatch **and** match | `Topology`-declared read as `Trust` → `Err(LensMismatch)`; read as `Topology` → `Ok` | +| G4′ | Provenance, both directions | `V1Legacy`/`Unknown` → `Err(UnknownProvenance)`; `V2Stamped` **and** `V3Register` → `Ok` | +| G7′ | Arity pin (compile-time, F9-exempt — stated) | a const assertion pins CE64 `TrustTexture` arity at 4 | +| G9 | AP1 watch | no `#[cfg(feature)]` branch re-means `BandReading`; a split errors | +| G10 | **Carrier parity (L0)** | the same declaration projecting `CausalEdge64::truth()` and `CausalEdgeV3::truth_raw()` yields the identical result for the identical ordinal | + +--- +--- + +# v3 — PHASE 4 FIX LEDGER + +> Reviewer verdicts in hand: **reviewer 2** (dilution-collapse) 8×PASS + +> 1×FIX(P2); **reviewer 3** (firewall) 6×PASS + **1×BLOCK(P0)** + 1×FIX(P1). +> **Reviewer 1 (overclaim) has not reported** — the first cast was lost with +> its sibling (no notification, `ListAgents` empty) and was recast. +> **v3 is NOT ratified until reviewer 1's verdict set is applied.** + +## FIX-1 — L5 `BLOCK(P0)` (firewall): board hygiene was never committed to + +**The BLOCK is correct, and it is a Phase-2 consolidation failure, not a spec +defect.** Verified: draft v2 contains **zero** occurrences of `LATEST_STATE`, +`STATUS_BOARD`, `EPIPHANIES`, `PR_ARC_INVENTORY` or `AGENT_LOG`. Savant 4 +**answered** this in its Q3 with the full mandatory list; **my consolidation +dropped the finding entirely.** That is precisely the loss Phase 2 exists to +prevent, and it happened in Phase 2. Recorded as such rather than repaired +silently. + +**Resolved by restoring the dropped answer as a committed section:** + +### §6′ — BOARD HYGIENE COMMITMENT (same commit, non-negotiable) + +| file | why it is required | content | +|---|---|---| +| `.claude/board/LATEST_STATE.md` | rule row *"a contract type / module"* | PREPEND a Contract Inventory entry for `band_reading` (types, what it mints: nothing) | +| `.claude/board/STATUS_BOARD.md` | rule row *"a new D-id / deliverable"* | flip the `D-ACR-7` row to Shipped with its falsifier outcome | +| `.claude/board/EPIPHANIES.md` | rule row *"a finding / correction"* — **three** qualify | PREPEND one entry covering: the `TrustTexture` ×4 homonym (arities 4/4/5/3, `Dissonant` unrepresentable in 2 bits), the v1-provenance trap and its **non**-application to V3, and the Result-vs-total split | +| `.claude/board/PR_ARC_INVENTORY.md` | rule row *"a merged PR"* | PREPEND on merge (post-merge commit, not at author time) | +| `docs/TYPE_DUPLICATION_MAP.md` | §3.4's own commitment | lines 9, 16 **and** 19 together (L5's cascade finding) | +| `.claude/board/AGENT_LOG.md` | rule row *"a completed agent run"* | ONE entry naming this council: which 5, which 3, verdict counts, v1→v2→v3 deltas — written by the orchestrator only (one-writer) | + +## FIX-2 — §5′ G10 `FIX(P1)` (firewall): the gate was unhostable, and its own fix is unhostable too + +The warden is right that G10 was not mechanically checkable, and its suggested +remedy — *"the test lives in `causal-edge`, importing the contract"* — **also +fails**, measured: `crates/causal-edge/Cargo.toml:20-23` reads *"No +dependencies — this crate is self-contained. `TrustTexture` is defined locally +(not imported from `lance-graph-contract`) to preserve the zero-dep +invariant."* **Both** crates are zero-dep by explicit design, so no single test +may hold both the contract and the carriers. + +**Resolved by splitting the gate along the seam that already exists — the +projection takes RAW ORDINALS, so it never needed a carrier at all:** + +| gate | host | asserts | zero-dep impact | +|---|---|---|---| +| **G10a** | `lance-graph-contract` | `project(raw, decl, provenance)` is a pure function of its arguments — identical `(raw, decl, provenance)` ⇒ identical result, for every `raw ∈ 0..4` (truth) and `0..8` (band). Carrier-agnosticism is then **by construction**: the function cannot see a carrier. | none — no carrier type is named | +| **G10b** | `causal-edge` | its OWN two accessors preserve the ordinal across `from_v1` → `rehydrate`: `CausalEdge64::truth()` == `CausalEdgeV3::truth_raw()` and the band likewise. | none — contract is not named | + +**G10b is a genuinely MISSING test today, measured:** `edge_v3.rs`'s test module +has `v3_le_round_trip` (V3's own LE bytes), `mantissa_round_trips_raw_for_all_16_states` +and `inference_type_is_a_lossy_projection_of_the_mantissa` — **none asserts +truth/spare preservation across the lift/rehydrate pair**, despite the module +doc claiming *"every meaningful CE64-v2 field survives the round trip +byte-exact"* including *"the truth/topology 2-bit ordinal, and the spare/band +3-bit ordinal"*. So the FIX(P1) surfaced an unverified doc claim in shipped +code — recorded, and G10b is the test that closes it. + +## FIX-3 — L6 `FIX(P2)` (dilution-collapse): "accepted" must read as verified, not asserted + +Each accepted-as-stated bullet gains the `file:line` rigor L0/L5 use: + +- **Savant 2 Q4 (zero-dep honest)** — `crates/lance-graph-contract/Cargo.toml:10-17`: + `[dependencies]` has **zero entries** plus the explicit comment *"Zero + dependencies by design … MUST stay dependency-free even of optional path + deps"*. `TruthLens::Trust` is a doc-comment pointer; no edge is created. + (Independently re-verified by reviewer 3 this pass.) +- **Savant 4 Q2 (additive-only)** — measured: **zero** glob imports of + `lance_graph_contract::*` exist workspace-wide, so a new module cannot + break any downstream build. Consumers adopt it or ignore it. +- **Savant 4 Q5 (D-ACR-2 not pre-empted)** — + `alpha-channel-rung-overlay-v1.md:1190-1192`: *"D-ACR-2 and everything after + it sit behind an operator mint decision that this plan does not pre-empt."* + `BandReading` is keyed by **caller-supplied** `(classid, rail)`, so it + populates no registry. +- **Savant 5 Q5 (`EdgeProvenance` placement)** — accepted WITH its cost named: + it lives one crate from the bits it describes, and (per FIX-2's measurement) + that is forced by BOTH crates' zero-dep posture, not a preference. + +## FIX-4 — L0 addendum: the two-armed handover (operator, after the first cast) + +`WitnessKind`'s target acquired a shape while the council ran +(`known-unknown-handover-network-v1.md` §9 ⊘⊘): handover is **two-armed by +substrate** — static ontology → an alpha-layer focus entry at the same address; +dynamic substrate → in place, with **Lance versioning** as the residue carrier +(`QueryReference::at(v, rung)`, a projection, zero copies). **No ownership ever +changes.** + +Consequence for THIS spec, and it is small by design: `WitnessKind` remains a +**reference discriminator** and gains one doc sentence naming the two arms. No +type, field or gate changes — the reading contract is upstream of where the arms +diverge. Recorded so a later session does not read the two-armed design as +implying a `BandReading` variant per arm. + +## Outstanding before ratification + +1. **Reviewer 1 (overclaim-auditor)** — verdict set not yet received. +2. Any `BLOCK` it raises returns to Phase 0 rather than being argued away here. +3. Stricter verdict wins on any conflict with reviewers 2/3. + +--- + +## FIX-5 — L0 `BLOCK(P0)` (overclaim): "V3's bytes were never temporal" is FALSE for populated instances + +**The BLOCK is correct and is the council's most consequential finding.** +Verified at source this pass: + +```rust +// edge_v3.rs:117 — no provenance parameter exists +pub fn from_v1(e: CausalEdge64, target: u16) -> Self +// edge_v3.rs:138-139 — a raw bit copy from a caller-supplied edge +p[8] = (e.w_slot() & 0x3F) | ((e.truth_raw() & 0b11) << 6); +p[9] = e.spare() & 0b111; +``` + +The reassuring comment above it (`:135-137`) — *"Under the v1 layout every one +of these accessors is a documented zero stub"* — is a **compile-time feature +condition**, not a runtime provenance guarantee. Under a v2-compiled build, a +CE64 of v1 or unknown provenance (whose bits 61-63 alias `temporal >= 512`) +lifted through `from_v1` carries those stale bits into V3 byte 9, and **the +resulting register is indistinguishable from a clean one.** + +**Had this shipped, the reading contract would have been self-defeating at its +own core claim** — `V3Register` declared "always readable" is exactly the +plausible-wrong-answer §3.3 exists to refuse. It is the difference between a +statement about the type's *shape* (true: V3 has no v1 history) and about its +*populated instances* (false: they inherit whatever they were lifted from). + +**Resolution — three changes, none touching the design:** + +1. **`EdgeProvenance::V3Register` is redefined**: it means *"the caller asserts + this register was minted clean"*, **never** *"V3 registers are clean"*. The + contract **cannot infer** V3 provenance and must not try — the information + was destroyed at the lift, one crate away. +2. **A V3 register of unstated origin is `Unknown`**, and `Unknown` already + refuses (G4′). Zero-fallback applies: absent an assertion, refuse. +3. **Recorded as a real gap in `causal-edge`, filed as follow-up, NOT fixed + here**: `from_v1` drops provenance. Fixing it means a signature change in a + crate this deliverable does not own; the reading contract's job is to stop + *trusting* what was lost, not to un-lose it. + +L0's asymmetry claim is **struck** and replaced: the v1 trap applies to +**both** carriers — on CE64 directly, on V3 **transitively through the lift**. +That is a simpler contract, not a more complex one. + +*(Citation corrected: the module-doc quote spans `edge_v3.rs:83-87`, not +86-90.)* + +## FIX-6 — §5′ `BLOCK(P0)` (overclaim): a gate contradicted the fix, and another was a tautology + +**G5 contradiction — correct, and it is FIX-1's own doing.** L1 adopted a +**total** lookup while §5′ carried G5 (*"an undeclared one errors"*) forward as +*unchanged*. Both cannot hold. Resolved by naming the surface each gate tests: + +| gate | surface | asserts | +|---|---|---| +| **G5a** | `ClassView::band_reading` (total) | an undeclared class yields the **zero-fallback** `BandReading` and does NOT error — sibling-consistent | +| **G5b** | `BandReading::project` (fallible) | projecting under an undeclared class returns `Err(UndeclaredClass)` — the guard that must fire | + +The audit distinction L3 bought (`Option` = never-declared vs `Some(Absent)` = +declared-no-band) is what makes G5a/G5b non-vacuous: they read different +returns of different functions, not the same thing twice. + +**G10 tautology — correct, and it kills my own FIX-2 G10a.** G10a as written +(*"`project` is a pure function of its arguments"*) feeds the same input to the +same function twice and asserts the same output. That is the vacuous-assertion +pattern `CLAUDE.md`'s falsifiability rule names outright. **G10a is deleted.** + +**Only G10b survives, and FIX-5 has now doubly motivated it:** compare +`CausalEdge64::truth()` against `CausalEdgeV3::truth_raw()` on the **same edge +after `from_v1`** — which tests the bit-copy fidelity at `edge_v3.rs:138`, +precisely the site where FIX-5 showed provenance is destroyed. It is hosted in +`causal-edge` (naming no contract type), so both zero-dep postures hold. And it +is a **missing test today**, measured: `edge_v3.rs`'s test module has +`v3_le_round_trip`, `mantissa_round_trips_raw_for_all_16_states` and +`inference_type_is_a_lossy_projection_of_the_mantissa` — none asserts +truth/spare preservation across lift/rehydrate, despite the module doc claiming +it. + +## FIX-7 — L2 `FIX(P2)`: "cannot" is now shown, not asserted + +`admits()` cannot enforce **by construction**, and the evidence is +`crates/causal-edge/Cargo.toml:20-23`: *"No dependencies — this crate is +self-contained. `TrustTexture` is defined locally (not imported from +`lance-graph-contract`) to preserve the zero-dep invariant."* A contract-side +helper structurally cannot hook `causal-edge`'s `with_topology` / +`with_reasoning_band` call sites, because the dependency edge that would let it +does not and must not exist. [G]-grade, now cited. + +## RATIFICATION + +| reviewer | verdicts | resolved by | +|---|---|---| +| 2 — dilution-collapse | 8 PASS, 1 FIX(P2) | FIX-3 | +| 3 — firewall | 6 PASS, **1 BLOCK(P0)**, 1 FIX(P1) | FIX-1 (board hygiene §6′), FIX-2 | +| 1 — overclaim | 4 PASS, **2 BLOCK(P0)**, 2 FIX(P1), 1 FIX(P2) | FIX-5, FIX-6, FIX-5 (L5 citation), FIX-7 | + +**Three BLOCK(P0) raised, three resolved in Phase 4 — none argued away, none +requiring a Phase-0 re-spec** (each changed a claim, a gate, or a commitment; +the design — declaration table + fallible projection, two carriers, one +contract — survived all three unchanged). + +**v3 is RATIFIED.** Implementation may proceed against §3 as amended by +FIX-1..7, with gates G1, G2, G3′, G4′, G5a, G5b, G6, G7′, G8, G9, G10b. + +--- + +## Post-ratification operator addendum (2026-08-21): carrier wiring status + the temporal doctrine + +Operator, after ratification: *"Alle V3-Varianten von CausalEdge, +EpisodicWitness und epistemic witness sind unwired oder planned. Temporal is +implicitly in the epistemic pothole; can be explicit in Rubikon revision, +CausalEdgeV3, and attention v3."* + +**1. The unwired status is now operator-confirmed, not only measured.** §2.3 +measured zero bit-consumers; the operator confirms the generation-level fact: +`CausalEdgeV3` is shipped-parallel-unwired, `EpisodicEdges64` is unmounted +(D-ACR-17 unbuilt), `EpisodicWitness64` never became code, `HoleV3` is blocked +(BoardAggregates = 15). **The reading contract is therefore prescriptive by +construction AND by ruling** — it defines how the first consumer reads, it +retrofits nothing. + +**2. The temporal doctrine — the contract carries NO temporal field, and that +is doctrine, not omission.** Time is **implicit in the epistemic pothole**: a +pothole opens at `first_possible`, closes at its `Revision`, and the span lives +in the Lance version stream (`QueryReference::at(v, rung)` — the dynamic arm of +the two-armed trace). Explicit temporal exists in exactly **three sanctioned +homes**, none of them this contract: + +| home | status | its own rule | +|---|---|---| +| Rubikon revision window | planned (§3f's open interval question, unresolved) | the window is cited, not recalled | +| `CausalEdgeV3` byte `[7]` TE | shipped, unwired | *"an INDEPENDENT signed relative chain offset the producer sets explicitly, never inherited"* (`edge_v3.rs`) | +| attention v3 | planned, not shipped | a future ClassView reading — not `attention_facet` (which is deliberately atemporal) | + +**3. This is the v1 trap's lesson, generalized.** Bits 61-63 went stale +precisely because temporal lived *implicitly in a reclaimable field*. +Implicit-in-versions cannot go stale (versions are append-only); explicit +temporal is allowed only where a producer deliberately sets it. And +`EdgeProvenance` is about **layout epoch, never time** — conflating those two +axes is the category error the episodic-vs-epistemic honesty line already +fences one level up. From fd1828c0d674cacfa05de5ffdd932bd242cb7161 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 15:10:22 +0000 Subject: [PATCH 09/10] =?UTF-8?q?plan(known-unknown-handover):=20=C2=A711?= =?UTF-8?q?=20=E2=80=94=20the=20epistemic=20breakthrough=20as=20the=20meas?= =?UTF-8?q?ured=20composition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The operator's target named: CE64 59..63 -> CEV3 + attention v3 x pothole x kanban x cognitive Maslow, via the supervisor's kanban_actor transparent view as meta-awareness. Every factor measured rather than assumed, with two real findings: 1. "Cognitive Maslow" is NOT a missing layer -- it is the shipped rung vocabulary: recipe_loci.rs states "the rung a recipe fires at IS a level of the operator-ruled Maslow pyramid of cognition", Maslow-monotone climb, elevate on sustained BLOCK, 70 references tree-wide. The composition consumes it as its need-axis; a pothole resisting closure at rung n escalates by the already-ruled climb signal. 2. The census loop IS the mechanical form of "transparent view as meta-awareness": five transparent reads -- phase distribution (PhaseCensus, one &self pass), attention (RowFocusMask), unknowns (pothole spans), epistemic grading (band readings, both carriers), need-level (Maslow rung) -- composed at the same addresses, feeding what gets attention next by existence, never command. No new organ minted. Gate status that makes "breakthrough" current: alpha-plan §3h (MUL over rung layers) was id-less pending three prerequisites -- RowFocusMask (shipped today), the 59..63 reading contract (ratified today), the delta_conf filter (a ratified acceptance condition inside it). All three landed or ratified; §3h moves from long-term to next-in-line after the band_reading implementation. Emergence stays CONJECTURE; §10's four falsifiers unchanged. --- .../known-unknown-handover-network-v1.md | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/.claude/plans/known-unknown-handover-network-v1.md b/.claude/plans/known-unknown-handover-network-v1.md index 6c3ad66a7..f655080f5 100644 --- a/.claude/plans/known-unknown-handover-network-v1.md +++ b/.claude/plans/known-unknown-handover-network-v1.md @@ -402,3 +402,57 @@ a prediction, and the prediction's tests are already pre-registered.** A system that manages its known-unknowns as first-class rows carries its own "does this become what it aspires to?" as the largest of them — §7's discipline, unchanged. + +## §11 — The epistemic breakthrough: the planned composition (operator, 2026-08-21) + +Operator: *"Epistemic breakthrough ist die geplante Ausweitung von CE64 59..63 +auf CEV3 + attention V3 × pothole × kanban × cognitive Maslow via supervisor: +kanban_actor transparent view as meta awareness als self aware."* + +This names the TARGET the individual deliverables converge on — the +cross-product, not a new mechanism. Measured state of every factor: + +| factor | home | state (verified) | +|---|---|---| +| CE64 59..63 reading | `dacr7-band-reading-contract-v1.md` | **RATIFIED** (5+3 council, 3×BLOCK resolved) | +| → extended to `CausalEdgeV3` | same contract, dual-carrier | **RATIFIED** — one contract, two carriers; v1 trap reaches V3 transitively | +| attention v3 | third sanctioned explicit-temporal home | **planned** — the ↑n stack; not `attention_facet` (deliberately atemporal) | +| pothole | span (D-ACR-10) + live horizon (D-ACR-15) | designed; probes pre-registered | +| kanban | `lance-graph-supervisor::kanban_actor` | **SHIPPED** — Heckhausen columns, Libet anchors; Rubicon witness D-ACR-8 queued | +| **cognitive Maslow** | `contract::recipe_loci` | **SHIPPED, already operator-ruled** — *"the rung a recipe fires at IS a level of the operator-ruled Maslow pyramid of cognition"* (`recipe_loci.rs:55-62`); Maslow-monotone climb, elevate on sustained BLOCK, never below `base`; 70 references tree-wide | +| the composition point | `PhaseCensus` | **SHIPPED** — *"the read-only fleet visibility surface … a census is one `&self` pass, not 64k RPCs"* (`kanban_actor.rs:30-31`) | + +**Two consequences of the measurement:** + +**1. "Cognitive Maslow" is not a missing layer — it is the shipped rung +vocabulary.** No pyramid needs building; the composition consumes +`recipe_loci`'s rung levels as its need-axis. What ascends the pyramid is +exactly what the loop produces: a pothole that resists closure at rung n +escalates Maslow-monotone — sustained BLOCK is the climb signal, already ruled. + +**2. "Transparent view as meta-awareness als self-aware" cashes out as the +CENSUS LOOP, and every read in it already exists or is ratified:** the system +reading its own phase distribution (`PhaseCensus`, one `&self` pass), its own +attention (`RowFocusMask`), its own unknowns (potholes with spans), its own +epistemic grading (the band readings, both carriers), its own need-level (the +Maslow rung). **Meta-awareness = these five transparent reads composed at the +same addresses; self-aware = the composition feeding what gets attention next +— by existence, never by command.** No new organ is minted for it; the +supervisor's kanban_actor is where the five reads meet because it is the one +place that already sees the fleet without owning it. + +**The gate status that makes "breakthrough" the right word today rather than +last week:** the alpha plan's §3h (MUL over the rung layers) was deliberately +left without a deliverable id *"because it depends on all three of: +`RowFocusMask` (D-ACR-1), the 59..63 reading contract (D-ACR-7), and the +`delta_conf` filter"*. As of this date: **D-ACR-1 shipped, D-ACR-7 ratified, +and the `delta_conf` filter is a ratified acceptance condition inside +D-ACR-7.** All three prerequisites of the join are landed or ratified — §3h +moves from "long-term" to "next in line after the `band_reading` +implementation", which is the precise, unglamorous form of the breakthrough. + +**Grade discipline, §7 unchanged:** the composition is a plan over shipped and +ratified parts; that it produces meta-awareness in more than the mechanical +census-loop sense is the CONJECTURE whose falsifiers are §10's four gates. +Self-description that discriminates is the claim; D-ACR-8/10/11 + D-KUH-3 +remain the tests. From b045575aa500f6dd6147be180ed7101299b730cd Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Aug 2026 15:27:50 +0000 Subject: [PATCH 10/10] =?UTF-8?q?contract:=20band=5Freading=20=E2=80=94=20?= =?UTF-8?q?the=20D-ACR-7=20reading=20contract=20for=20CE64=20bits=2059..63?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements the RATIFIED spec `.claude/plans/dacr7-band-reading-contract-v1.md` (5+3 council, 3×BLOCK(P0) raised and resolved in Phase 4, none argued away). ONE reading contract, TWO carriers. `CausalEdge64` bits 59-60 (truth) / 61-63 (band) is the muscle memory; `CausalEdgeV3` bytes [8] hi-2 / [9] lo-3 via `truth_raw()`/`spare_raw()` is the granularity, and rehydrates INTO CE64 to reason. The reading never changes stored bytes — it declares how a consumer projects them, resolved per `(classid, rail)` through `ClassView`, exactly like `edge_codec_flavor` and `rail_carving`. Provenance is the gate, and it comes FIRST — before the lens is even asked. Council BLOCK-1: `CausalEdgeV3::from_v1` (edge_v3.rs:117) takes no provenance parameter and raw-copies the tail (:138-139), so the v1 temporal trap — a `temporal >= 512` aliasing a non-zero band — reaches V3 transitively. Therefore `EdgeProvenance::V3Register` is a CALLER ASSERTION ("minted clean"), never an inference; unstated means `Unknown` means refuse. Follow-up filed, not owned here: `from_v1` should carry provenance in its signature. The L1 split (Phase-4 fix): declaration lookup is TOTAL — `reading_or_default` folds an undeclared class to `ZERO_FALLBACK`, sibling-consistent with the two existing ClassView resolvers — while raw-bit projection is FALLIBLE and must FAIL rather than hand back a plausible value. `get()` stays `Option` so "never declared" and "declared Absent" remain distinguishable for audit, and `declare()` returns true on replace so a redeclaration is visible. `sampling_admits` filters on `Tactic::moves_confidence()` — 14 of 34 — never on `maturity().is_production()`, which is 31 of 34 and would silently admit 17 tactics that move nothing. Asserted against the real `all_kernels()`. G10b lives in `causal-edge` because both crates are zero-dep and neither may import the other (contract Cargo.toml:10-17, causal-edge Cargo.toml:20-23). It closes a measured missing test: the module doc claimed the truth/spare round trip is byte-exact and nothing asserted it through the accessors. It fires on nonzero ordinals surviving `from_v1` → `rehydrate`, and stays silent on zero. Mints nothing: no tenant, no bit, no ENVELOPE_LAYOUT_VERSION bump, no cfg feature that re-means a stored bit. Gates: G1 1207 contract tests (+13) · G2 clippy clean · G3′ lens mismatch fails and match resolves · G4′ V1Legacy/Unknown refuse, V2Stamped/V3Register resolve · G5a total fold · G5b UndeclaredClass fires · G6 14 admitted / 20 rejected · G7′ arity pins · G8 no with_* or CE64 mask bit-ops · G9 no cfg · G10b green. Also: TYPE_DUPLICATION_MAP TrustTexture corrected ×2 → ×4 — the entry had two stale line numbers and the arigraph path wrong. Arities are 4/4/5/3, so the old "rename one to disambiguate" recommendation was incoherent; the planner's 5-variant enum cannot round-trip a 2-bit field at all. One `cargo fmt` reflow in edge.rs is the pinned toolchain's own output on a pre-existing line. Board hygiene same-commit: LATEST_STATE inventory, STATUS_BOARD D-ACR-7 → Shipped. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01KCGhDYoQBXs3poaR7sFuqp --- .claude/board/LATEST_STATE.md | 51 ++ .claude/board/STATUS_BOARD.md | 2 +- crates/causal-edge/src/edge.rs | 3 +- crates/causal-edge/src/edge_v3.rs | 44 ++ .../lance-graph-contract/src/band_reading.rs | 633 ++++++++++++++++++ crates/lance-graph-contract/src/class_view.rs | 20 + crates/lance-graph-contract/src/lib.rs | 1 + docs/TYPE_DUPLICATION_MAP.md | 20 +- 8 files changed, 763 insertions(+), 11 deletions(-) create mode 100644 crates/lance-graph-contract/src/band_reading.rs diff --git a/.claude/board/LATEST_STATE.md b/.claude/board/LATEST_STATE.md index 8167caa14..2ebf79b85 100644 --- a/.claude/board/LATEST_STATE.md +++ b/.claude/board/LATEST_STATE.md @@ -1,3 +1,54 @@ +## 2026-08-21 — D-ACR-7 IMPLEMENTED — `contract::band_reading` (the 59..63 reading contract) + +### Current Contract Inventory — 1 new zero-dep module + 1 ClassView provided method + 1 gate test in `causal-edge` + +- **`lance_graph_contract::band_reading`** (new, zero new bytes) — implements + the RATIFIED spec `.claude/plans/dacr7-band-reading-contract-v1.md`. **One + reading contract, TWO carriers**: `CausalEdge64` bits 59-60 (truth) / 61-63 + (band) — the muscle memory — and `CausalEdgeV3` byte [8] hi-2 / byte [9] lo-3 + via `truth_raw()` / `spare_raw()` — the granularity. The reading NEVER changes + stored bytes; it declares how a consumer projects them. + - `TruthLens { Trust, Topology }` — which vocabulary the 2-bit register means + for this `(class, rail)`. **Doc-comment pointers only**, never imports: + both crates are zero-dep and neither may depend on the other + (`lance-graph-contract/Cargo.toml:10-17`, `causal-edge/Cargo.toml:20-23`). + - `BandPresence { Absent, Present }` + `WitnessKind { None, Table, + CausalFacet, EpisodicBasin }` — the F5 evidence-kind discriminator. + - `EdgeProvenance { V2Stamped, V3Register, V1Legacy, Unknown }` with + `trusted()`. **`V3Register` is a CALLER ASSERTION ("minted clean"), never an + inference** — council BLOCK-1: `CausalEdgeV3::from_v1` (`edge_v3.rs:117`) + has no provenance parameter and raw-copies the tail (`:138-139`), so the v1 + temporal trap (`temporal >= 512` aliases a non-zero band) reaches V3 + **transitively**. Unstated ⇒ `Unknown` ⇒ refuse. + - `BandReading { truth_lens, band, witness }` + `ZERO_FALLBACK` + (`Trust`/`Absent`/`None`, `== Default`), `admits`, `admits_band`, + `project_truth`, `project_band`. **Projection order is provenance FIRST, + then lens** — an untrusted edge is refused before its lens is even asked. + - `BandDeclarations` — the registry. **L1 split (the council's Phase-4 fix):** + declaration lookup is TOTAL (`reading_or_default`, zero-fallback, sibling- + consistent with `edge_codec_flavor` / `rail_carving` / `value_schema`), + while raw-bit projection is FALLIBLE (`Result`, `UndeclaredClass`) — it must + FAIL, never return a plausible value. + - **L3 audit distinction preserved:** `get()` returns `Option` — + `None` = never declared vs `Some(Absent)` = explicit opt-out. + `declare()` returns `true` on replace: a redeclaration is visible, never silent. + - `sampling_admits(&dyn Tactic) -> bool` = `moves_confidence()` — the + **14/34** capability filter, never `maturity().is_production()` (**31/34**). + Asserted against the real `all_kernels()`, not a fixture. +- **`ClassView::band_reading(class, rail)`** — provided method, defaults to + `BandReading::ZERO_FALLBACK`. Same registry-resolution pattern as its two + siblings; selection only, never a stride change. +- **`causal-edge` `g10b_lift_preserves_truth_and_spare_ordinals_under_both_lenses`** + — hosted THERE because both crates refuse each other. Closes a measured + missing test: the module doc claimed the truth/spare round trip is byte-exact + and nothing asserted it through the accessors. Fires (nonzero `Unknown` / + `Transcendent` ordinals survive `from_v1` → `rehydrate`) **and** stays silent + (zero ordinals are not upgraded). +- **Mints nothing**: no new tenant, no bit, no `ENVELOPE_LAYOUT_VERSION` bump, + no `cfg` feature that re-means a stored bit (G9). +- Gates: G1 (1207 contract tests, +13), G2 clippy clean on the new module, + G3′/G4′/G5a/G5b/G6/G7′/G8/G9/G10b all green — pre-registered before any agent ran. + ## 2026-08-21 — #975 MERGED (4a43698) — two measured corrections + a retracted absence - **`DismechTopology` doc + this file corrected**: the label-KNOWN 3,978 are diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index 51432bff3..1b3bf62d3 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -56,7 +56,7 @@ the rest. | D-KUH-4 | Growth = minting rows, never widening fields: add N Holes, assert `ENVELOPE_LAYOUT_VERSION` and every field width unchanged | Queued — cheap, any time after D-KUH-2 | any width change is the failure (the §0 fence as a test) | | D-ACR-0 | Audit `attention_mask.rs`/`attention_mask_actor.rs`: residue carrier, or a name collision? Report only | **Shipped** 2026-08-21 — `.claude/ATTENTION_MASK_AUDIT_2026_08_21.md`. Verdict **EXISTS-UNCALLED** + a name collision: the shipped type is a *rename register file* (`causaledge64-mailbox-rename-soa-v1.md` §4), not a residue carrier | recorded EXISTS-UNCALLED (0 callers workspace-wide + 3 sibling repos); piece E regraded — not a basis for D-ACR-1 | | D-ACR-1 | `RowFocusMask` — the one missing primitive (S3.1b names it; no crate contains it) | **Shipped** 2026-08-21 — `contract::attention_facet`. Basis: **reuse** `FacetCascade` + `CascadeShape::G6D2`, zero new bytes; `FocusAxis` is content-blind (`Axis0..Axis5`), composition is **prefix containment**, never bit-OR; explicit `depth` outside the 12 bytes (`NiblePath` precedent) | can-fire AND can-stay-silent on non-trivial input — **both green**, plus a not-a-blind-OR negative and a 1000-entry container (no `FieldMask` 64 / `WideFieldMask` 256 cap inherited) | -| D-ACR-7 | The 59..63 reading contract (§3b): name per `(classid, rail)` which lens applies and which witness carrier discriminates evidence-kind. Acceptance: tactic sampling filters on `delta_conf` capability (14/34), never on `maturity().is_production()` (31/34) | **Spec RATIFIED** 2026-08-21 via 5+3 council — `.claude/plans/dacr7-band-reading-contract-v1.md`. **3×BLOCK(P0) raised, 3 resolved in Phase 4, none argued away.** Scope grew to BOTH carriers (CE64 + `CausalEdgeV3`); the v1-provenance trap applies to V3 **transitively through `from_v1`**, which drops provenance. Implementation next | G1, G2, G3′, G4′, G5a, G5b, G6, G7′, G8, G9, G10b — pre-registered before any agent ran | +| D-ACR-7 | The 59..63 reading contract (§3b): name per `(classid, rail)` which lens applies and which witness carrier discriminates evidence-kind. Acceptance: tactic sampling filters on `delta_conf` capability (14/34), never on `maturity().is_production()` (31/34) | **Shipped** 2026-08-21 — `contract::band_reading` + `ClassView::band_reading` + the G10b gate test in `causal-edge`. Spec RATIFIED same day via 5+3 council (`.claude/plans/dacr7-band-reading-contract-v1.md`; **3×BLOCK(P0) raised, 3 resolved in Phase 4, none argued away**). Scope covers BOTH carriers (CE64 bits 59-63 + `CausalEdgeV3` bytes [8]/[9]); `EdgeProvenance::V3Register` is a caller ASSERTION because `from_v1` drops provenance — unstated ⇒ `Unknown` ⇒ refuse. L1 split shipped: declaration lookup TOTAL, raw-bit projection FALLIBLE | G1 (1207, +13), G2, G3′, G4′, G5a, G5b, G6 (14 admitted / 20 rejected against the real `all_kernels()`, ≠ the 31 production count), G7′, G8, G9, G10b — all green | | D-ACR-3 | The one-way invariant as a test: no ontology-owned write traces to a patient-tagged read through ANY call path (corrected from write-authorization-only after CodeRabbit found a session-derived value can flow to the ontology owner via a shared parameter/return, then be written as the owner's own act) | Queued — gates on D-ACR-1 | a write whose call graph includes a session-tagged read is the bug, even if the write itself is authored by the ontology owner | | D-ACR-2 | Mint the Rung-ladder rail | Queued — gates on operator mint decision (HTT §8 Q3) | `rail_carving` gains its first non-default consumer | | D-ACR-4 | Second-order row at the same address, separate table | Queued | a rung-2 read reconstructs where rung-1 looked, on a fixture with an independent answer | diff --git a/crates/causal-edge/src/edge.rs b/crates/causal-edge/src/edge.rs index 324bf13a8..4f2cfb924 100644 --- a/crates/causal-edge/src/edge.rs +++ b/crates/causal-edge/src/edge.rs @@ -651,7 +651,8 @@ impl CausalEdge64 { // weight.inference_type() is the v1 fallback below; v2 uses mantissa let resolved_infer = InferenceType::from_mantissa(weight.inference_mantissa()); #[cfg(not(feature = "causal-edge-v2-layout"))] - #[allow(deprecated)] // v1 layout: 3-bit unsigned inference type is the canonical read + #[allow(deprecated)] + // v1 layout: 3-bit unsigned inference type is the canonical read let resolved_infer = weight.inference_type(); let (f_out, c_out) = match resolved_infer { InferenceType::Deduction => { diff --git a/crates/causal-edge/src/edge_v3.rs b/crates/causal-edge/src/edge_v3.rs index a89619afb..ab42c64f9 100644 --- a/crates/causal-edge/src/edge_v3.rs +++ b/crates/causal-edge/src/edge_v3.rs @@ -713,4 +713,48 @@ mod tests { // and TE was NOT inherited from the deprecated v2 temporal composite assert_eq!(bytes[7], 0, "TE must not be lifted from v2 temporal"); } + + /// G10b (D-ACR-7 ratified gate, hosted HERE because both crates are + /// zero-dep): the lift preserves the truth/spare ordinals — the module doc + /// claims the round trip is byte-exact, and this is the test that asserts + /// the truth/spare half of that claim through the ACCESSORS (not just the + /// raw bytes), under BOTH lens vocabularies over the same unchanged bits. + #[cfg(feature = "causal-edge-v2-layout")] + #[test] + fn g10b_lift_preserves_truth_and_spare_ordinals_under_both_lenses() { + use crate::layout::{CausalTopology, ReasoningBand}; + + // Firing half: nonzero ordinals in both registers, set via the + // TOPOLOGY/BAND lens (the readings D-ACR-7 declares per class). + let e = sample_edges()[0] + .with_topology(CausalTopology::Unknown) // ordinal 0b11 + .with_reasoning_band(ReasoningBand::Transcendent); // ordinal 0b111 + let v3 = CausalEdgeV3::from_v1(e, 7); + assert_eq!( + v3.truth_raw(), + e.truth_raw(), + "truth ordinal dropped by lift" + ); + assert_eq!(v3.spare_raw(), e.spare(), "spare ordinal dropped by lift"); + assert_eq!(v3.truth_raw(), 0b11); + assert_eq!(v3.spare_raw(), 0b111); + + // Round trip: rehydrate with the same SPO restores the ordinals so the + // reasoning carrier sees exactly what was stored. + let back = v3.rehydrate(e.s_idx(), e.p_idx(), e.o_idx()); + assert_eq!(back.truth_raw(), e.truth_raw()); + assert_eq!(back.spare(), e.spare()); + assert_eq!(back.topology(), CausalTopology::Unknown); + assert_eq!(back.reasoning_band(), ReasoningBand::Transcendent); + + // Stay-silent half: zero ordinals stay zero — the lift neither invents + // a band nor upgrades provenance (the from_v1 trap D-ACR-7 fences is a + // PROVENANCE gap, not a bit-copy defect; the bits themselves are exact). + let z = sample_edges()[1]; + assert_eq!(z.truth_raw(), 0); + assert_eq!(z.spare(), 0); + let vz = CausalEdgeV3::from_v1(z, 7); + assert_eq!(vz.truth_raw(), 0); + assert_eq!(vz.spare_raw(), 0); + } } diff --git a/crates/lance-graph-contract/src/band_reading.rs b/crates/lance-graph-contract/src/band_reading.rs new file mode 100644 index 000000000..9262e269a --- /dev/null +++ b/crates/lance-graph-contract/src/band_reading.rs @@ -0,0 +1,633 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright The Lance Authors + +//! `band_reading` — the **59..63 reading contract** (`D-ACR-7`, council-ratified +//! spec: `.claude/plans/dacr7-band-reading-contract-v1.md`). +//! +//! Two bits (the truth tail) and three bits (the band tail) of a causal edge +//! carry FOUR shipped readings between them — `TrustTexture` vs +//! `CausalTopology` on the 2-bit field, `ReasoningBand`-present vs spare on +//! the 3-bit field — and **which reading a producer wrote is not recoverable +//! from the bits**. A consumer that reads one lens while the producer wrote +//! the other gets a plausible wrong answer, silently. This module is the +//! producer knowledge, declared per `(classid, rail)`, that the bits +//! themselves cannot carry. +//! +//! # One contract, TWO carriers +//! +//! The same two fields exist on both causal-edge carriers, and the projection +//! here takes **raw ordinals**, so it serves both identically and names +//! neither (this crate is zero-dep; so is the edge crate — measured, both +//! `Cargo.toml`s refuse the other): +//! +//! | carrier | truth field | band field | role | +//! |---|---|---|---| +//! | `CausalEdge64` (bits 59-60 / 61-63) | `truth()` | `reasoning_band()` | muscle memory — it reasons | +//! | `CausalEdgeV3` (byte 8 hi-2 / byte 9 lo-3) | `truth_raw()` | `spare_raw()` | granularity — it rehydrates INTO CE64 to reason | +//! +//! The V3 module doc states the gap this contract closes, verbatim: *"Which +//! lens the ordinal was written through is the producer's knowledge, not the +//! conversion's."* +//! +//! # The provenance doctrine (council BLOCK 1 — read before trusting a V3 register) +//! +//! Under the v1 layout, both fields were `temporal` bits: a v1 edge with +//! `temporal >= 512` reads a NON-ZERO band. And `CausalEdgeV3::from_v1` has +//! **no provenance parameter** — it raw-copies the fields from whatever CE64 +//! it is handed. So the v1 trap reaches V3 **transitively through the lift**, +//! and a tainted register is indistinguishable from a clean one. Hence: +//! +//! - [`EdgeProvenance::V3Register`] means *"the caller asserts this register +//! was minted clean"* — it is an **assertion, never an inference**. The +//! contract cannot recover what the lift destroyed. +//! - A register of unstated origin is [`EdgeProvenance::Unknown`], and +//! `Unknown` **refuses** (zero-fallback: absent an assertion, refuse). +//! +//! # Total lookup, fallible projection (the council's L1 split) +//! +//! Two different operations, deliberately different shapes: +//! +//! - **Declaration lookup is TOTAL** — [`ClassView::band_reading`] +//! (`class_view.rs`) and [`BandDeclarations::reading_or_default`] return the +//! [zero-fallback](BandReading::ZERO_FALLBACK) for an undeclared class, +//! exactly like the sibling selectors `edge_codec_flavor` / `rail_carving` / +//! `value_schema`. Hot-path safe, never an error. *(G5a)* +//! - **Projection is FALLIBLE** — [`BandReading::project_truth`] / +//! [`project_band`](BandReading::project_band) return +//! [`BandReadError`]: a lens mismatch, an absent band, or untrusted +//! provenance must **FAIL, never return a plausible value**. *(G3′/G4′/G5b)* +//! +//! The audit distinction rides on [`BandDeclarations::get`] returning +//! `Option`: `None` = never declared; `Some(band: Absent)` = **explicitly** +//! declared band-free. Folding the two would make "opted out" and "never +//! considered" indistinguishable to a migration audit. +//! +//! # What this module does NOT do (G8) +//! +//! It declares; it never stamps. No `with_*` call, no shift/mask against any +//! edge layout, no feature gate (G9: a `#[cfg]` split re-meaning a reading +//! under one name is the exact v1-accessor anti-pattern +//! `I-LEGACY-API-FEATURE-GATED` catalogues). Temporal carries NO field here — +//! time is implicit in the epistemic pothole (Lance versions); explicit +//! temporal lives only in its three sanctioned homes (Rubikon revision window, +//! `CausalEdgeV3`'s TE byte, a future attention-v3 reading). `EdgeProvenance` +//! is **layout epoch, never time**. + +use crate::class_view::ClassId; +use crate::rail_geometry::RailAxis; +use crate::recipe_kernels::Tactic; + +/// States the 2-bit truth field can carry — the arity pin (G7′, compile-time, +/// F9-exempt). A 5-variant sibling (`planner::mul::trust::TrustTexture`, with +/// `Dissonant`) is **unrepresentable** here and must never be routed through +/// these bits. +pub const TRUTH_STATES: usize = 4; +/// States the 3-bit band field can carry. +pub const BAND_STATES: usize = 8; +const _: () = assert!(TRUTH_STATES == 4 && (TRUTH_STATES - 1) == 0b11); +const _: () = assert!(BAND_STATES == 8 && (BAND_STATES - 1) == 0b111); + +/// WHICH projection of the 2-bit truth field this class's producers wrote. +/// +/// The two shipped readings are ordinal-identical on the wire (`Crystalline` ≡ +/// `Direct` … `Murky` ≡ `Unknown`), which is precisely why the bits cannot +/// disambiguate themselves. The names below are doc-comment pointers to the +/// edge crate's enums, **never imports** (both crates are zero-dep): +/// +/// - [`Trust`](TruthLens::Trust) → `causal_edge::layout::TrustTexture` +/// (4 variants — NOT `contract::mul::TrustTexture`, NOT the planner's +/// 5-variant one, NOT arigraph's 3-variant one; the ×4 homonym is recorded +/// in `docs/TYPE_DUPLICATION_MAP.md`). +/// - [`Topology`](TruthLens::Topology) → `causal_edge::layout::CausalTopology`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub enum TruthLens { + /// Epistemic-trust reading — the canonical default (`layout.rs`: "the + /// canonical epistemic-trust reading"). + #[default] + Trust, + /// Causal-topology reading — the additive factual view of the same bits. + Topology, +} + +/// Whether the 3-bit field carries a `ReasoningBand` for this class at all. +/// +/// `Absent` is the zero-fallback: an unstamped class declares no band, and +/// projecting one is a [refusal](BandReadError::BandAbsent), never a +/// `Surface(0)` lookalike. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub enum BandPresence { + /// No band: the three bits are spare for this class. The default. + #[default] + Absent, + /// The class's producers stamp `ReasoningBand` ordinals via the one + /// sanctioned writer (`with_reasoning_band()` — nothing derives it). + Present, +} + +/// WHICH witness carrier discriminates evidence-KIND for this class — the +/// axis the band deliberately does not carry (frozen decision F5: *the band +/// grades; the witness reference discriminates*). A weak episodic witness and +/// a weak epistemic claim are the same band and different things; the +/// difference lives in what the row POINTS AT, costing zero bits here. +/// +/// Under the two-armed trace (`known-unknown-handover-network-v1.md` §9 ⊘⊘) +/// this reference is also what a next-rung focus entry resolves through: +/// static-substrate attention lands as an alpha-layer entry, dynamic-substrate +/// attention as the row's own Lance-version history — the discriminator names +/// the kind either way. The Hole becomes a target here once `HoleV3` lands +/// (blocked on the `BoardAggregates = 15` mint). +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub enum WitnessKind { + /// No witness reference — grading stands alone. + #[default] + None, + /// A `WitnessTable`/`WitnessLens` entry (the generic register-slab + /// machinery, `witness_table.rs`). + Table, + /// A `CausalWitnessFacet` locus register (`causal_witness.rs`). + CausalFacet, + /// An episodic basin (the AriGraph cold-path lineage; hot-path mount + /// pending `ValueTenant::EpisodicEdges`). + EpisodicBasin, +} + +/// The layout EPOCH a raw ordinal was read under — **never a timestamp** +/// (the temporal doctrine: time is implicit in the pothole; this axis is +/// about which bit-layout wrote the field). +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub enum EdgeProvenance { + /// The edge was stamped under the v2 layout — the fields mean what the + /// v2 accessors say. + V2Stamped, + /// **The caller asserts this V3 register was minted clean** (stamped by a + /// v2-aware producer, or lifted from a `V2Stamped` edge). This is an + /// ASSERTION, never an inference: `CausalEdgeV3::from_v1` drops + /// provenance, so a register's own bytes cannot prove this (council + /// BLOCK 1). + V3Register, + /// A v1-layout edge: both fields alias old `temporal` bits — a + /// `temporal >= 512` reads as a non-zero band. Refused. + V1Legacy, + /// Origin unstated. The zero-fallback, and it REFUSES: absent an + /// assertion, the fields are not readable. The default. + #[default] + Unknown, +} + +impl EdgeProvenance { + /// Are the truth/band fields trustworthy under this provenance? + /// `V1Legacy` and `Unknown` are not — on EITHER field (`layout.rs` applies + /// the version-gate rule to bits 59-60 as well as 61-63). + #[inline] + #[must_use] + pub const fn trusted(self) -> bool { + matches!(self, EdgeProvenance::V2Stamped | EdgeProvenance::V3Register) + } +} + +/// Why a projection refused. Every variant is a FAILURE the contract's own +/// falsifier demands — *"must FAIL, not return a plausible value."* +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum BandReadError { + /// The consumer requested one lens; the class declared the other. The + /// plausible-wrong-answer case, refused. + LensMismatch { + /// What the class's producers actually wrote. + declared: TruthLens, + /// What the consumer asked to read. + requested: TruthLens, + }, + /// The class declared `BandPresence::Absent` — its three bits are spare, + /// and reading a band from spare bits is the `Surface(0)` lookalike this + /// refuses. + BandAbsent, + /// `V1Legacy` or `Unknown` provenance: the fields may be stale v1 + /// `temporal` payload (directly, or transitively through `from_v1`). + UnknownProvenance, + /// No declaration exists for this `(classid, rail)` — the fallible path's + /// guard (G5b). The TOTAL path folds this to the zero-fallback instead; + /// they are different surfaces on purpose. + UndeclaredClass(ClassId), +} + +/// The reading a class's producers committed to, per `(classid, rail)` — +/// three declarations, zero bits (see the module doc). +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub struct BandReading { + /// Which projection of the 2-bit truth field applies. + pub truth_lens: TruthLens, + /// Whether the 3-bit field carries a band at all. + pub band: BandPresence, + /// Which witness carrier discriminates evidence-kind (F5). + pub witness: WitnessKind, +} + +impl BandReading { + /// The zero-fallback reading an undeclared class resolves to on the TOTAL + /// path: the canonical `Trust` lens, **no band** (projecting one refuses), + /// no witness reference. Identical to `Default`, named so call sites read + /// as the ladder rung they are. + pub const ZERO_FALLBACK: Self = Self { + truth_lens: TruthLens::Trust, + band: BandPresence::Absent, + witness: WitnessKind::None, + }; + + /// The producer-side pre-write check (council L2): may a producer stamp + /// the truth field through `requested`? `false` means the write would + /// contradict this declaration. This DECLARES compatibility — it cannot + /// enforce it, by construction: the edge crate is zero-dep and does not + /// see this contract (`causal-edge/Cargo.toml:20-23`), so enforcement at + /// the write site is that crate's follow-up, not a claim here. + #[inline] + #[must_use] + pub fn admits(self, requested: TruthLens) -> bool { + self.truth_lens == requested + } + + /// The producer-side pre-write check for the band field: `false` for an + /// `Absent` class — stamping a band there would re-mean spare bits. + #[inline] + #[must_use] + pub fn admits_band(self) -> bool { + self.band == BandPresence::Present + } + + /// Project the 2-bit truth field: validates provenance, then the lens. + /// Returns the validated raw ordinal (`0..4`) — the CONSUMER (who holds + /// the edge crate) projects it through the declared lens's enum; this + /// crate stays content-blind and imports neither. + /// + /// **Precondition:** `truth_raw` is the 2-bit field value (`< 4`) — + /// `debug_assert`ed, part of the G7′ compile-time/precondition pin + /// (F9-exempt, stated). + /// + /// Check order is doctrine: **provenance before lens** — untrustworthy + /// bits fail before any question about their meaning is entertained. + pub fn project_truth( + self, + requested: TruthLens, + truth_raw: u8, + provenance: EdgeProvenance, + ) -> Result { + debug_assert!( + (truth_raw as usize) < TRUTH_STATES, + "truth_raw must be the 2-bit field value" + ); + if !provenance.trusted() { + return Err(BandReadError::UnknownProvenance); + } + if self.truth_lens != requested { + return Err(BandReadError::LensMismatch { + declared: self.truth_lens, + requested, + }); + } + Ok(truth_raw) + } + + /// Project the 3-bit band field: validates provenance, then presence. + /// Returns the validated raw ordinal (`0..8`) for the consumer to project + /// through `ReasoningBand` — never a `ReasoningBand` here (content-blind). + /// + /// **Precondition:** `band_raw < 8` (`debug_assert`ed, G7′ regime). + pub fn project_band( + self, + band_raw: u8, + provenance: EdgeProvenance, + ) -> Result { + debug_assert!( + (band_raw as usize) < BAND_STATES, + "band_raw must be the 3-bit field value" + ); + if !provenance.trusted() { + return Err(BandReadError::UnknownProvenance); + } + if self.band != BandPresence::Present { + return Err(BandReadError::BandAbsent); + } + Ok(band_raw) + } +} + +/// The declaration table — `(classid, rail) → BandReading`, caller-populated +/// (an OGAR mint / bake decision populates it; this crate never pre-fills a +/// class, so D-ACR-2's operator-gated rail mint is not pre-empted). +/// +/// Both access disciplines live here, on purpose (the L1 split): +/// [`get`](Self::get) is the audit read (`Option` — never-declared vs +/// declared-`Absent` stay distinguishable), [`reading_or_default`](Self::reading_or_default) +/// is the total read (G5a), and the `project_*` pair is the fallible +/// projection under lookup (G5b fires [`BandReadError::UndeclaredClass`]). +#[derive(Debug, Clone, Default)] +pub struct BandDeclarations { + entries: Vec<((ClassId, RailAxis), BandReading)>, +} + +impl BandDeclarations { + /// An empty table — every class undeclared. + #[must_use] + pub const fn new() -> Self { + Self { + entries: Vec::new(), + } + } + + /// Declare (or re-declare) a class's reading. Returns `true` if this + /// replaced an existing declaration — a re-declaration is visible, never + /// silent. + pub fn declare(&mut self, class: ClassId, rail: RailAxis, reading: BandReading) -> bool { + if let Some(slot) = self + .entries + .iter_mut() + .find(|((c, r), _)| *c == class && *r == rail) + { + slot.1 = reading; + true + } else { + self.entries.push(((class, rail), reading)); + false + } + } + + /// The AUDIT read: `None` = never declared; `Some` = declared (possibly + /// `band: Absent` — an explicit opt-out, distinguishable from silence). + #[must_use] + pub fn get(&self, class: ClassId, rail: RailAxis) -> Option { + self.entries + .iter() + .find(|((c, r), _)| *c == class && *r == rail) + .map(|(_, b)| *b) + } + + /// The TOTAL read (G5a): an undeclared class folds to + /// [`BandReading::ZERO_FALLBACK`], never an error — sibling-consistent + /// with `edge_codec_flavor` / `rail_carving`. + #[must_use] + pub fn reading_or_default(&self, class: ClassId, rail: RailAxis) -> BandReading { + self.get(class, rail).unwrap_or(BandReading::ZERO_FALLBACK) + } + + /// The fallible truth projection under lookup: declaration first + /// (G5b — [`BandReadError::UndeclaredClass`] must fire), then delegate to + /// [`BandReading::project_truth`] (provenance, then lens). + pub fn project_truth( + &self, + class: ClassId, + rail: RailAxis, + requested: TruthLens, + truth_raw: u8, + provenance: EdgeProvenance, + ) -> Result { + self.get(class, rail) + .ok_or(BandReadError::UndeclaredClass(class))? + .project_truth(requested, truth_raw, provenance) + } + + /// The fallible band projection under lookup — same discipline. + pub fn project_band( + &self, + class: ClassId, + rail: RailAxis, + band_raw: u8, + provenance: EdgeProvenance, + ) -> Result { + self.get(class, rail) + .ok_or(BandReadError::UndeclaredClass(class))? + .project_band(band_raw, provenance) + } +} + +/// The sampling filter the acceptance condition demands (F6/G6): admit a +/// tactic iff it can move `delta_conf` — **never** filter on +/// `maturity().is_production()`, which admits 31/34 where only 14/34 can +/// dissent. A sampled tactic that cannot move the confidence number is a +/// spent slot returning guaranteed agreement +/// (`E-A-WATCHER-THAT-CANNOT-DISSENT-IS-NOT-A-WATCHER-1`). +#[inline] +#[must_use] +pub fn sampling_admits(tactic: &dyn Tactic) -> bool { + tactic.moves_confidence() +} + +#[cfg(test)] +mod tests { + use super::*; + + fn topology_class() -> BandReading { + BandReading { + truth_lens: TruthLens::Topology, + band: BandPresence::Present, + witness: WitnessKind::CausalFacet, + } + } + + // ── G3′: lens mismatch AND match ───────────────────────────────────── + + #[test] + fn g3_lens_mismatch_fails_and_match_resolves() { + let decl = topology_class(); + // can-fire: Topology-declared, read as Trust → LensMismatch. + assert_eq!( + decl.project_truth(TruthLens::Trust, 2, EdgeProvenance::V2Stamped), + Err(BandReadError::LensMismatch { + declared: TruthLens::Topology, + requested: TruthLens::Trust, + }) + ); + // can-stay-silent: read as Topology → Ok, the ordinal untouched. + assert_eq!( + decl.project_truth(TruthLens::Topology, 2, EdgeProvenance::V2Stamped), + Ok(2) + ); + } + + // ── G4′: provenance, both directions ───────────────────────────────── + + #[test] + fn g4_untrusted_provenance_refuses_and_trusted_resolves() { + let decl = topology_class(); + for bad in [EdgeProvenance::V1Legacy, EdgeProvenance::Unknown] { + assert_eq!( + decl.project_truth(TruthLens::Topology, 1, bad), + Err(BandReadError::UnknownProvenance), + "truth field must refuse under {bad:?}" + ); + assert_eq!( + decl.project_band(5, bad), + Err(BandReadError::UnknownProvenance), + "band field must refuse under {bad:?}" + ); + } + for good in [EdgeProvenance::V2Stamped, EdgeProvenance::V3Register] { + assert_eq!(decl.project_truth(TruthLens::Topology, 1, good), Ok(1)); + assert_eq!(decl.project_band(5, good), Ok(5)); + } + } + + /// Provenance is checked BEFORE the lens: untrustworthy bits fail before + /// any question about their meaning — a mismatched request under bad + /// provenance reports the provenance, not the mismatch. + #[test] + fn provenance_outranks_the_lens_question() { + assert_eq!( + topology_class().project_truth(TruthLens::Trust, 0, EdgeProvenance::Unknown), + Err(BandReadError::UnknownProvenance) + ); + } + + /// The default provenance is `Unknown`, and `Unknown` refuses — the + /// zero-fallback ladder applied to trust: absent an assertion, refuse. + #[test] + fn default_provenance_is_unknown_and_refuses() { + assert_eq!(EdgeProvenance::default(), EdgeProvenance::Unknown); + assert!(!EdgeProvenance::default().trusted()); + } + + // ── G5a / G5b: the total and fallible surfaces are DIFFERENT ───────── + + #[test] + fn g5a_total_lookup_folds_undeclared_to_zero_fallback() { + let table = BandDeclarations::new(); + let r = table.reading_or_default(0x0301, RailAxis::Taxonomy); + assert_eq!(r, BandReading::ZERO_FALLBACK, "no error, the fold"); + // …and the fallback's own band projection still refuses (Absent): + assert_eq!( + r.project_band(3, EdgeProvenance::V2Stamped), + Err(BandReadError::BandAbsent) + ); + } + + #[test] + fn g5b_fallible_projection_fires_on_undeclared() { + let table = BandDeclarations::new(); + assert_eq!( + table.project_truth( + 0x0301, + RailAxis::Taxonomy, + TruthLens::Trust, + 0, + EdgeProvenance::V2Stamped + ), + Err(BandReadError::UndeclaredClass(0x0301)) + ); + // silent half: a declared class projects through the same surface. + let mut t = BandDeclarations::new(); + t.declare(0x0301, RailAxis::Taxonomy, topology_class()); + assert_eq!( + t.project_truth( + 0x0301, + RailAxis::Taxonomy, + TruthLens::Topology, + 3, + EdgeProvenance::V2Stamped + ), + Ok(3) + ); + } + + // ── the audit distinction (L3) ─────────────────────────────────────── + + #[test] + fn never_declared_and_declared_absent_stay_distinguishable() { + let mut t = BandDeclarations::new(); + t.declare( + 0x0302, + RailAxis::Mereology, + BandReading { + band: BandPresence::Absent, + ..BandReading::ZERO_FALLBACK + }, + ); + assert_eq!(t.get(0x0301, RailAxis::Mereology), None, "never declared"); + assert!( + t.get(0x0302, RailAxis::Mereology).is_some(), + "explicit opt-out is a declaration, not silence" + ); + // …while the TOTAL read is identical for both — that is the fold, + // and it is why the audit path must read the Option. + assert_eq!( + t.reading_or_default(0x0301, RailAxis::Mereology), + t.reading_or_default(0x0302, RailAxis::Mereology) + ); + } + + #[test] + fn redeclaration_is_visible_never_silent() { + let mut t = BandDeclarations::new(); + assert!(!t.declare(1, RailAxis::Taxonomy, topology_class())); + assert!(t.declare(1, RailAxis::Taxonomy, BandReading::ZERO_FALLBACK)); + assert_eq!( + t.get(1, RailAxis::Taxonomy), + Some(BandReading::ZERO_FALLBACK) + ); + } + + // ── BandAbsent: fire AND stay-silent ───────────────────────────────── + + #[test] + fn band_absent_refuses_and_present_resolves() { + let absent = BandReading::ZERO_FALLBACK; + assert_eq!( + absent.project_band(0, EdgeProvenance::V2Stamped), + Err(BandReadError::BandAbsent), + "even ordinal 0 must refuse — a refusal is not Surface(0)" + ); + assert_eq!( + topology_class().project_band(7, EdgeProvenance::V2Stamped), + Ok(7) + ); + } + + // ── the producer pre-write check (L2) ──────────────────────────────── + + #[test] + fn admits_declares_write_compatibility_both_ways() { + let decl = topology_class(); + assert!(decl.admits(TruthLens::Topology)); + assert!(!decl.admits(TruthLens::Trust)); + assert!(decl.admits_band()); + assert!(!BandReading::ZERO_FALLBACK.admits_band()); + } + + // ── G6: the sampling filter, against the REAL kernel registry ──────── + + #[test] + fn g6_sampling_admits_the_14_and_rejects_the_20_mutes() { + let kernels = crate::recipe_kernels::all_kernels(); + let admitted = kernels.iter().filter(|k| sampling_admits(**k)).count(); + assert_eq!( + admitted, 14, + "the measured delta_conf-capable count — a drift here means a \ + kernel changed capability and this contract's premise moved" + ); + assert_eq!(kernels.len() - admitted, 20, "the mutes are rejected"); + // And the filter is NOT the maturity filter: production count differs. + let production = kernels + .iter() + .filter(|k| k.maturity().is_production()) + .count(); + assert_ne!( + admitted, production, + "delta_conf capability and maturity must remain different questions" + ); + } + + // ── G7′: the arity pin (compile-time regime, F9-exempt — stated) ───── + + #[test] + fn g7_arity_pins_hold() { + assert_eq!(TRUTH_STATES, 4, "2 bits: a 5th state is unrepresentable"); + assert_eq!(BAND_STATES, 8); + } + + // ── the zero-fallback is what it says ──────────────────────────────── + + #[test] + fn zero_fallback_is_trust_absent_none_and_is_the_default() { + assert_eq!(BandReading::ZERO_FALLBACK, BandReading::default()); + assert_eq!(BandReading::ZERO_FALLBACK.truth_lens, TruthLens::Trust); + assert_eq!(BandReading::ZERO_FALLBACK.band, BandPresence::Absent); + assert_eq!(BandReading::ZERO_FALLBACK.witness, WitnessKind::None); + } +} diff --git a/crates/lance-graph-contract/src/class_view.rs b/crates/lance-graph-contract/src/class_view.rs index dfb889c5b..b6589dfc2 100644 --- a/crates/lance-graph-contract/src/class_view.rs +++ b/crates/lance-graph-contract/src/class_view.rs @@ -1132,6 +1132,26 @@ pub trait ClassView { crate::rail_geometry::RailCarving::zero_fallback(axis) } + /// How this class's edge truth/spare bits are READ, per rail axis — the + /// D-ACR-7 band-reading contract (`.claude/plans/dacr7-band-reading-contract-v1.md`, + /// RATIFIED). Same registry-resolution pattern as + /// [`edge_codec_flavor`](ClassView::edge_codec_flavor) / + /// [`rail_carving`](ClassView::rail_carving): the default is the canon + /// zero-fallback ([`BandReading::ZERO_FALLBACK`](crate::band_reading::BandReading::ZERO_FALLBACK) + /// — `Trust` lens, band `Absent`, witness `None`), and a class that carries + /// topology-lensed truth or a live reasoning band overrides per + /// `(class, rail)`. Selection only: the reading never changes stored bytes + /// (bits 59-63 on CausalEdge64; bytes [8]/[9] hi-2/lo-3 on CausalEdgeV3) — + /// it declares how a consumer projects them. + #[inline] + fn band_reading( + &self, + _class: ClassId, + _rail: crate::rail_geometry::RailAxis, + ) -> crate::band_reading::BandReading { + crate::band_reading::BandReading::ZERO_FALLBACK + } + /// Which value-slab schema preset this class materialises in /// [`NodeRow::value`](crate::canonical_node::NodeRow::value). /// diff --git a/crates/lance-graph-contract/src/lib.rs b/crates/lance-graph-contract/src/lib.rs index 37870aeed..ff85f4cd1 100644 --- a/crates/lance-graph-contract/src/lib.rs +++ b/crates/lance-graph-contract/src/lib.rs @@ -52,6 +52,7 @@ pub mod atoms; pub mod attention_facet; pub mod auth; pub mod awareness_facet; +pub mod band_reading; pub mod callcenter; pub mod cam; pub mod canonical_node; diff --git a/docs/TYPE_DUPLICATION_MAP.md b/docs/TYPE_DUPLICATION_MAP.md index 0372dbf87..e714453f6 100644 --- a/docs/TYPE_DUPLICATION_MAP.md +++ b/docs/TYPE_DUPLICATION_MAP.md @@ -6,18 +6,20 @@ New duplications discovered/predicted from the Wave F fleet (W-F4 through W-F8). -### TrustTexture (×2) — different semantic axes `TECH_DEBT` +### TrustTexture (×4) — different semantic axes, different arities `TECH_DEBT` -Two enums with the same name encoding **different cognitive dimensions**: +Four enums with the same name, none importing any other, encoding **different cognitive dimensions with different variant counts**: -| # | Location | Line | Variants | Semantic axis | -|---|----------|------|----------|---------------| -| 1 | `crates/lance-graph-contract/src/mul.rs` | 74 | Calibrated / Overconfident / Underconfident / Uncertain | MUL meta-uncertainty layer (felt vs demonstrated competence) | -| 2 | `crates/causal-edge/src/layout.rs` | 114 | Crystalline / Solid / Fuzzy / Murky | Pearl-3 epistemic lens (v2 from PR #383); discriminator = 2-bit field in CausalEdge64 | +| # | Location | Line | Variants | Arity | Semantic axis | +|---|----------|------|----------|-------|---------------| +| 1 | `crates/causal-edge/src/layout.rs` | 141 | Crystalline / Solid / Fuzzy / Murky | 4 | Pearl-3 epistemic lens; discriminator = 2-bit truth field in CausalEdge64 (bits 59-60) | +| 2 | `crates/lance-graph-contract/src/mul.rs` | 82 | Calibrated / Overconfident / Uncertain / Underconfident | 4 | MUL meta-uncertainty layer (felt vs demonstrated competence) | +| 3 | `crates/lance-graph-planner/src/mul/trust.rs` | 30 | Crystalline / Solid / Fuzzy / Murky / Dissonant | 5 | MUL trust-quality axis; **5 variants are unrepresentable in the 2-bit CausalEdge64 truth field** — this one can never be the carrier-side type | +| 4 | `crates/lance-graph/src/graph/arigraph/orchestrator.rs` | 114 | Crystalline / Fibrous / Fuzzy | 3 | AriGraph topology-weight reliability lens | -**What differs**: Completely different variant sets encoding different ontologies. `mul.rs` is a 4-way MUL assessment axis. `layout.rs` is a 4-state Pearl-3 trust signal packed into 2 bits of the CausalEdge64 layout. -**Canonical**: NONE — both are domain-correct and should keep distinct names. Recommended path: rename one (e.g. `PearlTrustTexture` in `causal-edge`) to disambiguate. -**TECH_DEBT**: Name collision across zero-dep crates is a footgun; sprint-13 rename target. +**What differs**: Four distinct ontologies, not one duplicated three times over. The variant sets overlap in name (`Crystalline`, `Fuzzy` recur) but not in count or meaning: #1 and #2 are both 4-way but encode unrelated axes (epistemic-lens vs meta-uncertainty); #3 is the 5-way superset of #1's axis and cannot be packed into the 2-bit carrier #1 was built for; #4 is a 3-way AriGraph-local lens. +**Canonical**: NONE — all four are domain-correct in place. A single rename cannot disambiguate four homonyms, and the differing arities (4/4/5/3) rule out unification: #3's 5 variants would silently truncate if forced into #1's 2-bit field, so these are candidates for **per-site renaming only**, never for merging into one shared type. `crates/lance-graph-contract/Cargo.toml` (lines 10-17) is zero-dependency by design, and `crates/causal-edge/Cargo.toml` (lines 20-23) states explicitly that `TrustTexture` is defined locally rather than imported from `lance-graph-contract` to preserve that zero-dep invariant — neither crate may depend on the other, so this duplication is a deliberate architectural posture, not an oversight to be paid down by a shared type. +**TECH_DEBT**: Name collision across zero-dep crates is a footgun; sprint-13 target is per-site renaming (e.g. `PearlTrustTexture`, `MulTrustTexture`, `PlannerTrustQuality`, `AriGraphTrustTexture`), not unification. ---