CE64: additive CausalTopology + ReasoningBand lenses over bits 59..63 — no wire change - #970
Conversation
… 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.
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 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. Comment |
Bugbot couldn't run - usage limit reachedBugbot 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) |
There was a problem hiding this comment.
💡 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".
| 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)) |
There was a problem hiding this comment.
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 👍 / 👎.
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).
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
u64wire is byte-identical before and after.Mechanical compatibility ledger, run against
main:TrustTextureAPI diff vsmainspare()/truth()outside this cratetruth(),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, existingTrustTexture) gain a factual sibling view:The ordinal identity with
TrustTextureis the migration contract, not coincidence:Crystalline==Direct==0,Solid==IndirectKnown==1,Fuzzy==IndirectUnknown==2,Murky==Unknown==3. A consumer that knows onlytruth()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:
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 asplast | (w_slot << 1) | (truth << 7) | (band << 9).Network::evidence_trailsorts bytemporal(), 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 aw_slotor atruth, 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_bandas a trap for the first producer rather than left to be discovered by one.Naming:
ReasoningBand, notTextureBandSettled by a vocabulary audit, not preference. "Texture" is the most collided word in this stack: four distinct
TrustTextureenums exist — this crate's, the contract'smul::TrustTexture(a different ontology), the planner's five-variant one (whoseDissonantcannot 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. AndTrustTextureat 59..60 sitting directly aboveTextureBandat 61..63 reads as one 5-bit widening of it; they are unrelated fields.Deliberately not any
*Rung*name:RungLevelshares 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.TrustTexturekeeps its name. The wider duplication is owned by an existing debt item; doing half of it here would fragment that work.Historical honesty
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::ZEROreadsDirectexactly as it readsCrystalline— intentional staged-migration compatibility, documented rather than "fixed", and no sentinel invented.Three pre-existing doc defects corrected in place
layout.rsclaimed the contract'smul::TrustTextureis "the canonical contract type" and "byte-compatible by construction". Both false — different ontology, noFromimpl in either direction, anddocs/TYPE_DUPLICATION_MAP.mdrules the opposite: "Canonical: NONE". Marked as a correction so nobody builds a cast on the old sentence.spare()claimed it returns 0 for all v1 edges. False — bits 61..63 are v1 temporal bits 9..11, sotemporal >= 512reads non-zero. Now states the real threshold, matching whattruth()already does correctly for its own bits.CausalTopologymeans 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, orThinkingStyle. That guarantee is load-bearing: it is the only thing that makes the orthogonality claims testable. Two tests pinReasoningBand::Counterfactual⇎ mantissa-6in 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).mainwith this branch stashed. This change introduces none; they are unrelated and not fixed here.mainis already fmt-dirty inedge.rs:651; that hunk was deliberately kept out of the diff.Three disable runs, each red-then-green:
with_topologywrites SPARE bits instead of TRUTHtopology()reads SPARE bits instead of 59..60CausalTopologyordinals 1 and 2 swappedThe 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.