From b448ffecd8138b55418d972ddd97885c93898f80 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Oct 2026 12:12:14 +0000 Subject: [PATCH 1/3] selector probe: new_encounter holds while revision would still change the horizon A roots-only test dropped rootless revisions (a contradiction that withdraws a claim without a new root was never applied). The fact is now one mask test per field delta.resulting writes, and equals "revise changes the horizon" on every world of a 2-bit universe. True echo and rootless-contradiction cases pinned. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MHMvKHLhM6xwRswKY4s6vY --- .claude/board/STATUS_BOARD.md | 2 +- .../2026-10-06-selector-revision-wired.md | 19 ++- .../examples/recipe_selector_probe.rs | 125 +++++++++++++++--- 3 files changed, 116 insertions(+), 30 deletions(-) diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index 5fe4daec3..bc9c3498e 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -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); `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-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 (revising would still change a horizon mask; equals `revise` changing it on a 2-bit universe; rootless revisions included) 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; 14 tests, 10 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 | diff --git a/.claude/board/entries/2026-10-06-selector-revision-wired.md b/.claude/board/entries/2026-10-06-selector-revision-wired.md index 122e8379b..9fe1301de 100644 --- a/.claude/board/entries/2026-10-06-selector-revision-wired.md +++ b/.claude/board/entries/2026-10-06-selector-revision-wired.md @@ -2,25 +2,30 @@ ## 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 +`crates/cognitive-shader-driver/examples/recipe_selector_probe.rs`, now 14 +tests (9 new), 10 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. +- `new_encounter` is no longer supplied. It is read from the world: revising + would still change a mask of the horizon (projected claims, a root, an + inherited root, or a contradiction not yet in the tension). On all 65 536 + worlds of a 2-bit universe it equals whether `revise` changes those masks. +- A first version tested new roots only and dropped rootless revisions + (codex review): a contradiction that withdraws a claim without a new root + (`ContradictionPreserved`) was never applied. Now it is applied once. - 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 +- An echo (same projection, roots held, no contradiction) 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 +against projected claims instead of roots; the bypass flag ignored; each of +the four mask clauses dropped; the roots-only predicate restored. The roots-vs-claims disable was green until the held-claim case was added. ## FINDING diff --git a/crates/cognitive-shader-driver/examples/recipe_selector_probe.rs b/crates/cognitive-shader-driver/examples/recipe_selector_probe.rs index 4bfd8c16f..b94bbfe58 100644 --- a/crates/cognitive-shader-driver/examples/recipe_selector_probe.rs +++ b/crates/cognitive-shader-driver/examples/recipe_selector_probe.rs @@ -42,15 +42,18 @@ //! //! # 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 +//! `new_encounter` is read from the world, not supplied: it holds while +//! revising with the pending encounter would still change the horizon's +//! masks: the projected claims, a root not yet held, an inherited root not +//! yet held, or a contradiction not yet in the tension. Those are exactly the +//! fields `GadamerRevision` writes into `delta.resulting` (with the horizon as +//! ancestry), each one mask comparison (one fused fold, Round 6). It covers +//! rootless revisions too (`ContradictionPreserved`, `AssumptionExposed`, +//! `Reinterpretation`): they change the horizon without minting a root. 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. +//! revision because the encounter is absorbed, 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 @@ -234,14 +237,20 @@ fn ancestry(h: &Horizon) -> BasisView { } impl World { - /// `new_encounter`, derived: the encounter carries a root the horizon - /// does not have. Equal to `revise(...).new_independent_roots != 0`. + /// `new_encounter`, derived: revising would still change a mask of the + /// horizon. One test per field `delta.resulting` writes. fn new_encounter(&self) -> bool { - !self - .encounter - .independent_roots - .difference(&self.horizon.independent_roots) - .is_empty() + let (h, e) = (&self.horizon, &self.encounter); + e.proposed_claims != h.projected_claims + || !e + .independent_roots + .difference(&h.independent_roots) + .is_empty() + || !e.inherited_roots.difference(&h.inherited_roots).is_empty() + || !e + .contradictions + .difference(&h.unresolved_tension) + .is_empty() } /// The declared state: three stand-in facts plus the derived one. @@ -492,6 +501,46 @@ mod tests { state(false, true, false, false) } + /// The masks `delta.resulting` can change. + fn masks(h: &Horizon) -> [u64; 4] { + [ + h.projected_claims, + h.independent_roots, + h.inherited_roots, + h.unresolved_tension, + ] + } + + /// FAILS IF: the derived fact disagrees with whether revising actually + /// changes the horizon, on any world of a 2-bit universe. + #[test] + fn the_derived_fact_is_whether_revision_changes_the_horizon() { + let (mut pending, mut settled_worlds) = (0, 0); + for x in 0u32..1 << 16 { + let f = |i: u32| u64::from(x >> (2 * i) & 3); + let mut w = fusion_world(); + w.horizon.projected_claims = f(0); + w.horizon.independent_roots = f(1); + w.horizon.inherited_roots = f(2); + w.horizon.unresolved_tension = f(3); + w.encounter.proposed_claims = f(4); + w.encounter.independent_roots = f(5); + w.encounter.inherited_roots = f(6); + w.encounter.contradictions = f(7); + w.encounter.resistance = f(7); + let before = masks(&w.horizon); + let derived = w.new_encounter(); + w.revise(); + assert_eq!(derived, masks(&w.horizon) != before, "{x:#x}"); + if derived { + pending += 1; + } else { + settled_worlds += 1; + } + } + assert!(pending > 0 && settled_worlds > 0, "both outcomes occur"); + } + /// FAILS IF: the derived fact disagrees with revision's own new-root test. #[test] fn the_derived_fact_is_revisions_new_root_test() { @@ -576,19 +625,51 @@ mod tests { assert_eq!(world.horizon.independent_roots, 0b011); } - /// FAILS IF: an echo (every root already held) still selects revision. + /// An echo: the encounter proposes what the horizon already projects, + /// with roots it already holds and no contradiction. + fn echo_world() -> World { + let mut w = fusion_world(); + w.encounter = EncounterEvidence { + proposed_claims: 0b101, + independent_roots: 0b001, + inherited_roots: 0, + resistance: 0, + contradictions: 0, + affected_parts: 0b101, + }; + w + } + + /// FAILS IF: an echo selects revision. #[test] fn an_echo_selects_nothing() { - let mut world = fusion_world(); - world.encounter.independent_roots = 0b001; + let mut world = echo_world(); + let delta = + GadamerRevision.revise(&world.horizon, &world.encounter, &ancestry(&world.horizon)); + assert_eq!(delta.kind, RevisionKind::Echo); 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 - }); + assert_eq!(world, echo_world()); + } + + /// FAILS IF: a revision that mints no root but changes the horizon is + /// dropped. The encounter contradicts claim 0 and withdraws it, with no + /// new root: `ContradictionPreserved`. It must be applied once, then rest. + #[test] + fn a_rootless_contradiction_is_still_revised() { + let mut world = fusion_world(); + world.encounter.proposed_claims = 0b100; + world.encounter.independent_roots = 0b100; + let delta = + GadamerRevision.revise(&world.horizon, &world.encounter, &ancestry(&world.horizon)); + assert_eq!(delta.kind, RevisionKind::ContradictionPreserved); + assert_eq!(delta.new_independent_roots, 0); + 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.projected_claims, 0b100); + assert_eq!(world.horizon.unresolved_tension, 0b001); } /// FAILS IF: the same starting world replays to a different path or a From de69eefe8140a0b535d815e14ccbf05b9ec667cd Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Oct 2026 12:15:15 +0000 Subject: [PATCH 2/3] selector probe: derive local_disagreement as tension not yet interrogated local_disagreement = unresolved_tension \ interrogated, where interrogated is the coverage the MooreInterrogation recipe writes. Tension is never cleared; a revision that adds a contradiction reopens the fact. Dropping the interrogation write never rests. observations_pending and frontier_bounded remain stand-ins. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MHMvKHLhM6xwRswKY4s6vY --- .claude/board/STATUS_BOARD.md | 2 +- ...10-06-selector-local-disagreement-wired.md | 42 ++++ .claude/board/entries/README.md | 3 +- .../examples/recipe_selector_probe.rs | 186 +++++++++++++----- 4 files changed, 186 insertions(+), 47 deletions(-) create mode 100644 .claude/board/entries/2026-10-06-selector-local-disagreement-wired.md diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index bc9c3498e..90453ba5e 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -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); `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 (revising would still change a horizon mask; equals `revise` changing it on a 2-bit universe; rootless revisions included) 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; 14 tests, 10 disable runs red | +| **D-GSO-6** | P6: deterministic, versioned recipe selector | Shipped (#1358, #1362); `local_disagreement` wired + rootless-revision fix: 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 (revising would still change a horizon mask; equals `revise` changing it on a 2-bit universe; rootless revisions included) 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; 14 tests, 10 disable runs red. Follow-up 2: `local_disagreement` = `unresolved_tension \ interrogated`, where `interrogated` is the coverage `MooreInterrogation` writes (tension itself never cleared); a revision adding a new contradiction reopens it; dropping the interrogation write never rests; `observations_pending` and `frontier_bounded` remain stand-ins; 17 tests, 15 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 | diff --git a/.claude/board/entries/2026-10-06-selector-local-disagreement-wired.md b/.claude/board/entries/2026-10-06-selector-local-disagreement-wired.md new file mode 100644 index 000000000..d9f42579c --- /dev/null +++ b/.claude/board/entries/2026-10-06-selector-local-disagreement-wired.md @@ -0,0 +1,42 @@ +# 2026-10-06 — D-GSO-6 follow-up 2: local disagreement wired as uncovered tension + +## MEASURED + +`crates/cognitive-shader-driver/examples/recipe_selector_probe.rs`, 17 +tests, 15 disable runs red. Builds on #1362. Also carries the rootless- +revision fix from codex review of #1362, which merged before it was pushed: +`new_encounter` holds while revising would still change a horizon mask +(equal to "revise changes those masks" on all 65 536 worlds of a 2-bit +universe). + +- `local_disagreement` is read from the world as + `unresolved_tension \ interrogated`. `interrogated` is the coverage the + `MooreInterrogation` recipe writes (by union); nothing else writes it. +- Interrogation clears the fact and leaves the tension unchanged. +- A revision that adds a contradiction reopens the fact: from a settled + state the fusion world runs `[Revision, MooreInterrogation]` and rests. + A contradiction already covered does not reopen it; one on a new bit does. +- Dropping the interrogation's write never rests, as dropping revision's + write never rests. +- Every wired cycle rests within 5 steps and replays to the same path and + final world. + +Disable runs, each red: raw tension read without coverage; interrogation a +no-op; supplied flag used instead of the derived one; interrogation clearing +the tension; the drop flag ignored. + +## FINDING + +- **With both facts derived, a cycle is no longer one step per condition.** + Revision can reopen local disagreement, so V1 runs `Revision` then + `MooreInterrogation` where the stand-in transitions ran them once each in + §11 order. + +## OPEN + +- `interrogated` is probe-local. `InterpretiveHorizon` has no coverage + field; where coverage lives durably is undecided. +- The Moore recipe's fold (Palette hop over `Register128`, D-GSO-5 R2) is not + linked to claim bits; only its coverage is wired. What an interrogation + concludes about a tension bit is not modelled. +- `observations_pending` and `frontier_bounded` remain stand-ins. diff --git a/.claude/board/entries/README.md b/.claude/board/entries/README.md index 717cc7cad..a0e4fe993 100644 --- a/.claude/board/entries/README.md +++ b/.claude/board/entries/README.md @@ -25,13 +25,14 @@ 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. -227 entries, 2026-08-06 .. 2026-10-06. +228 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 | `D-GSO-6` | | [2026-10-06-selector-local-disagreement-wired.md](2026-10-06-selector-local-disagreement-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) | diff --git a/crates/cognitive-shader-driver/examples/recipe_selector_probe.rs b/crates/cognitive-shader-driver/examples/recipe_selector_probe.rs index b94bbfe58..05d2047b8 100644 --- a/crates/cognitive-shader-driver/examples/recipe_selector_probe.rs +++ b/crates/cognitive-shader-driver/examples/recipe_selector_probe.rs @@ -37,8 +37,9 @@ //! 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. `new_encounter` is no longer one: see -//! "Revision, wired" below. The other three still are. +//! change from their outputs. `new_encounter` and `local_disagreement` +//! are no longer stand-ins: see "Revision, wired" below. The other two +//! (`observations_pending`, `frontier_bounded`) still are. //! //! # Revision, wired //! @@ -55,10 +56,20 @@ //! revision because the encounter is absorbed, 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. +//! `local_disagreement` cannot be read off `unresolved_tension` alone: +//! revision preserves tension and never clears it, so a selector reading it +//! would interrogate forever. It is read as tension not yet interrogated: +//! `unresolved_tension \ interrogated`, where `interrogated` is the coverage +//! the `MooreInterrogation` recipe writes, the tension bits it has examined. +//! The tension itself is never cleared; only coverage grows. A revision that +//! adds a new contradiction reopens the fact, so a cycle can run `Revision` +//! and then interrogate the tension that revision introduced. +//! +//! What this does not decide: `interrogated` is a probe-local record. The +//! horizon carries no such field, and the Moore recipe's actual fold (the +//! Palette hop over `Register128`, D-GSO-5 R2) is not linked to claim bits +//! here; only the coverage it leaves is. Where coverage lives durably, and +//! what the interrogation concludes, stay open. //! //! Run: `cargo run -p cognitive-shader-driver --example recipe_selector_probe` //! Tests: `cargo test -p cognitive-shader-driver --example recipe_selector_probe` @@ -224,6 +235,9 @@ type Horizon = InterpretiveHorizon<(), u64>; struct World { horizon: Horizon, encounter: EncounterEvidence, + /// Tension bits `MooreInterrogation` has examined. Only that recipe + /// writes it, and only by union. + interrogated: u64, } /// The horizon as ancestry, as in the Round 6 cycle. @@ -253,14 +267,29 @@ impl World { .is_empty() } - /// The declared state: three stand-in facts plus the derived one. + /// `local_disagreement`, derived: tension not yet interrogated. + fn local_disagreement(&self) -> bool { + !self + .horizon + .unresolved_tension + .difference(&self.interrogated) + .is_empty() + } + + /// The declared state: two stand-in facts plus the two derived ones. fn state(&self, rest: EpistemicState) -> EpistemicState { EpistemicState { new_encounter: self.new_encounter(), + local_disagreement: self.local_disagreement(), ..rest } } + /// The `MooreInterrogation` recipe's write: the tension it examined. + fn interrogate(&mut self) { + self.interrogated |= self.horizon.unresolved_tension; + } + /// The `Revision` recipe: revise once; `delta.resulting` is the only /// output carried forward. fn revise(&mut self) { @@ -270,36 +299,40 @@ impl World { } } -/// 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. +/// The most steps a wired cycle may take: the two stand-in conditions, one +/// revision, and an interrogation before and after it (revision can reopen +/// local disagreement). +const WIRED_MAX_STEPS: usize = 5; + +/// Run a cycle whose `new_encounter` and `local_disagreement` are derived from +/// `world` at every step. The other two facts use the stand-in transition. +/// `drop` names a recipe whose write is discarded (the bypass falsifiers): +/// the recipe runs on a copy and nothing lands. fn wired_cycle( policy: SelectorPolicy, rest: EpistemicState, world: &mut World, - write: bool, -) -> ([Option; MAX_STEPS + 1], usize) { - let mut trace = [None; MAX_STEPS + 1]; + drop: Option, +) -> ([Option; WIRED_MAX_STEPS + 1], usize) { + let mut trace = [None; WIRED_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), + let Some(recipe) = policy.select(world.state(rest)) else { + return (trace, step); + }; + *slot = Some(recipe); + let target = if drop == Some(recipe) { + &mut world.clone() + } else { + &mut *world + }; + match recipe { + ProbeRecipe::Revision => target.revise(), + ProbeRecipe::MooreInterrogation => target.interrogate(), + other => rest = after(other, rest), } } - (trace, MAX_STEPS + 1) + (trace, WIRED_MAX_STEPS + 1) } /// A small world: the horizon holds claims {0, 2}; the encounter proposes @@ -328,6 +361,7 @@ fn fusion_world() -> World { contradictions: 0b001, affected_parts: 0b111, }, + interrogated: 0, } } @@ -353,7 +387,7 @@ fn main() { SelectorPolicy::CURRENT ); let mut world = fusion_world(); - let (trace, n) = wired_cycle(SelectorPolicy::CURRENT, start, &mut world, true); + let (trace, n) = wired_cycle(SelectorPolicy::CURRENT, start, &mut world, None); println!( "wired: {:?}, horizon roots {:#b}", &trace[..n], @@ -564,26 +598,29 @@ mod tests { 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)]); + let (trace, n) = wired_cycle(SelectorPolicy::V1, settled(), &mut world, None); + // Revision adds contradiction 0 to the tension, which reopens local + // disagreement; one interrogation covers it and the cycle rests. + assert_eq!(&trace[..n], &[Some(Revision), Some(MooreInterrogation)]); 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. + // The full cycle under V1. The supplied `local_disagreement = true` + // is ignored: the fact is derived, and there is no tension yet. let mut world = fusion_world(); let (trace, n) = wired_cycle( SelectorPolicy::V1, state(true, false, true, false), &mut world, - true, + None, ); assert_eq!( &trace[..n], &[ Some(ObserveFold), Some(ProductInterrogation), - Some(MooreInterrogation), - Some(Revision) + Some(Revision), + Some(MooreInterrogation) ] ); } @@ -593,8 +630,8 @@ mod tests { #[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"); + let (trace, n) = wired_cycle(SelectorPolicy::V1, settled(), &mut world, Some(Revision)); + assert_eq!(n, WIRED_MAX_STEPS + 1, "did not rest"); assert!(trace.iter().all(|r| *r == Some(Revision))); assert_eq!(world, fusion_world(), "nothing was written"); } @@ -620,7 +657,7 @@ mod tests { 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); + let (trace, n) = wired_cycle(SelectorPolicy::V1, settled(), &mut world, None); assert_eq!(&trace[..n], &[Some(Revision)]); assert_eq!(world.horizon.independent_roots, 0b011); } @@ -648,7 +685,7 @@ mod tests { GadamerRevision.revise(&world.horizon, &world.encounter, &ancestry(&world.horizon)); assert_eq!(delta.kind, RevisionKind::Echo); assert!(!world.new_encounter()); - let (_, n) = wired_cycle(SelectorPolicy::V1, settled(), &mut world, true); + let (_, n) = wired_cycle(SelectorPolicy::V1, settled(), &mut world, None); assert_eq!(n, 0); assert_eq!(world, echo_world()); } @@ -666,8 +703,8 @@ mod tests { assert_eq!(delta.kind, RevisionKind::ContradictionPreserved); assert_eq!(delta.new_independent_roots, 0); assert!(world.new_encounter()); - let (trace, n) = wired_cycle(SelectorPolicy::V1, settled(), &mut world, true); - assert_eq!(&trace[..n], &[Some(Revision)]); + let (trace, n) = wired_cycle(SelectorPolicy::V1, settled(), &mut world, None); + assert_eq!(&trace[..n], &[Some(Revision), Some(MooreInterrogation)]); assert_eq!(world.horizon.projected_claims, 0b100); assert_eq!(world.horizon.unresolved_tension, 0b001); } @@ -679,15 +716,74 @@ mod tests { 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); + let ra = wired_cycle(policy, rest, &mut a, None); + let rb = wired_cycle(policy, rest, &mut b, None); assert_eq!(ra, rb, "{policy:?} {rest:?}"); assert_eq!(a, b, "{policy:?} {rest:?}"); - assert!(ra.1 <= MAX_STEPS, "{policy:?} {rest:?} did not rest"); + assert!(ra.1 <= WIRED_MAX_STEPS, "{policy:?} {rest:?} did not rest"); } } } + /// FAILS IF: interrogation's coverage is not what clears the fact, or + /// clearing it touches the tension itself. + #[test] + fn interrogation_clears_the_fact_and_keeps_the_tension() { + let mut world = fusion_world(); + world.horizon.unresolved_tension = 0b001; + world.encounter = echo_world().encounter; + assert!(world.local_disagreement()); + assert!(!world.new_encounter()); + let (trace, n) = wired_cycle(SelectorPolicy::V1, settled(), &mut world, None); + assert_eq!(&trace[..n], &[Some(MooreInterrogation)]); + assert!(!world.local_disagreement()); + assert_eq!(world.horizon.unresolved_tension, 0b001, "tension kept"); + assert_eq!(world.interrogated, 0b001); + } + + /// FAILS IF: dropping the interrogation's write still lets the cycle + /// rest, i.e. the rest does not depend on the interrogation's output. + #[test] + fn dropping_the_interrogation_write_never_rests() { + let mut world = fusion_world(); + world.horizon.unresolved_tension = 0b001; + world.encounter = echo_world().encounter; + let before = world.clone(); + let (trace, n) = wired_cycle( + SelectorPolicy::V1, + settled(), + &mut world, + Some(MooreInterrogation), + ); + assert_eq!(n, WIRED_MAX_STEPS + 1, "did not rest"); + assert!(trace.iter().all(|r| *r == Some(MooreInterrogation))); + assert_eq!(world, before, "nothing was written"); + } + + /// FAILS IF: tension already interrogated reopens the fact, or a new + /// contradiction does not. + #[test] + fn only_new_tension_reopens_local_disagreement() { + // Contradiction 0 is already in the tension and already covered: the + // revision changes the projection, so it runs, but adds no new + // tension, so nothing is interrogated afterwards. + let mut world = fusion_world(); + world.horizon.unresolved_tension = 0b001; + world.interrogated = 0b001; + let (trace, n) = wired_cycle(SelectorPolicy::V1, settled(), &mut world, None); + assert_eq!(&trace[..n], &[Some(Revision)]); + + // A contradiction on a new bit reopens it after the revision. + let mut world = fusion_world(); + world.horizon.unresolved_tension = 0b001; + world.interrogated = 0b001; + world.encounter.contradictions = 0b011; + world.encounter.resistance = 0b011; + let (trace, n) = wired_cycle(SelectorPolicy::V1, settled(), &mut world, None); + assert_eq!(&trace[..n], &[Some(Revision), Some(MooreInterrogation)]); + assert_eq!(world.interrogated, 0b011); + } + /// 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. From f07e3b424c19513a5ee6a06494330178f6735e42 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Oct 2026 12:17:45 +0000 Subject: [PATCH 3/3] board: give the local-disagreement entry its own id Both selector follow-up entries resolved to D-GSO-6 from their headings, which the entries index rejects as a duplicate ID. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MHMvKHLhM6xwRswKY4s6vY --- .../entries/2026-10-06-selector-local-disagreement-wired.md | 4 +++- .claude/board/entries/README.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.claude/board/entries/2026-10-06-selector-local-disagreement-wired.md b/.claude/board/entries/2026-10-06-selector-local-disagreement-wired.md index d9f42579c..301b418a8 100644 --- a/.claude/board/entries/2026-10-06-selector-local-disagreement-wired.md +++ b/.claude/board/entries/2026-10-06-selector-local-disagreement-wired.md @@ -1,4 +1,6 @@ -# 2026-10-06 — D-GSO-6 follow-up 2: local disagreement wired as uncovered tension +# 2026-10-06 — Selector follow-up 2: local disagreement wired as uncovered tension + +Deliverable line: the D-GSO-6 selector (follow-up to #1362). ## MEASURED diff --git a/.claude/board/entries/README.md b/.claude/board/entries/README.md index a0e4fe993..82b42e03e 100644 --- a/.claude/board/entries/README.md +++ b/.claude/board/entries/README.md @@ -32,7 +32,7 @@ exactly one of them, never both. | 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 | `D-GSO-6` | | [2026-10-06-selector-local-disagreement-wired.md](2026-10-06-selector-local-disagreement-wired.md) | +| 2026-10-06 | `selector-local-disagreement-wired` | | [2026-10-06-selector-local-disagreement-wired.md](2026-10-06-selector-local-disagreement-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) |