Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/board/STATUS_BOARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Plan: `.claude/plans/2026-10-06-global-sudoku-replayable-orchestration-v1.md`. O
| **D-GSO-3** | P3: global ontology agreement/disagreement probe (mammal fixture) | Shipped (#1350) | `cognitive-shader-driver/examples/ontology_agreement_probe.rs`: `NiblePath` hierarchy + `ontology_warrant::Quorum`/`SourceVerdict` folded per observation into local (self + siblings), up (ancestors), down (descendants); binary-entropy tension; all four cells + unknown reached; 6 tests, 5 disable runs red |
| **D-GSO-4** | P4: entropy × topology routing probe | Shipped (#1351) | `cognitive-shader-driver/examples/entropy_topology_probe.rs` = F-ECG-1 and F-ECG-2 at probe level (F-ECG-3 open: no real corpus carries topology bits yet): bits 59..60 read through `BandDeclarations::project_truth` (topology lens, asserted provenance), known-share census as competence, `SettlementSignals::cell`, §4b walker route; equal measured entropy (0.8167 bits) → Crystal vs Glass; 6 tests, 4 disable runs red |
| **D-GSO-5** | P5: first recipe quartet over existing primitives | Shipped (#1355) | `cognitive-shader-driver/examples/recipe_quartet_probe.rs`: four `match`-arm recipes over `Quorum::observe`, `Quad8::fold_product`, `Morton8x8` + `PalettePerturbation::hop`, `GadamerRevision::revise` (verdict left `NotRun`: removing the new roots is tautological, so the counterfactual moves to P7); a thread-local counting allocator shows zero allocations per recipe (no instruction vector); `ProbeRecipe` is its own type, not a shipped `recipes` ID; ordinal meanings not canonized; no Pearl projection (P7) and no selector (P6); 7 tests, 6 disable runs red |
| **D-GSO-6** | P6: deterministic, versioned recipe selector | Shipped (#1358) | `cognitive-shader-driver/examples/recipe_selector_probe.rs`: `SelectorPolicy::select(state)` is a pure `const fn` over a declared 4-fact `EpistemicState`; V1 = §11 order (fold, bound, local, revise), rests only on the settled state; V2 swaps two steps (differs on 2 of 16 states); a recorded `Selection` (policy + state) replays under its own policy; every cycle rests in one step per open condition and replays identically; the version covers selection only, not recipe implementations (§13 digest open); 5 tests, 5 disable runs red |
| **D-GSO-6** | P6: deterministic, versioned recipe selector | Shipped (#1358); `new_encounter` wired to revision output: In PR | `cognitive-shader-driver/examples/recipe_selector_probe.rs`: `SelectorPolicy::select(state)` is a pure `const fn` over a declared 4-fact `EpistemicState`; V1 = §11 order (fold, bound, local, revise), rests only on the settled state; V2 swaps two steps (differs on 2 of 16 states); a recorded `Selection` (policy + state) replays under its own policy; every cycle rests in one step per open condition and replays identically; the version covers selection only, not recipe implementations (§13 digest open); 5 tests, 5 disable runs red. Follow-up: `new_encounter` is derived from the world (encounter roots \ horizon roots) and the `Revision` recipe writes `GadamerRevision`'s `delta.resulting`; dropping that write never rests; `unresolved_tension` cannot drive `local_disagreement` (never cleared); the other three facts are still stand-ins; 12 tests, 5 disable runs red |
| **D-GSO-7** | P7: reasoning-band earning and downgrade | In PR | `cognitive-shader-driver/examples/reasoning_band_probe.rs`: `ReasoningBand` (bits 61..63) read only via `band_reading::project_band`; earned one rung per executed passing proof under the shipped Pearl `CausalMask` (SO majority → Association, PO intervention trial passed → Causal, SPO removal attack passed → Counterfactual), outcomes executed from trial data and never supplied, no skipping, `NotRun` never raises; failed test, contradicting `Quorum` (minority caps at Association, majority drops to Surface) and `simpsons_paradox_risk` lower it; replay gives the same edge bits; unreadable provenance / undeclared / band-absent refuse; ladder and thresholds are policy pins; 7 tests, 10 disable runs red |
| **D-GSO-8** | P8: seal boundary | Queued | replay from the prior seal reproduces the next seal |

Expand Down
39 changes: 39 additions & 0 deletions .claude/board/entries/2026-10-06-selector-revision-wired.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 2026-10-06 — D-GSO-6 follow-up: the selector's revision fact is wired

## MEASURED

`crates/cognitive-shader-driver/examples/recipe_selector_probe.rs`, now 12
tests (7 new), 5 disable runs red. Builds on D-GSO-6 (#1358) and the Round 6
cycle (#1359).

- `new_encounter` is no longer supplied. It is read from the world: the
encounter's independent roots minus the horizon's. That equals
`GadamerRevision`'s `new_independent_roots` with the horizon as ancestry.
- The `Revision` recipe runs `GadamerRevision::revise`; only
`delta.resulting` reaches the next state.
- After one real revision the cycle rests (roots `0b101 → 0b111`). With the
write dropped it selects `Revision` every step and never rests.
- An echo (no new root) selects nothing. A held claim gaining its first
root (`IndependentConfirmation`) selects `Revision` once.
- Every (policy, stand-in state) start replays to the same path and the
same final horizon.

Disable runs, each red: derived fact forced true; derived fact ignored in
favour of the supplied flag; revise result discarded; roots compared
against projected claims instead of roots; the bypass flag ignored. The
roots-vs-claims disable was green until the held-claim case was added.

## FINDING

- **The selector's rest after revision now comes from revision's output.**
It is no longer a stand-in.
- **`local_disagreement` cannot be read off the horizon.**
`unresolved_tension` survives every revision, so a selector reading it
would interrogate forever.

## OPEN

- `observations_pending`, `frontier_bounded` and `local_disagreement` are
still stand-ins. The last needs a source that clears: tension new since
the last interrogation, not tension held.
- The policy version still covers selection only (§13 digest).
3 changes: 2 additions & 1 deletion .claude/board/entries/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ index row, (3) no duplicate entry id. Checks 1 and 2 are deliberately
opposite directions; the stranding this convention prevents shows up in
exactly one of them, never both.

226 entries, 2026-08-06 .. 2026-10-06.
227 entries, 2026-08-06 .. 2026-10-06.

| date | entry id | finding | file |
|---|---|---|---|
| 2026-10-06 | `v3-v4-dual-reading-round3` | | [2026-10-06-v3-v4-dual-reading-round3.md](2026-10-06-v3-v4-dual-reading-round3.md) |
| 2026-10-06 | `spofc-do-learn-as-compile-round6` | | [2026-10-06-spofc-do-learn-as-compile-round6.md](2026-10-06-spofc-do-learn-as-compile-round6.md) |
| 2026-10-06 | `D-GSO-6` | | [2026-10-06-selector-revision-wired.md](2026-10-06-selector-revision-wired.md) |
| 2026-10-06 | `quack-r2il-one-evaluator-round4` | | [2026-10-06-quack-r2il-one-evaluator-round4.md](2026-10-06-quack-r2il-one-evaluator-round4.md) |
| 2026-10-06 | `D-MORTON-0` | | [2026-10-06-morton8x8-checked-address.md](2026-10-06-morton8x8-checked-address.md) |
| 2026-10-06 | `diff-plateau-as-expression-round5` | | [2026-10-06-diff-plateau-as-expression-round5.md](2026-10-06-diff-plateau-as-expression-round5.md) |
Expand Down
277 changes: 276 additions & 1 deletion crates/cognitive-shader-driver/examples/recipe_selector_probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,25 @@
//! digest) is not done here.
//! - **The state transitions in the replay test are a stand-in.** Each step
//! clears the condition its recipe answers; real recipes would produce that
//! change from their outputs.
//! change from their outputs. `new_encounter` is no longer one: see
//! "Revision, wired" below. The other three still are.
//!
//! # Revision, wired
//!
//! `new_encounter` is read from the world, not supplied: it holds while the
//! pending encounter carries an independent root the horizon does not yet
//! have. That is `GadamerRevision`'s own `has_new_root` with the horizon as
//! ancestry, one mask difference (one fused `Any` fold, Round 6). The
//! `Revision` recipe runs `GadamerRevision::revise` and its only output that
//! reaches the next state is `delta.resulting`. So the cycle rests after
//! revision because revision absorbed the roots, not because a stand-in
//! cleared a flag. Bypass the write and the selector picks `Revision` again,
//! forever.
//!
//! `local_disagreement` cannot be read off the horizon the same way:
//! `unresolved_tension` is preserved by revision and never cleared, so a
//! selector reading it would interrogate forever. Pinned as a test; the
//! source for that fact stays open.
//!
//! Run: `cargo run -p cognitive-shader-driver --example recipe_selector_probe`
//! Tests: `cargo test -p cognitive-shader-driver --example recipe_selector_probe`
Expand Down Expand Up @@ -187,6 +205,123 @@ fn cycle(
(trace, MAX_STEPS + 1)
}

// ── revision, wired ─────────────────────────────────────────────────────

use lance_graph_contract::revision::{
BasisView, CodebookId, EncounterEvidence, EvidenceMask, GadamerRevision, GrammarId, HorizonId,
InterpretiveHorizon, LanguageId, LensId, QuestionId, RevisionPolicy,
};

type Horizon = InterpretiveHorizon<(), u64>;

/// The part of the world the wired fact reads: the current horizon and the
/// encounter waiting at it. The encounter is supplied by observation; the
/// selector never creates one.
#[derive(Debug, Clone, PartialEq, Eq)]
struct World {
horizon: Horizon,
encounter: EncounterEvidence<u64>,
}

/// The horizon as ancestry, as in the Round 6 cycle.
fn ancestry(h: &Horizon) -> BasisView<u64> {
BasisView {
ancestry_independent_roots: h.independent_roots,
ancestry_derived_roots: h.inherited_roots,
ancestor_claims: h.projected_claims,
closes_cycle: false,
}
}

impl World {
/// `new_encounter`, derived: the encounter carries a root the horizon
/// does not have. Equal to `revise(...).new_independent_roots != 0`.
fn new_encounter(&self) -> bool {
!self
.encounter
.independent_roots
.difference(&self.horizon.independent_roots)
.is_empty()
Comment on lines +242 to +244

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 rootless revisions pending

When a fresh encounter changes the projection or carries resistance/contradictions but introduces no new independent root, this predicate returns false and wired_cycle rests without applying the encounter. The added an_echo_selects_nothing case demonstrates the problem: it retains fusion_world()'s changed proposed_claims, resistance, and contradiction, so GadamerRevision::revise would produce ContradictionPreserved, update projected_claims, and preserve the tension—not an echo. Track whether the encounter has been processed separately from root novelty so these valid rootless revisions are not silently discarded.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Correct. A roots-only test dropped rootless revisions, and an_echo_selects_nothing was not an echo. Fixed in d82a346f (pushing now): new_encounter holds while revising would still change a horizon mask (projected claims, a root, an inherited root, or a contradiction not yet in the tension), one test per field delta.resulting writes. On all 65 536 worlds of a 2-bit universe it equals whether revise changes those masks. Added a_rootless_contradiction_is_still_revised (ContradictionPreserved, applied once, then rests) and made the echo test a true Echo. Restoring the roots-only predicate turns both new tests red.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Correction to the reply above: this PR merged before the fix was pushed. The fix landed in #1365 (commit b448ffe).


Generated by Claude Code

}

/// The declared state: three stand-in facts plus the derived one.
fn state(&self, rest: EpistemicState) -> EpistemicState {
EpistemicState {
new_encounter: self.new_encounter(),
..rest
}
}

/// The `Revision` recipe: revise once; `delta.resulting` is the only
/// output carried forward.
fn revise(&mut self) {
let delta =
GadamerRevision.revise(&self.horizon, &self.encounter, &ancestry(&self.horizon));
self.horizon = delta.resulting;
}
}

/// Run a cycle whose `new_encounter` is derived from `world` at every step.
/// The other three facts use the stand-in transition. `write` is false only
/// in the bypass falsifier: the revision runs but its result is dropped.
fn wired_cycle(
policy: SelectorPolicy,
rest: EpistemicState,
world: &mut World,
write: bool,
) -> ([Option<ProbeRecipe>; MAX_STEPS + 1], usize) {
let mut trace = [None; MAX_STEPS + 1];
let mut rest = rest;
for (step, slot) in trace.iter_mut().enumerate() {
match policy.select(world.state(rest)) {
Some(ProbeRecipe::Revision) => {
*slot = Some(ProbeRecipe::Revision);
if write {
world.revise();
} else {
// Revise a copy and drop it: the work runs, nothing lands.
world.clone().revise();
}
}
Some(recipe) => {
*slot = Some(recipe);
rest = after(recipe, rest);
}
None => return (trace, step),
}
}
(trace, MAX_STEPS + 1)
}

/// A small world: the horizon holds claims {0, 2}; the encounter proposes
/// {1, 2}, rooted in {1, 2}, and contradicts claim 0.
fn fusion_world() -> World {
World {
horizon: InterpretiveHorizon {
id: HorizonId(0),
awareness: (),
question: QuestionId(6),
language: LanguageId(0),
grammar: GrammarId(0),
codebook: CodebookId(0),
lens: LensId(0),
projected_claims: 0b101,
independent_roots: 0b101,
inherited_roots: 0,
unresolved_tension: 0,
revision_index: 0,
},
encounter: EncounterEvidence {
proposed_claims: 0b110,
independent_roots: 0b110,
inherited_roots: 0,
resistance: 0b001,
contradictions: 0b001,
affected_parts: 0b111,
},
}
}

fn main() {
let start = EpistemicState {
observations_pending: true,
Expand All @@ -208,11 +343,19 @@ fn main() {
"{replayed} selections recorded under {:?} and replayed",
SelectorPolicy::CURRENT
);
let mut world = fusion_world();
let (trace, n) = wired_cycle(SelectorPolicy::CURRENT, start, &mut world, true);
println!(
"wired: {:?}, horizon roots {:#b}",
&trace[..n],
world.horizon.independent_roots
);
}

#[cfg(test)]
mod tests {
use super::*;
use lance_graph_contract::revision::RevisionKind;
use ProbeRecipe::*;

fn state(o: bool, f: bool, l: bool, n: bool) -> EpistemicState {
Expand Down Expand Up @@ -344,6 +487,138 @@ mod tests {
);
}

/// The settled stand-in facts, so only the wired fact is open.
fn settled() -> EpistemicState {
state(false, true, false, false)
}

/// FAILS IF: the derived fact disagrees with revision's own new-root test.
#[test]
fn the_derived_fact_is_revisions_new_root_test() {
let w = fusion_world();
let delta = GadamerRevision.revise(&w.horizon, &w.encounter, &ancestry(&w.horizon));
assert!(w.new_encounter());
assert_ne!(delta.new_independent_roots, 0);
let after = World {
horizon: delta.resulting,
..w
};
assert!(
!after.new_encounter(),
"the resulting horizon has the roots"
);
}

/// FAILS IF: the cycle does not rest after one real revision, or rests
/// without one. The rest comes from `delta.resulting`, not a stand-in.
#[test]
fn a_real_revision_makes_the_cycle_rest() {
let mut world = fusion_world();
let before = world.horizon.clone();
let (trace, n) = wired_cycle(SelectorPolicy::V1, settled(), &mut world, true);
assert_eq!(&trace[..n], &[Some(Revision)]);
assert_eq!(world.horizon.independent_roots, 0b111);
assert_eq!(world.horizon.revision_index, before.revision_index + 1);

// The full cycle under V1, with the wired fact last.
let mut world = fusion_world();
let (trace, n) = wired_cycle(
SelectorPolicy::V1,
state(true, false, true, false),
&mut world,
true,
);
assert_eq!(
&trace[..n],
&[
Some(ObserveFold),
Some(ProductInterrogation),
Some(MooreInterrogation),
Some(Revision)
]
);
}

/// FAILS IF: dropping revision's write still lets the cycle rest, i.e.
/// the rest does not depend on revision's output.
#[test]
fn dropping_the_revision_write_never_rests() {
let mut world = fusion_world();
let (trace, n) = wired_cycle(SelectorPolicy::V1, settled(), &mut world, false);
assert_eq!(n, MAX_STEPS + 1, "did not rest");
assert!(trace.iter().all(|r| *r == Some(Revision)));
assert_eq!(world, fusion_world(), "nothing was written");
}

/// FAILS IF: the derived fact reads claims instead of roots. Claim 1 is
/// already projected but has no independent root; the encounter brings
/// one. Revision calls that an independent confirmation, so the selector
/// must pick it.
#[test]
fn a_new_root_for_a_held_claim_selects_revision() {
let mut world = fusion_world();
world.horizon.projected_claims = 0b011;
world.horizon.independent_roots = 0b001;
world.encounter = EncounterEvidence {
proposed_claims: 0b011,
independent_roots: 0b010,
inherited_roots: 0,
resistance: 0,
contradictions: 0,
affected_parts: 0b011,
};
let delta =
GadamerRevision.revise(&world.horizon, &world.encounter, &ancestry(&world.horizon));
assert_eq!(delta.kind, RevisionKind::IndependentConfirmation);
assert!(world.new_encounter());
let (trace, n) = wired_cycle(SelectorPolicy::V1, settled(), &mut world, true);
assert_eq!(&trace[..n], &[Some(Revision)]);
assert_eq!(world.horizon.independent_roots, 0b011);
}

/// FAILS IF: an echo (every root already held) still selects revision.
#[test]
fn an_echo_selects_nothing() {
let mut world = fusion_world();
world.encounter.independent_roots = 0b001;
assert!(!world.new_encounter());
let (_, n) = wired_cycle(SelectorPolicy::V1, settled(), &mut world, true);
assert_eq!(n, 0);
assert_eq!(world, {
let mut w = fusion_world();
w.encounter.independent_roots = 0b001;
w
});
}

/// FAILS IF: the same starting world replays to a different path or a
/// different final horizon.
#[test]
fn the_wired_cycle_replays() {
for policy in [SelectorPolicy::V1, SelectorPolicy::V2] {
for rest in EpistemicState::all() {
let (mut a, mut b) = (fusion_world(), fusion_world());
let ra = wired_cycle(policy, rest, &mut a, true);
let rb = wired_cycle(policy, rest, &mut b, true);
assert_eq!(ra, rb, "{policy:?} {rest:?}");
assert_eq!(a, b, "{policy:?} {rest:?}");
assert!(ra.1 <= MAX_STEPS, "{policy:?} {rest:?} did not rest");
}
}
}

/// FAILS IF: revision starts clearing tension. Then `local_disagreement`
/// could be read off the horizon; while it holds, it cannot, because the
/// tension survives every revision and a reader of it would never rest.
#[test]
fn preserved_tension_cannot_drive_the_selector() {
let mut world = fusion_world();
world.revise();
assert_eq!(world.horizon.unresolved_tension, 0b001);
world.revise();
assert_eq!(world.horizon.unresolved_tension, 0b001, "still held");
}

/// FAILS IF: the selector fires on a settled state or stays silent on an
/// open one. Only the fully settled state rests.
#[test]
Expand Down
Loading