Skip to content

CE64: additive CausalTopology + ReasoningBand lenses over bits 59..63 — no wire change - #970

Merged
AdaWorldAPI merged 3 commits into
mainfrom
claude/ce64-topology-and-texture
Aug 20, 2026
Merged

CE64: additive CausalTopology + ReasoningBand lenses over bits 59..63 — no wire change#970
AdaWorldAPI merged 3 commits into
mainfrom
claude/ce64-topology-and-texture

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

Non-breaking by construction, and proven rather than asserted

Two fields that already exist gain a second way to be read. Nothing moves, nothing is removed, no layout version, no CE64 v3. The u64 wire is byte-identical before and after.

Mechanical compatibility ledger, run against main:

check result
public items removed or resignatured NONE
bit shift/mask constants changed NONE — every one identical
non-doc code deletions NONE (one test-import consolidation)
TrustTexture API diff vs main EMPTY
production consumers of spare()/truth() outside this crate NONE FOUND across 8 repos on disk

truth(), truth_raw(), with_truth(), set_truth(), with_routing(), spare(), with_spare(), set_spare(), pack, pack_v2, CausalEdge64::ZERO, every feature flag — all untouched.

What is added

Bits 59..60 (existing TRUTH_SHIFT, existing TrustTexture) gain a factual sibling view:

CausalTopology { Direct=0b00, IndirectKnownIntermediates=0b01,
                 IndirectUnknownIntermediates=0b10, Unknown=0b11 }

The ordinal identity with TrustTexture is the migration contract, not coincidence: Crystalline==Direct==0, Solid==IndirectKnown==1, Fuzzy==IndirectUnknown==2, Murky==Unknown==3. A consumer that knows only truth() keeps seeing the identical ordinal and identical behaviour; a new consumer may read the same two bits as topology.

Bits 61..63 (existing SPARE_SHIFT) gain a 3-bit cognitive-level view, ReasoningBand — Surface / Association / Relation / Causal / Counterfactual / Perspective / Meta / Transcendent. spare() still sees the same three bits, unchanged.

API shape follows the crate's existing consuming-builder idiom rather than introducing mutable setters into a register-style value type:

let edge = edge
    .with_topology(CausalTopology::IndirectUnknownIntermediates)
    .with_reasoning_band(ReasoningBand::Causal);

Where "non-breaking" has a boundary — stated, not buried

Reading is non-breaking. Writing a band is not, for one specific consumer.

The deprecated temporal() reads bits 52..63 — a window that contains these bits — decomposing under v2 as plast | (w_slot << 1) | (truth << 7) | (band << 9). Network::evidence_trail sorts by temporal(), and its comment claims that sort "degrades to a stable no-op" under v2. That holds only while 52..63 are all zero. It is already false for any edge carrying a w_slot or a truth, and a written band becomes the dominant term.

Nothing in-tree writes a band today, so this PR is inert. It is documented on with_reasoning_band as a trap for the first producer rather than left to be discovered by one.

Naming: ReasoningBand, not TextureBand

Settled by a vocabulary audit, not preference. "Texture" is the most collided word in this stack: four distinct TrustTexture enums exist — this crate's, the contract's mul::TrustTexture (a different ontology), the planner's five-variant one (whose Dissonant cannot fit a 2-bit field), and AriGraph's three-variant one. That duplication is booked debt whose recorded remedy is a rename. An operator ruling holds "Texture = binding topology, not polarity" — Texture is deliberately not an ordinal. And TrustTexture at 59..60 sitting directly above TextureBand at 61..63 reads as one 5-bit widening of it; they are unrelated fields.

Deliberately not any *Rung* name: RungLevel shares four variant names with this band at different ordinals (Surface 0==0, but Counterfactual 4 vs 6, Meta 6 vs 7, Transcendent 7 vs 9). A *Rung* name would assert a correspondence that is ordinally false in three of four cases. The doc states the disagreement explicitly.

TrustTexture keeps its name. The wider duplication is owned by an existing debt item; doing half of it here would fragment that work.

Historical honesty

wire compatibility:            exact
ordinal compatibility:         exact
legacy behavioural projection: intentional
historical factual provenance: NOT guaranteed for old rows

Old rows had bits 59..60 stamped with truth-texture semantics. Source-authoritative topology begins only when a later producer explicitly writes CausalTopology. No old row is inferred or repaired. CausalEdge64::ZERO reads Direct exactly as it reads Crystalline — intentional staged-migration compatibility, documented rather than "fixed", and no sentinel invented.

Three pre-existing doc defects corrected in place

  1. layout.rs claimed the contract's mul::TrustTexture is "the canonical contract type" and "byte-compatible by construction". Both false — different ontology, no From impl in either direction, and docs/TYPE_DUPLICATION_MAP.md rules the opposite: "Canonical: NONE". Marked as a correction so nobody builds a cast on the old sentence.
  2. spare() claimed it returns 0 for all v1 edges. False — bits 61..63 are v1 temporal bits 9..11, so temporal >= 512 reads non-zero. Now states the real threshold, matching what truth() already does correctly for its own bits.
  3. CausalTopology means causal-path shape (how many intermediates), not the "binding topology over loci" sense the Texture ruling attaches to that word. One line says so.

No auto-derivation

Nothing derives the band from CausalMask, InferenceType, NARS confidence, MUL, ReasoningGap, or ThinkingStyle. That guarantee is load-bearing: it is the only thing that makes the orthogonality claims testable. Two tests pin ReasoningBand::Counterfactual ⇎ mantissa -6 in both directions.

Verified

  • cargo test: 66 passed, 0 failed (default v2 layout).
  • cargo test --no-default-features: 35 passed, 0 failed (v1 stub path — every new accessor has its zero/default stub).
  • clippy: 7 lib / 8 lib-test errors — measured identical on main with this branch stashed. This change introduces none; they are unrelated and not fixed here.
  • fmt: applied to new code only. main is already fmt-dirty in edge.rs:651; that hunk was deliberately kept out of the diff.

Three disable runs, each red-then-green:

disable result
with_topology writes SPARE bits instead of TRUTH 4 arms red
topology() reads SPARE bits instead of 59..60 5 arms red
CausalTopology ordinals 1 and 2 swapped 5 arms red

The isolation tests XOR the whole word against the field mask, so "changed only these bits" is proven across all 64 bits at once rather than field by field.

Deferred

No consumer migrated to the new APIs. No OGAR-loco wiring, no recipe semantics, no DisMech, no HHTL, no MUL/Rubicon change. Board hygiene for this change is owed and tracked separately.

claude added 3 commits August 20, 2026 10:34
… no wire change

Strictly mechanical, strictly additive. No bit moves, no layout version, no CE64
v3. The u64 wire is byte-identical before and after; this PR only adds a second
way to READ two fields that already exist.

## What was added

Bits 59..60 (existing `TRUTH_SHIFT`, existing `TrustTexture`) gain a factual
sibling view:

    CausalTopology { Direct=0b00, IndirectKnownIntermediates=0b01,
                     IndirectUnknownIntermediates=0b10, Unknown=0b11 }

The ordinal identity with `TrustTexture` is the MIGRATION CONTRACT, not
coincidence: Crystalline==Direct==0, Solid==IndirectKnown==1,
Fuzzy==IndirectUnknown==2, Murky==Unknown==3. A consumer that knows only
`truth()` keeps seeing the exact same ordinal and the exact same behaviour; a
new consumer may read the SAME two bits as topology instead.

Bits 61..63 (existing `SPARE_SHIFT`) gain a 3-bit cognitive-level view,
provisionally `TextureBand` — Surface / Association / Relation / Causal /
Counterfactual / Perspective / Meta / Transcendent. `spare()` and
`with_spare()` are untouched and still see the same 3 bits.

**The type name carries a `// TODO(name)` marker.** A vocabulary audit across
the workspace is still open; the final name is deliberately not settled by
whoever typed first, because a public type name in a zero-dep crate that four
repos consume is the one irreversible choice in this change.

## Orthogonality, stated in the docs because the words collide

`TextureBand::Causal` is NOT `CausalMask` (bits 40..42, which says WHICH
Pearl/SPO projection is represented). `TextureBand::Counterfactual` is NOT the
signed inference mantissa's -6 (a specific NARS operation being
counterfactual). `TextureBand::Perspective` is NOT `direction` (bits 43..45).
Two tests pin the counterfactual pair in BOTH directions so the claim is
falsifiable rather than merely asserted.

`Transcendent` is an ordinal and nothing more — no mystical behaviour, by
construction and by doc comment.

## Historical honesty

    wire compatibility:            exact
    ordinal compatibility:         exact
    legacy behavioural projection: intentional
    historical factual provenance: NOT guaranteed for old rows

Old rows had bits 59..60 written with truth-texture semantics. Source-
authoritative topology begins only when a later producer explicitly writes
`CausalTopology`. No old row is inferred or repaired here.

`CausalEdge64::ZERO` therefore reads `Direct` under the new view exactly as it
reads `Crystalline` under the old. That is intentional staged-migration
compatibility, not a sentinel, and it is documented as such rather than
"fixed".

## No auto-derivation

Nothing derives `TextureBand` from CausalMask, InferenceType, confidence,
topology, MUL, ReasoningGap or ThinkingStyle. Raw encode/decode, accessors,
consuming builders, tests, docs. The orchestration layer decides when the
field changes; this layer only stores it.

## API shape

Consuming builders, matching the crate's existing `with_truth`/`with_spare`
idiom rather than introducing mutable setters into a register-style value
type:

    edge.topology() / edge.with_topology(..)
    edge.texture_band() / edge.with_texture_band(..)

so the usage stays compositional. The `#[cfg(not(feature =
"causal-edge-v2-layout"))]` stub twins mirror the existing v1 stub convention
exactly.

## Verified

- `cargo test` (default v2 layout): 66 passed, 0 failed.
- `cargo test --no-default-features` (v1 stub path): 35 passed, 0 failed.
- clippy: 7 lib / 8 lib-test errors — MEASURED IDENTICAL on main with this
  branch stashed, so this change introduces none. Not fixed here; unrelated.
- fmt: applied to the new code. `main` is fmt-dirty already; the one
  pre-existing hunk that `cargo fmt` wanted to move in `edge.rs:651` was
  reverted so it stays out of this diff.

Three disable runs, each red-then-green:

    with_topology writes SPARE bits instead of TRUTH  -> 4 arms red
    topology() reads SPARE bits instead of 59..60     -> 5 arms red
    CausalTopology ordinals 1 and 2 swapped           -> 5 arms red

The isolation tests use an XOR diff of the whole word against the field mask,
so "changed only these bits" is proven for all 64 bits at once rather than
field by field.

## Not done

No consumer was migrated to the new APIs. No OGAR-loco wiring, no recipe
semantics, no DisMech, no HHTL, no MUL/Rubicon change.
…ts the audit found

Follow-up to the additive-lens commit, applying a workspace vocabulary audit.
No bits moved, no wire change, no API removed. `TrustTexture` is untouched.

## The name: ReasoningBand, not TextureBand

The `// TODO(name)` marker is resolved. "Texture" is the most collided word in
this stack's cognitive vocabulary and the collision is already booked debt
whose recorded remedy is a rename:

  1. FOUR distinct `TrustTexture` enums exist, not two — this crate's
     (Crystalline/Solid/Fuzzy/Murky), the contract's `mul::TrustTexture`
     (Calibrated/Overconfident/Uncertain/Underconfident), the planner's
     FIVE-variant `mul::trust::TrustTexture` (adds `Dissonant`, which cannot
     fit a 2-bit field), and AriGraph's THREE-variant orchestrator one.
  2. An operator ruling holds "Texture = binding topology, not polarity" —
     Texture is deliberately NOT an ordinal. Naming a 3-bit ordinal
     `TextureBand` puts a retired instrument's name on the coarse projection
     meant to sit beside it.
  3. Adjacency reads as continuation: `TrustTexture` at 59..60 directly above
     `TextureBand` at 61..63 would be read as one 5-bit widening. They are
     unrelated fields.

`ReasoningBand` is the noun the type's own doc comment already used eight
times ("Surface-level reasoning", "Causal-level reasoning", …). Verified
ABSENT from every repo on disk, as are `ReasoningLevel` and `CognitiveBand`.

Deliberately NOT named with `Rung` or `Level`: `RungLevel` shares FOUR variant
names with this band at DIFFERENT ordinals (Surface 0==0, but Counterfactual
4 vs 6, Meta 6 vs 7, Transcendent 7 vs 9). A `*Rung*` name would assert a
correspondence that is ordinally false in three of four cases. The doc now
states the disagreement explicitly so nobody maps between them by ordinal.

`TrustTexture` keeps its name — canonical, and unchanged here by design. The
wider duplication is owned by an existing debt item; doing half of it in this
PR would fragment that work.

## Three documentation defects, all pre-existing, all corrected in place

1. `layout.rs` claimed `lance_graph_contract::mul::TrustTexture` is "the
   canonical contract type" and "byte-compatible by construction". BOTH
   halves are false — different ontology, no semantic mapping, and no `From`
   impl exists in either direction. `docs/TYPE_DUPLICATION_MAP.md` rules the
   opposite outright: "Canonical: NONE — both are domain-correct and should
   keep distinct names." Corrected in place, marked as a correction rather
   than silently rewritten, so nobody builds a cast on the old sentence.

2. `spare()` claimed it returns 0 for "all v1-written edges (temporal MSBs
   were <= 0xFFF)". False: bits 61..63 are v1 temporal bits 9..11, so any v1
   edge with `temporal >= 512` reads a NON-ZERO spare. Now states the real
   threshold, matching the pattern `truth()` already uses correctly for its
   own bits at `temporal >= 128`.

3. `with_reasoning_band` now carries the trap that matters most here: the
   deprecated `temporal()` reads bits 52..63, a window CONTAINING these three
   bits, decomposing as `plast | (w_slot << 1) | (truth << 7) | (band << 9)`.
   `Network::evidence_trail` sorts by `temporal()` and its comment claims the
   sort "degrades to a stable no-op" under v2 — true only while 52..63 are
   all zero, already false for any edge carrying a w_slot or truth, and a
   written band becomes the DOMINANT sort term. Nothing in-tree writes a band
   today, so this is a trap for the first producer, not a live defect. Named
   now rather than discovered later.

## Also documented

`CausalTopology` means causal-PATH shape (how many intermediates) — NOT the
"binding topology over loci" sense the Texture ruling attaches to that word.
One line says so, so the two are never conflated.

## Verified

- `cargo test`: 66 passed, 0 failed (default v2 layout).
- `cargo test --no-default-features`: 35 passed, 0 failed (v1 stub path).
- `TrustTexture` API diff vs main: EMPTY. The only lines mentioning its
  variants are the new ordinal-bridge doc comment.
- fmt applied to the touched code only.
cargo fmt wants to move this comment onto its own line, but the hunk is
pre-existing drift on main (which is already fmt-dirty here) and is unrelated
to the additive lenses. Reverted so the diff contains only this change.
@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 75d36a88-9fe9-4c14-ad7b-3ba6386d6553


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Aug 20, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_3d732921-abdb-47f4-94d6-3fd5206ca680)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a3cd4e622

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1008 to +1010
pub fn with_topology(self, topo: crate::layout::CausalTopology) -> Self {
use crate::layout::{BITS2_MASK, TRUTH_MASK, TRUTH_SHIFT};
Self((self.0 & !TRUTH_MASK) | ((topo.to_bits_2() as u64 & BITS2_MASK) << TRUTH_SHIFT))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep topology from overwriting trust texture

When a producer calls with_topology on an edge whose trust was determined independently, this assignment replaces the same TRUTH_MASK bits used by with_truth; consequently Unknown is exposed to legacy truth() readers as Murky (the veto value in layout.rs:113-120), while Direct becomes Crystalline/mastered. Since causal-path shape and epistemic trust are distinct facts, matching their ordinals does not preserve legacy behavior and the edge cannot carry both values; use separate storage or an explicitly versioned migration before exposing this writer.

Useful? React with 👍 / 👎.

@AdaWorldAPI
AdaWorldAPI merged commit 781c3b9 into main Aug 20, 2026
7 checks passed
AdaWorldAPI pushed a commit that referenced this pull request Aug 20, 2026
Stage-3 handoff gate. Conversion correctness only: no Stage-3 semantic
wiring, no predicate algebra, no DisMech, no ThoughtCtx change, no CE64
layout touch, no ENVELOPE_LAYOUT_VERSION bump.

1. Carry the signed inference mantissa RAW.

   `rehydrate` routed the stored 4-bit signed mantissa through
   `InferenceType::from_mantissa(...) -> pack(...) -> to_mantissa()`.
   That enum is a lossy compatibility projection -- 16 mantissa states
   onto 8 variants, so it cannot be injective -- and the round trip
   silently rewrote 8 of the 16:

       -8 -> +1    -3 -> -1
       -7 -> +7    -2 -> -1
       -5 -> +5     0 -> +1
       -4 -> +4    +3 -> +5

   `0 -> +1` is the one that matters most: every `pack_v2` edge defaults
   to mantissa 0, so the neutral/identity state was rewritten to
   Deduction on any lift-and-rehydrate.

   Fixed by restoring the raw nibble with `set_inference_mantissa` after
   `pack`; the `InferenceType` argument is now an explicitly-labelled
   throwaway placeholder. Added `CausalEdgeV3::inference_mantissa()`,
   factoring the sign-extension out of `rehydrate`.

   Why it hid: exactly half the states survive. A projection that failed
   on everything would have been caught by the first test written; one
   that is right half the time looks like a working codec until someone
   sweeps it. And the sweep was structurally impossible where the type
   is actually exercised -- the Stage-2.6 planner harness only ever
   carries `InferenceType::Deduction` (mantissa +1, a surviving state),
   so a correct, green, load-bearing harness was blind to this by
   construction. A parity harness proves two legs agree; it says nothing
   about whether the conversion under them is total.

2. Preserve the rest of the CE64-v2 register.

   Three fields were dropped entirely -- w_slot (6 bits), the
   truth/topology register (2), the spare/ReasoningBand register (3) --
   all of which became meaningful state with #970. They land in the
   dormant reserved bytes:

       [8] = w_slot(6 low) | truth/topology RAW(2 high)
       [9] = spare/ReasoningBand RAW(3 low) | reserved(5 high)
       [10..12] still reserved (pinned zero by test)

   RAW ordinals, deliberately. Copying a CE64 topology/truth ordinal 01
   into V3 means "ordinal 01 preserved", never "IndirectKnown is now
   source-authoritative" -- which lens the producer meant is not
   recoverable from the register.

   The truth carry goes through `TrustTexture::from_bits_2` only because
   `set_truth` has no raw form. That is safe where `InferenceType` was
   not, and the difference is the point: from_bits_2/to_bits_2 is a
   total bijection on 0..=3. Pinned by its own test so an added or
   reordered variant fails there first.

   Also added read accessors mirroring each stored field (frequency,
   confidence, causal_mask, direction, plasticity, w_slot, truth_raw,
   spare_raw). No new setters -- out of scope.

3. Pin full field parity, low-level and separate.

   A new suite in `causal-edge::edge_v3::tests`, distinct from the
   planner Stage-2.6 comparison, which is untouched and still green
   (requirement 4 -- both are needed, neither subsumes the other).

   - exhaustive mantissa round trip over all 16 states
   - the four named regressions (-2, +3, -4, -5) asserted individually,
     each also asserting it no longer lands on the old lossy value
   - the loss set itself measured (`vec![-8,-7,-5,-4,-3,-2,0,3]`) so the
     doc's "8 of 16" is a measurement, not a memory
   - full field parity over 6 varied non-zero edges (all 4 truth
     ordinals, w_slot at both ends of its 6 bits, spare across its 3,
     mantissa on both signs), with fixture anti-vacuity asserts
   - and whole-register equality: under v2 the 64 bits are fully
     partitioned, so field parity IS bit parity -- that single line
     catches a field a future session forgets to enumerate
   - tail isolation: bytes 8/9 carry exactly the preserve, 10..11 stay
     zero, and TE is not lifted from the deprecated v2 temporal

   Not compared: the deprecated v2 temporal. It is not valid CE64-v2
   state (bits 52..63 are the reclaim zone) and is not mapped into V3
   TE, which stays an independent producer-set signed chain offset.

Gates: causal-edge 72/72 (v2 default) and 38/38 (--no-default-features
v1); five disable-runs each verified red-then-green (the old mantissa
path -- 3 tests red -- and each of the w_slot / truth / spare / from_v1
tail carries individually); fmt and clippy -D warnings clean on
edge_v3.rs in both feature states; planner `cache::stage26_v3_parity`
4/4; `cognitive-shader-driver::edge_v3_compare` 3/3.

One method note, recorded because it nearly cost a real finding: one of
the five disable-runs used a malformed sed pattern that matched nothing,
and the resulting green read exactly like "this carry is not
load-bearing". Re-run with an exact-string edit that asserts the anchor
exists before removing it, it went red immediately. A disable that does
not disable is indistinguishable from a guard that does not guard.

Board: EPIPHANIES E-THE-COMPAT-ENUM-WAS-EATING-HALF-THE-REGISTER-1;
LATEST_STATE inventory + conversion contract; TECH_DEBT
TD-CAUSAL-EDGE-IS-EXCLUDED-SO-CI-NEVER-LINTS-IT (the crate is
workspace-excluded, so the workspace clippy gate never sees it; 7
pre-existing errors in edge.rs/tables.rs, untouched here).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants