From 791eb0eec7c084722497fb9f3c8dac6db98c5341 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 16:38:35 +0000 Subject: [PATCH 01/13] plan: MUL <-> EWA trust propagation (measure-before-carve) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .claude/plans/mul-ewa-trust-propagation-v1.md — PROPOSED, plan/board only. MUL is point-wise and scalar (MulAssessment, verified: no variance/propagation surface anywhere); jc's certified EWA sandwich (Pillars 6/7) is the workspace's lawful uncertainty propagation operator; KanbanColumn::Plan = 4 (re-enter Planning carrying the witness) is the revision exit a propagated number would calibrate. W0 parity-anchors an inlined 2x2 sandwich against jc's certified output (jc stays zero-dep by constitution; the probe lives in deepnsm-v2). W1 is the STOP gate: scalar vs sandwich suspicion rankings over real multi-hop chains must diverge (rho < 0.95) AND the sandwich must predict S4-guarded error signals better — never the TD-NARS-REVISION-UNGUARDED confidences (fence, not target), with a stamp-shuffled null. W2 is an optional DTO-only Option (no tenant carve, no layout bump, minted only if W1 forces it). W3 probes the Commit->Plan flip rate on advance_on_gate — the epistemic-pothole detector quantified, two-sided. W4 is an explicit BUY / NO-BUY. Delineation: feeds mul-calibration-not-verdict-v1's thesis with calibration data, renames nothing (F-MUL-6 block respected), uses dialectic-engine-v1's arena as instrument only, leaves the tarski register HELD, resurrects none of E-3DGS-MU-HYDRATION-1's dropped EWA-semiring claim. Board hygiene same-commit: INTEGRATION_PLANS prepend, STATUS_BOARD D-MEP-0..4, supersession index regenerated (the diff is the plan's own GateDecision READ row — mechanical, expected). Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1 --- .claude/board/INTEGRATION_PLANS.md | 22 +++ .claude/board/STATUS_BOARD.md | 12 ++ .claude/board/SUPERSESSION-INDEX.md | 7 +- .claude/plans/mul-ewa-trust-propagation-v1.md | 177 ++++++++++++++++++ 4 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 .claude/plans/mul-ewa-trust-propagation-v1.md diff --git a/.claude/board/INTEGRATION_PLANS.md b/.claude/board/INTEGRATION_PLANS.md index 98de7accf..6b519c075 100644 --- a/.claude/board/INTEGRATION_PLANS.md +++ b/.claude/board/INTEGRATION_PLANS.md @@ -1,3 +1,25 @@ +## 2026-08-28 — PROPOSED (unbuilt, measure-before-carve): MUL ↔ EWA trust propagation, `.claude/plans/mul-ewa-trust-propagation-v1.md` + +Operator: *"check epistemic potholes > revision in kanban y rubicon model"* + +*"check for synergies MUL <> EWA."* Session-verified state: MUL is scalar and +point-wise (`MulAssessment`, zero variance/propagation surface); jc's EWA +sandwich (Pillars 6/7, certified) is the workspace's lawful uncertainty +propagation operator; `KanbanColumn::Plan = 4` (re-enter Planning carrying +the witness) is the revision exit the propagated number would calibrate; the +composition-legality question is ALREADY owned by jirak/pearl/ewa_sandwich +(EPIPHANIES:12867, deferred). The plan: W0 parity-anchor an inlined 2×2 +sandwich against jc's certified output (jc stays zero-dep; probe lives in +deepnsm-v2); W1 STOP-gate — do sandwich-propagated and naive-scalar suspicion +rankings of real multi-hop derived beliefs DIVERGE (ρ < 0.95) and does the +sandwich predict S4-guarded error signals better (never the +TD-NARS-REVISION-UNGUARDED confidences — fence, not target); W2 optional +DTO-only `Option` (no tenant carve, no layout bump, only if W1 +forces it); W3 the Commit→Plan flip-rate probe on `advance_on_gate` (the +epistemic-pothole detector quantified, two-sided: flips concentrate on +flagged chains AND clean chains stay silent); W4 explicit BUY / NO-BUY. +Feeds `mul-calibration-not-verdict-v1`'s thesis with calibration data; +renames nothing (F-MUL-6 block respected). D-MEP-0..4 on STATUS_BOARD. + ## 2026-08-28 — PROPOSED (unbuilt, measure-before-carve): a token value tenant inside the 40,767-triple stream, `.claude/plans/token-value-tenant-v1.md` Operator directive: *"you might need a token value tenant inside the 40k."* diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index 5624e7e2e..179000702 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -1,3 +1,15 @@ +## mul-ewa-trust-propagation-v1 — PROPOSED 2026-08-28 (measure-before-carve) + +Plan: `.claude/plans/mul-ewa-trust-propagation-v1.md`. PLAN/BOARD ONLY. W1 +is the STOP gate; NO-BUY is a valid exit at every gate. + +| D-id | Deliverable | Status | +|---|---|---| +| D-MEP-0 | jc Pillar-6/7 provers green in-checkout + inlined-sandwich parity gate (F-MEP-0 disable-verified) | Queued | +| D-MEP-1 | information probe over real chains: scalar vs sandwich suspicion rankings, divergence + S4-guarded error prediction + shuffle null (F-MEP-1/2/3) | Queued | +| D-MEP-2 | `Option` DTO (ONLY if W1 forces it; no tenant carve; F-MEP-4 consumer-build gate) | Queued | +| D-MEP-3 | `advance_on_gate` Commit→Plan flip-rate probe, two-sided (F-MEP-5) | Queued | +| D-MEP-4 | verdict: BUY / NO-BUY, numbers banked either way | Queued | ## token-value-tenant-v1 — PROPOSED 2026-08-28 (measure-before-carve) Plan: `.claude/plans/token-value-tenant-v1.md`. PLAN/BOARD ONLY. W1 is the diff --git a/.claude/board/SUPERSESSION-INDEX.md b/.claude/board/SUPERSESSION-INDEX.md index 3f1d4115e..ca2aa4bf6 100644 --- a/.claude/board/SUPERSESSION-INDEX.md +++ b/.claude/board/SUPERSESSION-INDEX.md @@ -56,7 +56,7 @@ coverage instead. | `persist_cycle` | BLOCKED | — | 10 | 6 | 5 | | `CognitiveMarkers` | REPURPOSE | `Commit` | 2 | 0 | 0 | | `DominoCascade` | REPURPOSE | `Commit` | 8 | 0 | 0 | -| `GateDecision` | REPURPOSE | — | 25 | 26 | 23 | +| `GateDecision` | REPURPOSE | — | 25 | 27 | 24 | | `GateState` | REPURPOSE | — | 14 | 1 | 1 | | `MergeMode` | REPURPOSE | — | 8 | 12 | 11 | | `ResonanceDto` | REPURPOSE | `PerturbationDto` | 2 | 6 | 5 | @@ -64,7 +64,7 @@ coverage instead. | `CollapseGateEmission` | RETIRE | — | 5 | 12 | 12 | | `ThinkingStyle` | RETIRE-toward-contract | — | 51 | 27 | 24 | -## Table 2 — plans naming a ruled symbol without citing the ruling (69) +## Table 2 — plans naming a ruled symbol without citing the ruling (70) Route is **mechanical triage, not a verdict**: `ARCHIVE?` = the plan's own status says it shipped; `RESCOPE` = it targets a symbol marked RETIRE; `READ` = neither signal fires @@ -89,6 +89,7 @@ a licence to act on it. | **READ** | `mul-calibration-not-verdict-v1` | `GateDecision` | PROPOSAL (unbuilt) — 2026-08-26. PLAN/BOARD | 6/12 | | **READ** | `mul-consumer-build-gate-v1` | `GateDecision` | GATE RUN — 2026-08-27. Discharges D-MCAL-6 a | 3/7 | | **READ** | `mul-consumer-census-v1` | `GateDecision` | MEASUREMENT COMPLETE — 2026-08-27. Measureme | 1/2 | +| **READ** | `mul-ewa-trust-propagation-v1` | `GateDecision` | PROPOSED — PLAN/BOARD ONLY. Measure-before-c | 1/2 | | **READ** | `persistence-artifact-backed-commit-v1` | `persist_cycle` | RATIFIED (operator ruling 2026-08-09). Phase | 0/0 | | **READ** | `r2il-bpe-typed-genetic-recombination-v1` | `GateDecision` | PROPOSAL, §7's three falsifiers now RUN (see | 1/1 | | **READ** | `scientific-kg-substrate-v1` | `GateDecision` | PROPOSED — **scoping doc**, no code. Records | 8/9 | @@ -151,5 +152,5 @@ a licence to act on it. - **ARCHIVE?**: 0 - **RESCOPE**: 54 -- **READ**: 15 +- **READ**: 16 - ruled symbols tracked: 14 diff --git a/.claude/plans/mul-ewa-trust-propagation-v1.md b/.claude/plans/mul-ewa-trust-propagation-v1.md new file mode 100644 index 000000000..26635e46f --- /dev/null +++ b/.claude/plans/mul-ewa-trust-propagation-v1.md @@ -0,0 +1,177 @@ +# mul-ewa-trust-propagation-v1 — trust is point-wise today; the sandwich is its lawful propagation operator + +> **Status: PROPOSED — PLAN/BOARD ONLY. Measure-before-carve.** No contract +> change, no wiring, until W1's numbers land (the STOP rule in §4). Same +> ratified shape as `token-value-tenant-v1` (#1072). +> **Operator directive (2026-08-28):** *"cognitive speedbumps" > check +> epistemic potholes > revision in kanban y rubicon model* + *"check for +> synergies MUL <> EWA"* + *"please explore for possible integration plan."* +> **Arc:** D-MCAL (#1065–#1070) → `mul-calibration-not-verdict-v1` (thesis +> untouched by this plan; this plan produces CALIBRATION DATA for it) → this. + +## §0 The thesis, one paragraph + +MUL answers "how much do I trust HERE": `MulAssessment` is scalar-only — +`TrustQualia { value: f64, texture }`, `DkPosition`, `Homeostasis`, +`free_will_modifier` (`contract/src/mul.rs:50-61`, verified at HEAD). Nothing +in the workspace answers "what does that trust become N hops away." jc's +certified EWA sandwich — `Σ_path = M_n·…·M_1·Σ_0·M_1ᵀ·…·M_nᵀ`, Pillar 6 +(2×2, tightness 1.467× ≤ 1.75) / Pillar 7 (3×3, PSD ≥ 0.999) — is exactly +that operator: *filling indirect unknowns*, applied to trust itself. And the +kanban×Rubicon model already has the revision exit this would calibrate: +`KanbanColumn::Plan = 4` — "re-enter Planning **carrying the witness**" — the +epistemic-pothole handler. The question this plan measures before building +anything: **does sandwich-propagated uncertainty rank multi-hop derived +beliefs differently — and better — than the naive scalar decay every stack +defaults to?** If not, NO-BUY, numbers banked. + +## §1 What is established (verified at HEAD this session, file:line) + +- **Zero MUL↔EWA wiring exists.** No `jc`/`ewa`/`sandwich` reference in + `contract/src/mul.rs` or `planner/src/mul/*`; no MUL reference in `jc` + (grep hits are the substring "mul" in multiplication contexts). No + variance/covariance/propagation surface anywhere in MUL. +- **jc is zero-dep BY CONSTITUTION** (`crates/jc/Cargo.toml`: "Default build + is zero-dep — honors the standalone constitution"; `sigker` is the one + optional feature). A probe consuming real chains therefore CANNOT live in + jc — see §3 placement. +- **The sandwich is certified, twice**: `jc::ewa_sandwich` (Pillar 6, 2×2 — + "certified by `cargo run --release --example prove_it` at tightness + 1.467× ≤ 1.75", cited in `jc/examples/splat_to_ewa_bridge.rs`) and + `jc::ewa_sandwich_3d` (Pillar 7: PSD-preservation ≥ 0.999, Smith-1961 + closed-form eigendecomposition, CV formula `√(2/n)·√(1+3σ²n)`). +- **The revision exit exists and is forward-only-preserving**: + `KanbanColumn::{Planning=0, CognitiveWork=1, Evaluation=2}` + + 3-way terminals `{Commit=3 (DECLARED-UNWIRED calcify), Plan=4 (re-enter + Planning carrying the witness), Prune=5 (Libet free-won't)}`; + `advance_on_gate(GateDecision)` gates on LOCAL axes only + (`GateDecision::from_axes(TrustTexture, FlowState)`, #1068). +- **Lawful-composition tools ship one per layer, unconnected**: the S4 + disjointness guard (`deepnsm-v2::belief::BeliefArena::revise_at` — + DISJOINT stamps → `Revised{synthesis_c, depth=|f₁−f₂|}`, OVERLAPPING → + Choice); the EWA sandwich (jc); Fisher-z averaging + (`contract::distance::similarity_z`). Same theme three times: uncertainty + composes lawfully or it inflates. +- **The composition-legality question is ALREADY assigned to this pairing**: + EPIPHANIES:12867 — where roll-up applies "is a MATH question OWNED BY THE + JC CRATE (jirak/pearl/ewa_sandwich), **deferred**." This plan produces + measured INPUT to that question; it does not answer it. +- **Real multi-hop data exists, with a poisoned-well caveat**: the KJV + arena carries real derivation chains (92,464 derived statements, F1 + EPIPHANIES:10383; 35,613 pronoun bindings with per-hop SelectionalFit + margins). BUT `TD-NARS-REVISION-UNGUARDED` rules the planner's + revise-all-history confidences "suspect upward" — so ground truth in W1 + comes ONLY from the S4-guarded arena, never the unguarded paths. + +## §2 Delineation — what this plan deliberately does NOT collide with + +| neighboring artifact | relationship | +|---|---| +| `mul-calibration-not-verdict-v1` (PROPOSAL, live) | **Feeds it, never redefines it.** That plan owns MUL's output identity ("calibrates, does not adjudicate"). Path-propagated Σ is calibration DATA for that thesis. No output vocabulary is touched here. | +| `ISS-MUL-GATE-NAMED-FOR-THE-WRONG-LAYER` + F-MUL-6 | **Untouched.** The rename stays blocked on F-MUL-6; this plan renames nothing and adds no gate variant. | +| `dialectic-engine-v1` (ACTIVE) | The S4-guarded `BeliefArena` is this plan's INSTRUMENT (ground-truth source), never modified. | +| `TD-NARS-REVISION-UNGUARDED` | **Not bundled.** Its payment path is already prescribed (thread the stamp guard through ndarray's callers). This plan only inherits its LESSON: unguarded confidences are not ground truth. | +| tarski-markov-hhtl-seam register (HELD) | Untouched. No fold, no accumulation mechanism, no rung/stamp delegation proposed. | +| `token-value-tenant-v1` (#1072) | Sibling shape, disjoint domain. Shared discipline only. | + +## §3 The carrier decision (deferred to W1, both candidates named) + +Minimal carrier for a trust second-moment, BOTH additive-DTO-only — **no +`ValueTenant` carve, no layout change, no `ENVELOPE_LAYOUT_VERSION` bump is +proposed anywhere in this plan**: + +- **K1 — `TrustSigma { s11, s12, s22 }: Option<_>` on `TrustQualia`** — a 2×2 + SPD over (value, calibration). Pillar 6 is literally the certified 2×2 + case. `None` ⇒ today's behavior byte-identical (zero-fallback). +- **K2 — per-hop Σ derived, never stored** — the probe computes Σ from + existing per-hop quantities (`NarsTruth{frequency,confidence}` or + SelectionalFit margin × OCR conf) and only the READ is new. If W1 shows K2 + suffices, K1 is never minted at all. + +**Probe placement**: `deepnsm-v2/examples/` (sole dep = contract; the real +chains + S4 arena live there). The 2×2 sandwich math is INLINED in the +probe (~15 lines) — jc's own precedent (`ewa_sandwich_3d` is "self-contained +in f64 … without any dependency on the graphics crate") — and W0 gates the +inlined math against jc's certified output so the math is never silently +forked. + +## §4 Waves — Opus filigree / Sonnet grind / Haiku contract-gated churn + +**W0 — parity anchors (Sonnet).** +1. Run jc's Pillar-6/7 provers green in THIS checkout (`prove_it`); bank the + tightness/PSD numbers. +2. Inline-sandwich parity gate: the probe's 2×2 math must reproduce jc's + certified propagation on identical seeded inputs. + - F-MEP-0 (disable-verified): perturb one matrix entry in the inlined + sandwich → parity goes red. If this cannot be made to fail, the gate + is vacuous and W0 is not done. + +**W1 — the information probe (STOP GATE for everything below; Sonnet arms, +Opus adjudication).** Over real multi-hop chains (KJV derivation chains +and/or anaphora chains with per-hop margins), rank derived beliefs by +suspicion under two arms: +- (a) **scalar baseline** — naive decay (product / min of per-hop trust), + the default every stack uses; +- (b) **EWA arm** — per-hop 2×2 Σ (K2 derivation), sandwich-propagated, + read out as a scalar (largest eigenvalue or trace). +- F-MEP-1 (anti-vacuity): the two rankings must DIVERGE non-trivially + (Spearman ρ < 0.95 over the suspicion ordering). Identical rankings ⇒ + the seam buys nothing ⇒ **NO-BUY immediately**, numbers banked. +- F-MEP-2 (the buy signal): on the divergent subset, arm (b) must predict + an INDEPENDENT error signal better than (a). Error signal = S4-guarded + arena events only (Choice-on-overlap hits, revision conflicts, + `depth=|f₁−f₂|` spikes) — never unguarded-path confidences + (TD-NARS-REVISION-UNGUARDED's lesson as a methodological fence). +- F-MEP-3 (null control, `shuffle_beliefs_null` precedent): arm (b)'s + advantage must beat a stamp-shuffled null on the same chains. + +**W2 — the carrier (GATED on W1 BUY; Opus review, Sonnet transcription).** +Mint K1 ONLY if W1 showed derived-per-read Σ (K2) insufficient (e.g. a +consumer needs Σ across a boundary where the hop quantities are gone). +Additive `Option` on the contract DTO; `None` byte-identical to +today (zero-fallback test); SPD-validity guard (refuse, never clamp, a +non-PSD write — jc Pillar 6's own invariant applied at the type boundary). +- F-MEP-4: field-isolation — every existing MUL consumer compiles and + behaves identically with `None`. Disable: make the field non-optional → + the consumer-build gate (the F-MUL-6 method) must go red. + +**W3 — path-aware gating probe (GATED on W2 or K2-suffices; Sonnet).** +`advance_on_gate` arm comparison on the SAME chains: gate decisions with +local axes vs with propagated Σ folded into `TrustTexture`. The number +that matters: **the Commit→Plan flip rate** — how many multi-hop beliefs +that gate Commit under local trust flip to Plan (re-enter Planning carrying +the witness) under propagated uncertainty. That is the epistemic-pothole +detector, quantified in the kanban×Rubicon model's own vocabulary. +- F-MEP-5 (two-sided): flips must CONCENTRATE on chains W1's error signal + flagged (not uniform noise), AND a can-stay-silent half — short/clean + chains must not flip (a gate that flips everything is the 150/150 + defect). +- **No wiring lands in W3.** It is a probe against the existing + `advance_on_gate`; changing the gate's signature or default is explicitly + out of scope for v1. + +**W4 — verdict (Opus).** Explicit **BUY / NO-BUY** against W1-W3's numbers. +NO-BUY is a valid exit at every gate; numbers are banked either way. BUY's +consequence is also bounded: a follow-up plan for the actual gate wiring — +never wiring-by-momentum inside this one. + +## §5 Fences + +1. jc stays zero-dep (probe lives in deepnsm-v2; math inlined + W0-gated). +2. No `ValueTenant` carve, no layout change, no version bump — DTO-only, and + only if W1 forces it. +3. The gate RENAME stays blocked on F-MUL-6 — untouched. +4. `TD-NARS-REVISION-UNGUARDED` is paid on its own prescribed path — not + here; here it is only a fence on ground-truth selection. +5. The tarski register stays HELD — no fold/accumulation mechanism. +6. `KanbanColumn::Commit`'s unwired calcify is not this plan's to wire. +7. `mul-calibration-not-verdict-v1`'s thesis is consumed, not amended. +8. No GPU/wgpu; the sandwich is ~15 scalar lines. + +## §6 Board hygiene (same commit as this file) + +`INTEGRATION_PLANS.md` PREPEND; `STATUS_BOARD.md` D-MEP-0..4 rows (Queued); +`SUPERSESSION-INDEX.md` regenerated. Cross-refs: EPIPHANIES:12867 (the +deferred jc ownership this plan feeds), E-3DGS-MU-HYDRATION-1 (the EWA- +semiring DROP this plan does not resurrect), `token-value-tenant-v1` +(the ratified measure-before-carve shape). From 3536898eaaf50841a01d2879fe86fc89e1f4093e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 23:31:35 +0000 Subject: [PATCH 02/13] plan(mul-ewa): address three codex P1 findings, all confirmed at HEAD 1. W3's headline metric was UNMEASURABLE. "Commit->Plan flip rate" cannot be observed through advance_on_gate: advance() takes "the first non-Prune successor" and Evaluation's next_phases() is [Commit, Plan, Prune], so it returns Commit always; veto() gives Prune; Hold gives None. Reachable set is {Commit, Prune, None}. Re-scoped to the Commit->{Hold,Prune} flip rate. The finding is worth more than the metric it cost, so it is recorded rather than papered over: KanbanColumn::Plan -- the revision exit this whole plan is motivated by -- is legal in the DAG but emitted by NO named routing primitive. Whether that is intentional Rubicon discipline or an omission is an open operator question, logged as ISS-KANBAN-PLAN-EXIT-HAS-NO-NAMED-ROUTE. No code depends on its resolution. 2. Option does NOT confer source compatibility. contract::mul::TrustQualia is a pub struct with pub fields and no #[non_exhaustive], constructed by literal in-tree (exploration.rs:907, mul.rs:603, :887) and externally; any added field breaks them with E0063. W2 is now a construction-path decision (non_exhaustive + ctor / side table / K2-only, with K2-only the default), and F-MEP-4 is two-sided so the gate must prove it can SEE the breakage. Also names the second TrustQualia (planner/mul/trust.rs). 3. K2 was underspecified. A symmetric 2x2 needs three values and the sandwich needs a hop transform M_k, while NarsTruth supplies two scalars. Defining (Sigma_0, M_k) is now D-MEP-1's first deliverable, with F-MEP-1b requiring the probe to REPORT algebraic equivalence when M_k is isotropic rather than dress it up as a difference. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1 --- .claude/board/ISSUES.md | 23 +++++ .claude/board/SUPERSESSION-INDEX.md | 2 +- .claude/plans/mul-ewa-trust-propagation-v1.md | 96 ++++++++++++++++--- 3 files changed, 106 insertions(+), 15 deletions(-) diff --git a/.claude/board/ISSUES.md b/.claude/board/ISSUES.md index f8aabdb72..072768542 100644 --- a/.claude/board/ISSUES.md +++ b/.claude/board/ISSUES.md @@ -1,3 +1,26 @@ +## ISS-KANBAN-PLAN-EXIT-HAS-NO-NAMED-ROUTE — `KanbanColumn::Plan` is legal in the DAG but no routing primitive emits it (2026-08-28) + +Surfaced by a codex P1 on #1074 and confirmed by reading +`contract/src/kanban.rs` at HEAD. `advance()` is documented as "the first +non-`Prune` successor"; `Evaluation::next_phases()` is `[Commit, Plan, +Prune]`, so `advance()` returns **`Commit`, always**. `veto()` returns +`Prune`. `Hold` returns `None`. Therefore `advance_on_gate`'s reachable set is +exactly `{Commit, Prune, None}` and **`Plan = 4` — "re-enter Planning carrying +the witness", the revision exit — is emitted by no named primitive**; only a +caller hand-walking `next_phases()` can reach it. + +**Open question, for the operator, not for a plan to settle:** is this +intentional Rubicon discipline (revision is a deliberate act, never a gate's +automatic output) or an omission (`revise()` missing beside `advance()` / +`veto()`)? Either answer is defensible; the current state is that the +transition the kanban×Rubicon model names as its re-plan exit has no ergonomic +route, while `Prune` — its sibling terminal — got one in D-MCAL-4. + +**Consequence already absorbed:** `mul-ewa-trust-propagation-v1` W3 was +re-scoped from a "Commit→Plan flip rate" (unmeasurable) to a +"Commit→{Hold,Prune} flip rate" before any work started. No code depends on +this issue's resolution today. + # Issues Log — Open + Resolved (double-entry, append-only) ## ISS-F-MUL-6-HALF-BUILT (2026-08-27) — OPEN diff --git a/.claude/board/SUPERSESSION-INDEX.md b/.claude/board/SUPERSESSION-INDEX.md index ca2aa4bf6..437a3015c 100644 --- a/.claude/board/SUPERSESSION-INDEX.md +++ b/.claude/board/SUPERSESSION-INDEX.md @@ -89,7 +89,7 @@ a licence to act on it. | **READ** | `mul-calibration-not-verdict-v1` | `GateDecision` | PROPOSAL (unbuilt) — 2026-08-26. PLAN/BOARD | 6/12 | | **READ** | `mul-consumer-build-gate-v1` | `GateDecision` | GATE RUN — 2026-08-27. Discharges D-MCAL-6 a | 3/7 | | **READ** | `mul-consumer-census-v1` | `GateDecision` | MEASUREMENT COMPLETE — 2026-08-27. Measureme | 1/2 | -| **READ** | `mul-ewa-trust-propagation-v1` | `GateDecision` | PROPOSED — PLAN/BOARD ONLY. Measure-before-c | 1/2 | +| **READ** | `mul-ewa-trust-propagation-v1` | `GateDecision` | PROPOSED — PLAN/BOARD ONLY. Measure-before-c | 1/3 | | **READ** | `persistence-artifact-backed-commit-v1` | `persist_cycle` | RATIFIED (operator ruling 2026-08-09). Phase | 0/0 | | **READ** | `r2il-bpe-typed-genetic-recombination-v1` | `GateDecision` | PROPOSAL, §7's three falsifiers now RUN (see | 1/1 | | **READ** | `scientific-kg-substrate-v1` | `GateDecision` | PROPOSED — **scoping doc**, no code. Records | 8/9 | diff --git a/.claude/plans/mul-ewa-trust-propagation-v1.md b/.claude/plans/mul-ewa-trust-propagation-v1.md index 26635e46f..691463cc3 100644 --- a/.claude/plans/mul-ewa-trust-propagation-v1.md +++ b/.claude/plans/mul-ewa-trust-propagation-v1.md @@ -84,10 +84,33 @@ proposed anywhere in this plan**: SPD over (value, calibration). Pillar 6 is literally the certified 2×2 case. `None` ⇒ today's behavior byte-identical (zero-fallback). - **K2 — per-hop Σ derived, never stored** — the probe computes Σ from - existing per-hop quantities (`NarsTruth{frequency,confidence}` or - SelectionalFit margin × OCR conf) and only the READ is new. If W1 shows K2 + existing per-hop quantities and only the READ is new. If W1 shows K2 suffices, K1 is never minted at all. +**K2 is UNDERSPECIFIED as stated, and defining it is D-MEP-1's first +deliverable, not an assumed input** (codex P1, #1074): a symmetric 2×2 Σ has +**three** free values (`s11, s12, s22`) and sandwich propagation additionally +needs a **hop transform `M_k`** — while `NarsTruth` supplies only TWO scalars +(`frequency`, `confidence`) and the alternative source (SelectionalFit margin) +is a single scalar. So W1 cannot begin by "computing Σ"; it must first fix, +and write down, a construction: + +- **Σ₀ (the seed):** which two quantities are the axes, and what the + off-diagonal `s12` means. A diagonal seed (`s12 = 0`) is the honest default + — it asserts no measured correlation — and must be declared as such rather + than smuggled in. +- **`M_k` (the hop transform):** what a hop DOES to uncertainty. Candidates: + `M_k = √(per-hop trust)·I` (isotropic decay, the closest analogue of the + scalar baseline and therefore the fairest control), or a margin-scaled + anisotropic form. Whichever is chosen, the scalar baseline in W1(a) MUST be + the one that arm (b) collapses to when `M_k` is isotropic — otherwise the + comparison measures the construction, not the propagation. +- **F-MEP-1b (construction-honesty gate):** if arm (b) with an isotropic + `M_k` and a diagonal Σ₀ is algebraically equivalent to arm (a), the probe + MUST report that equivalence rather than a spurious difference — and any + measured divergence then comes only from a NON-isotropic `M_k`, which is + the claim actually under test. + **Probe placement**: `deepnsm-v2/examples/` (sole dep = contract; the real chains + S4 arena live there). The 2×2 sandwich math is INLINED in the probe (~15 lines) — jc's own precedent (`ewa_sandwich_3d` is "self-contained @@ -128,20 +151,65 @@ suspicion under two arms: **W2 — the carrier (GATED on W1 BUY; Opus review, Sonnet transcription).** Mint K1 ONLY if W1 showed derived-per-read Σ (K2) insufficient (e.g. a consumer needs Σ across a boundary where the hop quantities are gone). -Additive `Option` on the contract DTO; `None` byte-identical to -today (zero-fallback test); SPD-validity guard (refuse, never clamp, a -non-PSD write — jc Pillar 6's own invariant applied at the type boundary). -- F-MEP-4: field-isolation — every existing MUL consumer compiles and - behaves identically with `None`. Disable: make the field non-optional → - the consumer-build gate (the F-MUL-6 method) must go red. +`Option` on the contract DTO; SPD-validity guard (refuse, never +clamp, a non-PSD write — jc Pillar 6's own invariant applied at the type +boundary). + +> **⚠ `Option` DOES NOT make this additive for SOURCE compatibility** (codex +> P1, #1074 — verified at HEAD). `contract::mul::TrustQualia` is a `pub +> struct` with `pub` fields and **no `#[non_exhaustive]`**, and it is +> constructed by struct literal in-tree (`contract/src/exploration.rs:907`, +> `contract/src/mul.rs:603`, `:887`) as well as by external consumers. Adding +> ANY field — optional or not — breaks every such literal with `E0063: +> missing field`. `None` gives *behavioural* compatibility, never *source* +> compatibility. Note also there are **two** `TrustQualia` types (the contract +> one and `planner/src/mul/trust.rs:15`); W2 must state which it carves. +> +> So W2's carve is not "add a field" but a construction-path decision, and it +> is part of the deliverable: +> (a) `#[non_exhaustive]` + a constructor (itself breaking for existing +> external literals — it buys future additivity, not this one); +> (b) a side table keyed by assessment identity (zero DTO change); +> (c) **K2-only — no carrier at all**, which W1 may well render sufficient. +> **(c) is the default; (a)/(b) require W1 to have shown a boundary where the +> hop quantities are genuinely gone.** + +- F-MEP-4: consumer-build gate (the F-MUL-6 method — a real compile of every + live consumer, never a grep). Two-sided: the chosen path must compile every + existing consumer UNCHANGED, and the disable — adding the field bare to + `TrustQualia` — must FAIL that build with `E0063`, proving the gate can + see the breakage it exists to catch. **W3 — path-aware gating probe (GATED on W2 or K2-suffices; Sonnet).** -`advance_on_gate` arm comparison on the SAME chains: gate decisions with -local axes vs with propagated Σ folded into `TrustTexture`. The number -that matters: **the Commit→Plan flip rate** — how many multi-hop beliefs -that gate Commit under local trust flip to Plan (re-enter Planning carrying -the witness) under propagated uncertainty. That is the epistemic-pothole -detector, quantified in the kanban×Rubicon model's own vocabulary. + +> **⚠ CORRECTED BEFORE ANY WORK STARTED (codex P1, #1074 — confirmed by +> reading the source).** This wave originally named "**the Commit→Plan flip +> rate**" as its headline metric. That metric is **unmeasurable through +> `advance_on_gate`**, because `Plan` is structurally unreachable from it: +> `advance()` is *"the first non-`Prune` successor"* and `Evaluation`'s +> `next_phases()` is `[Commit, Plan, Prune]`, so `advance()` returns +> **`Commit`, always**; `veto()` returns `Prune`; `Hold` returns `None`. The +> reachable set is therefore exactly `{Commit, Prune, None}`. +> +> **The finding this surfaces is worth more than the metric it cost**, and it +> is recorded here rather than papered over: **`Plan = 4` — the revision exit, +> "re-enter Planning carrying the witness", the very transition this whole +> plan is motivated by — is legal in the DAG but reachable by NO named routing +> primitive.** `advance`/`veto`/`advance_on_gate` cannot emit it; only a +> caller hand-walking `next_phases()` can. Whether that gap is intentional +> (Rubicon-forward discipline: revision must be a deliberate act, never a +> gate's automatic output) or an omission is **an open question for the +> operator — not something this plan resolves or builds.** Logged as +> `ISS-KANBAN-PLAN-EXIT-HAS-NO-NAMED-ROUTE`. + +Re-scoped metric, measurable against the surface that actually exists: +`advance_on_gate` arm comparison on the SAME chains — local axes vs +propagated Σ folded into `TrustTexture` — measuring the **Commit→{Hold, +Prune} flip rate**: how many multi-hop beliefs that gate `Commit` under local +trust instead hold or veto under propagated uncertainty. That is the +epistemic-pothole detector expressed in the vocabulary the routing primitives +can actually speak. Reaching `Plan` would require the open question above to +be answered first. - F-MEP-5 (two-sided): flips must CONCENTRATE on chains W1's error signal flagged (not uniform noise), AND a can-stay-silent half — short/clean chains must not flip (a gate that flips everything is the 150/150 From 96581524a5d4abdb8d0dd5262af86180006df3b1 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 23:36:01 +0000 Subject: [PATCH 03/13] plan(mul-ewa): predeclare the statistical protocol; correct symbiont citation and the "lawful" overclaim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses the remaining CodeRabbit findings on #1074, plus an operator correction. OPERATOR CORRECTION (symbiont): my planner Rubicon-compliance report cited crates/symbiont/src/kanban_loop.rs as evidence. symbiont is DEPRECATED (operator no-go 2026-08-18) -- a dormant excluded crate, never a live surface -- so citing it as evidence about live behaviour was the same error class as citing a stale doc. Re-verified on live surfaces only: the verdict HOLDS and is stronger than first stated. lance-graph-planner/src/persist_sink.rs:707-724 guards three deep before mutating -- OwnerMismatch, then StalePhase, then the CHECKED try_advance_phase whose Err becomes PersistError::Illegal. The unchecked advance_phase at :776 is inside #[cfg(test)] (block opens at :731). owner_adapter.rs does not mutate at all. Wording (CodeRabbit Major): the title asserted the sandwich is the "lawful" propagation operator while §1 defers composition legality to jc. Retitled to CANDIDATE and added a wording-discipline note: "certified" is reserved for jc's numerical properties (PSD, tightness), never for the semantic claim under test. W0 (CodeRabbit Major): a contract-only probe cannot call jc::ewa_sandwich, so the stated parity gate was unexecutable. Replaced with a checked-fixture path -- a jc-side generator emits seeded (Sigma_0, M_k, Sigma_out) triples carrying the jc commit SHA; the probe diffs against that. jc's zero-dep constitution stays intact. W1 (CodeRabbit Major): the protocol is now PREDECLARED as a table -- cohort (hop-length >= 2), ONE readout (trace, eigenvalue explicitly not evaluated), tie convention, missing-data exclusion with reporting, n >= 200 or UNDERPOWERED-and-stop, held-out split, AUC metric, and a BUY threshold of dAUC >= 0.05 clearing the null by >= 2 sigma. "Better" has no meaning outside that table. W3 (CodeRabbit Major): the Sigma -> TrustTexture mapping is predeclared and single -- trace percentiles (50th/90th) from the W1 held-out half, ties to the lower-suspicion texture, Underconfident never produced, FlowState held fixed so only the input under test varies. Board: MD058 blank line after the D-MEP table; D-MEP-3 row re-worded to Commit->{Hold,Prune} to match the corrected W3. NOT done, deliberately: CodeRabbit asks to change .claude/tools/supersession_index.py's D-id range handling. That generator is shared by every plan in the repo; changing it as a drive-by inside a plan PR would silently re-scope every other plan's coverage number. Raised for its own PR instead. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1 --- .claude/board/STATUS_BOARD.md | 3 +- .claude/plans/mul-ewa-trust-propagation-v1.md | 51 ++++++++++++++++--- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index 179000702..4913f70fa 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -8,8 +8,9 @@ is the STOP gate; NO-BUY is a valid exit at every gate. | D-MEP-0 | jc Pillar-6/7 provers green in-checkout + inlined-sandwich parity gate (F-MEP-0 disable-verified) | Queued | | D-MEP-1 | information probe over real chains: scalar vs sandwich suspicion rankings, divergence + S4-guarded error prediction + shuffle null (F-MEP-1/2/3) | Queued | | D-MEP-2 | `Option` DTO (ONLY if W1 forces it; no tenant carve; F-MEP-4 consumer-build gate) | Queued | -| D-MEP-3 | `advance_on_gate` Commit→Plan flip-rate probe, two-sided (F-MEP-5) | Queued | +| D-MEP-3 | `advance_on_gate` Commit→{Hold,Prune} flip-rate probe, two-sided (F-MEP-5) | Queued | | D-MEP-4 | verdict: BUY / NO-BUY, numbers banked either way | Queued | + ## token-value-tenant-v1 — PROPOSED 2026-08-28 (measure-before-carve) Plan: `.claude/plans/token-value-tenant-v1.md`. PLAN/BOARD ONLY. W1 is the diff --git a/.claude/plans/mul-ewa-trust-propagation-v1.md b/.claude/plans/mul-ewa-trust-propagation-v1.md index 691463cc3..545acbb2c 100644 --- a/.claude/plans/mul-ewa-trust-propagation-v1.md +++ b/.claude/plans/mul-ewa-trust-propagation-v1.md @@ -1,4 +1,4 @@ -# mul-ewa-trust-propagation-v1 — trust is point-wise today; the sandwich is its lawful propagation operator +# mul-ewa-trust-propagation-v1 — trust is point-wise today; the sandwich is a CANDIDATE propagation operator > **Status: PROPOSED — PLAN/BOARD ONLY. Measure-before-carve.** No contract > change, no wiring, until W1's numbers land (the STOP rule in §4). Same @@ -14,10 +14,14 @@ MUL answers "how much do I trust HERE": `MulAssessment` is scalar-only — `TrustQualia { value: f64, texture }`, `DkPosition`, `Homeostasis`, `free_will_modifier` (`contract/src/mul.rs:50-61`, verified at HEAD). Nothing -in the workspace answers "what does that trust become N hops away." jc's -certified EWA sandwich — `Σ_path = M_n·…·M_1·Σ_0·M_1ᵀ·…·M_nᵀ`, Pillar 6 -(2×2, tightness 1.467× ≤ 1.75) / Pillar 7 (3×3, PSD ≥ 0.999) — is exactly -that operator: *filling indirect unknowns*, applied to trust itself. And the +in the workspace answers "what does that trust become N hops away." jc's EWA +sandwich — `Σ_path = M_n·…·M_1·Σ_0·M_1ᵀ·…·M_nᵀ`, Pillar 6 (2×2, tightness +1.467× ≤ 1.75) / Pillar 7 (3×3, PSD ≥ 0.999) — is the **candidate** for that +operator: *filling indirect unknowns*, applied to trust itself. +**Wording discipline, held throughout** (CodeRabbit, #1074): "certified" +applies ONLY to jc's *numerical* properties (PSD-preservation, tightness), +never to the *semantic* claim that trust composes this way — that claim is +exactly what EPIPHANIES:12867 defers to jc, and what W1 measures. And the kanban×Rubicon model already has the revision exit this would calibrate: `KanbanColumn::Plan = 4` — "re-enter Planning **carrying the witness**" — the epistemic-pothole handler. The question this plan measures before building @@ -148,6 +152,26 @@ suspicion under two arms: - F-MEP-3 (null control, `shuffle_beliefs_null` precedent): arm (b)'s advantage must beat a stamp-shuffled null on the same chains. +**The statistical protocol is PREDECLARED — every value below is fixed +BEFORE the probe runs** (CodeRabbit, #1074: unspecified choices "can be made +after results and can produce a spurious BUY"). This block is the +pre-registration; changing any of it after seeing numbers invalidates the +run and requires a re-pin with the change stated: + +| knob | predeclared value | +|---|---| +| cohort | every S4-guarded arena chain of hop-length **≥ 2** (single-hop chains cannot distinguish propagation from its seed) | +| readout | **trace(Σ)** — ONE scalar, fixed. (Largest eigenvalue is NOT evaluated; picking between them post-hoc is the defect this row exists to prevent.) | +| ties | equal readout ⇒ equal rank (average-rank convention, standard for Spearman) | +| missing hop data | chain EXCLUDED wholesale, never imputed; the excluded count is REPORTED | +| minimum sample | **n ≥ 200** qualifying chains; below that the probe reports UNDERPOWERED and stops — that is a valid, honest exit | +| split | metric fixed on a held-out half; the F-MEP-2 threshold is evaluated ONCE on the other half, no re-fitting | +| comparison metric | AUC of suspicion-rank vs the binary S4 error signal | +| BUY threshold | ΔAUC **≥ 0.05** over arm (a) AND clearing the F-MEP-3 null by ≥ 2σ of the shuffle distribution | + +Anything short of BOTH thresholds is NO-BUY. "Better" has no meaning in this +plan outside this table. + **W2 — the carrier (GATED on W1 BUY; Opus review, Sonnet transcription).** Mint K1 ONLY if W1 showed derived-per-read Σ (K2) insufficient (e.g. a consumer needs Σ across a boundary where the hop quantities are gone). @@ -202,10 +226,23 @@ boundary). > operator — not something this plan resolves or builds.** Logged as > `ISS-KANBAN-PLAN-EXIT-HAS-NO-NAMED-ROUTE`. +**The Σ → `TrustTexture` mapping is PREDECLARED, deterministic, and single** +(CodeRabbit, #1074 — "different mappings produce different flip rates"): +using the same `trace(Σ)` readout W1 fixed, and the W1 held-out half to set +its cut points, `TrustTexture` is `Calibrated` below the 50th percentile of +the clean-chain trace distribution, `Uncertain` between the 50th and 90th, +and `Overconfident` at or above the 90th. Ties resolve to the LOWER-suspicion +texture (the conservative direction: a tie must not manufacture a flip). +`Underconfident` is never produced — nothing in a propagated covariance +distinguishes it from `Calibrated`, and inventing that distinction is exactly +the coordinate-fabrication the census measured. `FlowState` is held FIXED at +its locally-assessed value in both arms, so the only varying input is the one +under test. No other mapping is evaluated. + Re-scoped metric, measurable against the surface that actually exists: `advance_on_gate` arm comparison on the SAME chains — local axes vs -propagated Σ folded into `TrustTexture` — measuring the **Commit→{Hold, -Prune} flip rate**: how many multi-hop beliefs that gate `Commit` under local +propagated Σ folded into `TrustTexture` per the mapping above — measuring the +**Commit→{Hold,Prune} flip rate**: how many multi-hop beliefs that gate `Commit` under local trust instead hold or veto under propagated uncertainty. That is the epistemic-pothole detector expressed in the vocabulary the routing primitives can actually speak. Reaching `Plan` would require the open question above to From e7c569dd4c0de767147f793db8ddc67cd951636e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 23:42:12 +0000 Subject: [PATCH 04/13] plan(mul-ewa) + board: fix two live findings; file three defects that merged unreviewed in #1072 LIVE FINDINGS on this PR (both CodeRabbit, both verified correct): 1. The scalar baseline is now PREDECLARED as the PRODUCT of per-hop trust, with `min` explicitly rejected. This is forced by the math, not chosen: under the control form M_k = sqrt(t_k)*I the sandwich gives Sigma_n = (prod t_k)*Sigma_0, so trace scales by the product. Baselining against `min` would make arm (b) differ from arm (a) for a reason unrelated to propagation -- a manufactured BUY. Readout normalization fixed with it: both arms report trace(Sigma_n)/trace(Sigma_0), unitless, one scale. 2. The {Commit, Prune, None} reachable set is now qualified by STARTING PHASE -- it holds from Evaluation, which is where W3 measures and the only phase whose successors include Plan at all. From other phases Flow yields that phase's own first non-Prune successor. Same qualification added to the ISSUES entry, which had inherited the imprecision. DEFECTS THAT MERGED UNREVIEWED (#1072), now filed so they cannot be implemented silently. All three were found by reviews that landed after the merge; I had reported "green, no open threads" from a check-runs poll taken before the reviewers finished writing. - ISS-TOKEN-TENANT-16-COLLIDES-WITH-HOLEV3 (BLOCKS D-TVT-2): INTEGRATION_PLANS.md:653 records HoleV3 = ValueTenant 16 as a hard blocker; the merged plan assigns Token = 16. Two tenants claim the same discriminant on main. Root cause worth generalizing: the plan checked that BoardAggregates re-bases -- the reservation adjacent to the enum -- and never swept the board for other pending claims on that ordinal. - ISS-TVT-3-DISABLE-RUN-IS-VACUOUS: verify_layout() inspects only the three fixed NODE_ROW_COLUMNS entries, never the nested tenant descriptors, so F-TVT-3's prescribed disable cannot fail. A vacuous falsifier shipped inside a plan that invokes the falsifiability rule. - ISS-TVT-HYDRATION-REGION-CLAIM-WRONG: the plan says from_env() returns None on a missing AWS_DEFAULT_REGION; env.rs:56-59 defaults it to "auto". I read and quoted that code correctly in session and then wrote the plan against it wrongly. Same entry records the lower-severity remainder (candidate-B continuation contract, missing W1/W4 thresholds, mint_for vs "No V1 mints"). Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1 --- .claude/board/ISSUES.md | 61 +++++++++++++++++-- .claude/plans/mul-ewa-trust-propagation-v1.md | 31 +++++++--- 2 files changed, 78 insertions(+), 14 deletions(-) diff --git a/.claude/board/ISSUES.md b/.claude/board/ISSUES.md index 072768542..cb5c73010 100644 --- a/.claude/board/ISSUES.md +++ b/.claude/board/ISSUES.md @@ -1,13 +1,66 @@ +## ISS-TOKEN-TENANT-16-COLLIDES-WITH-HOLEV3 — `token-value-tenant-v1` (merged #1072) assigns an ordinal already reserved (2026-08-28) + +**BLOCKS D-TVT-2.** `.claude/plans/token-value-tenant-v1.md` §2 assigns +`ValueTenant::Token = 16` and re-bases only `BoardAggregates` to 17. But +`INTEGRATION_PLANS.md:653` already records **`HoleV3 = ValueTenant 16` as a +hard blocker**, and `:726` repeats it. Two tenants claim discriminant 16 on +`main` today. Found by a CodeRabbit review that landed AFTER #1072 merged. + +**Do not implement D-TVT-2 until the reservation is adjudicated.** Either +`HoleV3` rebases, or `Token` takes the next free ordinal and +`BoardAggregates` rebases accordingly — an operator/board call, not a +plan-local one. Whichever way it goes, the descriptor's offset stays DERIVED +(`value_offset()`), never a literal. + +**Root cause, recorded because it generalizes:** the plan verified that +`BoardAggregates` re-bases (the *documented* reservation adjacent to the +tenant list) and stopped there — it never swept `INTEGRATION_PLANS.md` for +OTHER pending ordinal claims. A discriminant assignment must grep the whole +board for the target ordinal, not just read the enum's own neighbourhood. + +## ISS-TVT-3-DISABLE-RUN-IS-VACUOUS — `token-value-tenant-v1`'s F-TVT-3 gate cannot fail (2026-08-28) + +F-TVT-3's disable-run reads: "remove the `VALUE_TENANTS` descriptor → +`verify_layout` goes red." Measured by a codex review (post-merge): +`NodeRowPacket::verify_layout()` inspects only the three fixed +`NODE_ROW_COLUMNS` entries (key, edges, the whole 480-byte value slab) — it +never walks the nested tenant descriptors, so removing one cannot turn it +red. **The prescribed falsifier is vacuous** — the exact defect class this +repo's own falsifiability rule exists to catch, shipped inside a plan that +invokes that rule. + +Replacement when D-TVT-2 runs: assert `VALUE_TENANTS` contiguity + +discriminant ordering + the `Full` mask directly, and disable-verify THAT. + +## ISS-TVT-HYDRATION-REGION-CLAIM-WRONG — the plan contradicts the code it cites (2026-08-28) + +`token-value-tenant-v1` W0 states `HydrationSource::from_env()` returns a +hard `None` on a missing var and lists `AWS_DEFAULT_REGION` among the five +required. Actual (`lance-graph-hydrate/src/env.rs:56-59`): +`env("AWS_DEFAULT_REGION").unwrap_or_else(|| "auto".into())` — the region is +OPTIONAL with an `"auto"` default; only key/secret/endpoint/bucket are +required. A W0 implementation written from the plan's text would reject a +configuration the shared hydration API intentionally supports. + +Also open on that plan, same review batch, lower severity: candidate B's +continuation contract is undefined (identity, link fields, ordering, +termination, chain-length budget); W1/W4 name no numeric thresholds; and W3's +`mint_for` step contradicts §5's "No V1 mints, anywhere." + ## ISS-KANBAN-PLAN-EXIT-HAS-NO-NAMED-ROUTE — `KanbanColumn::Plan` is legal in the DAG but no routing primitive emits it (2026-08-28) Surfaced by a codex P1 on #1074 and confirmed by reading `contract/src/kanban.rs` at HEAD. `advance()` is documented as "the first non-`Prune` successor"; `Evaluation::next_phases()` is `[Commit, Plan, Prune]`, so `advance()` returns **`Commit`, always**. `veto()` returns -`Prune`. `Hold` returns `None`. Therefore `advance_on_gate`'s reachable set is -exactly `{Commit, Prune, None}` and **`Plan = 4` — "re-enter Planning carrying -the witness", the revision exit — is emitted by no named primitive**; only a -caller hand-walking `next_phases()` can reach it. +`Prune`. `Hold` returns `None`. Therefore, **starting from `Evaluation`**, +`advance_on_gate`'s reachable set is exactly `{Commit, Prune, None}` (from +other phases `Flow` yields that phase's own first non-`Prune` successor — +`Planning → CognitiveWork`, `CognitiveWork → Evaluation`, `Plan → Planning`; +`Evaluation` is the only phase whose successors include `Plan` at all). So +**`Plan = 4` — "re-enter Planning carrying the witness", the revision exit — +is emitted by no named primitive**; only a caller hand-walking +`next_phases()` can reach it. **Open question, for the operator, not for a plan to settle:** is this intentional Rubicon discipline (revision is a deliberate act, never a gate's diff --git a/.claude/plans/mul-ewa-trust-propagation-v1.md b/.claude/plans/mul-ewa-trust-propagation-v1.md index 545acbb2c..bfe095432 100644 --- a/.claude/plans/mul-ewa-trust-propagation-v1.md +++ b/.claude/plans/mul-ewa-trust-propagation-v1.md @@ -103,12 +103,17 @@ and write down, a construction: off-diagonal `s12` means. A diagonal seed (`s12 = 0`) is the honest default — it asserts no measured correlation — and must be declared as such rather than smuggled in. -- **`M_k` (the hop transform):** what a hop DOES to uncertainty. Candidates: - `M_k = √(per-hop trust)·I` (isotropic decay, the closest analogue of the - scalar baseline and therefore the fairest control), or a margin-scaled - anisotropic form. Whichever is chosen, the scalar baseline in W1(a) MUST be - the one that arm (b) collapses to when `M_k` is isotropic — otherwise the - comparison measures the construction, not the propagation. +- **`M_k` (the hop transform) — PREDECLARED:** `M_k = √(per-hop trust)·I` + (isotropic) is the CONTROL form; the treatment is a margin-scaled + anisotropic form, declared in D-MEP-1 before any run. +- **The scalar baseline is PREDECLARED as the PRODUCT of per-hop trust, and + `min` is explicitly rejected** (CodeRabbit, #1074). This is forced, not + chosen: under `M_k = √(t_k)·I` the sandwich gives + `Σ_n = (∏ t_k)·Σ₀`, so trace scales by **∏ t_k** — the product, never the + minimum. Baselining against `min` would make arm (b) differ from arm (a) + for a reason that has nothing to do with propagation, manufacturing a + spurious BUY. Readout normalization is fixed with it: both arms report + `trace(Σ_n)/trace(Σ₀)`, a unitless ratio, so the two are on one scale. - **F-MEP-1b (construction-honesty gate):** if arm (b) with an isotropic `M_k` and a diagonal Σ₀ is algebraically equivalent to arm (a), the probe MUST report that equivalence rather than a spurious difference — and any @@ -137,8 +142,8 @@ forked. Opus adjudication).** Over real multi-hop chains (KJV derivation chains and/or anaphora chains with per-hop margins), rank derived beliefs by suspicion under two arms: -- (a) **scalar baseline** — naive decay (product / min of per-hop trust), - the default every stack uses; +- (a) **scalar baseline** — naive decay: the **product** of per-hop trust + (predeclared above; `min` rejected), the default every stack uses; - (b) **EWA arm** — per-hop 2×2 Σ (K2 derivation), sandwich-propagated, read out as a scalar (largest eigenvalue or trace). - F-MEP-1 (anti-vacuity): the two rankings must DIVERGE non-trivially @@ -212,8 +217,14 @@ boundary). > `advance_on_gate`**, because `Plan` is structurally unreachable from it: > `advance()` is *"the first non-`Prune` successor"* and `Evaluation`'s > `next_phases()` is `[Commit, Plan, Prune]`, so `advance()` returns -> **`Commit`, always**; `veto()` returns `Prune`; `Hold` returns `None`. The -> reachable set is therefore exactly `{Commit, Prune, None}`. +> **`Commit`, always**; `veto()` returns `Prune`; `Hold` returns `None`. +> **Precondition, stated because it is load-bearing** (CodeRabbit, #1074): +> the reachable set `{Commit, Prune, None}` holds **starting from +> `Evaluation`** — which is where W3 measures, since that is the only phase +> whose successors include `Plan` at all. From other phases `Flow` yields +> that phase's own first non-`Prune` successor (`Planning → CognitiveWork`, +> `CognitiveWork → Evaluation`, `Plan → Planning`); the claim is about +> `Evaluation`'s 3-way terminal, not about the DAG as a whole. > > **The finding this surfaces is worth more than the metric it cost**, and it > is recorded here rather than papered over: **`Plan = 4` — the revision exit, From eb743063c47a028823f46cf2be75f03ecd8f38c4 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 23:46:58 +0000 Subject: [PATCH 05/13] plan(mul-ewa): close three protocol holes; file the generator finding on the board MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All three CodeRabbit Majors verified correct and fixed: - Split was underspecified ("held-out half" / "other half" named no assignment rule, so repeated runs could partition differently and produce different BUY decisions). Now deterministic and leakage-safe: partition key is the chain's ROOT SUBJECT id -- never the chain id, since two chains sharing a root would otherwise straddle the split and leak -- with half = blake3(root ‖ "mep-w1-v1")[0] & 1. Half 0 fits, half 1 evaluates once. The salt is part of the pre-registration so a re-run reproduces the identical partition. - AUC is undefined on a single-class evaluation half, and n >= 200 does not prevent that. Both halves' class counts are now reported unconditionally, and a single-class half stops as UNDERPOWERED -- deliberately not NO-BUY, because a degenerate split is a statement about the cohort, not about the operator under test. - W3's percentile mapping was implementation-dependent. "Clean chain" is now defined as zero S4 error events in the W1 fit half -- the same binary signal W1's AUC uses, so the waves cannot drift on what clean means -- with nearest-rank percentiles (no interpolation) and an UNDERPOWERED exit when the clean set is empty or N < 50. Also files ISS-SUPERSESSION-GENERATOR-DID-RANGE-NARROW: the generator reads D-MEP-0..4 as D-MEP-0 alone and excludes STATUS_BOARD.md from its inputs. Recorded on the board rather than fixed here, with the scope reasoning that CodeRabbit accepted -- the fix re-scopes every plan's coverage number and needs its own PR with the full before/after diff. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1 --- .claude/board/ISSUES.md | 22 +++++++++++++++++++ .claude/plans/mul-ewa-trust-propagation-v1.md | 18 +++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/.claude/board/ISSUES.md b/.claude/board/ISSUES.md index cb5c73010..59d36ad19 100644 --- a/.claude/board/ISSUES.md +++ b/.claude/board/ISSUES.md @@ -1,3 +1,25 @@ +## ISS-SUPERSESSION-GENERATOR-DID-RANGE-NARROW — the coverage generator reads `D-XXX-0..4` as one D-id (2026-08-28) + +`.claude/tools/supersession_index.py` extracts only the FIRST id from a +plan's `D-XXX-0..N` range notation (so `D-MEP-0..4` counts as `D-MEP-0` +alone), and its board input set excludes `.claude/board/STATUS_BOARD.md` — +where the per-deliverable rows actually live. Net effect: a plan that writes +its D-ids as a range gets a coverage number that cannot reflect its +deliverables. Found by CodeRabbit on #1074. + +**Deliberately NOT fixed in #1074, and the reasoning is the tracked part:** +the generator is shared by every plan in the repo and its output is CI-gated +(`regenerate-and-diff`). Changing range expansion or the board-input set +re-scopes EVERY other plan's coverage number in the same commit — a +repo-wide governance change riding inside a plan-only PR, which is the +drive-by this board's hygiene rule exists to prevent. CodeRabbit agreed with +the scope call and recorded it as a review learning. + +**The fix, when it lands, needs its own PR carrying the complete before/after +coverage diff across all plans**, so the re-scoping is visible and +reviewable rather than a side effect. Affects every plan using range +notation, not just `mul-ewa-trust-propagation-v1`. + ## ISS-TOKEN-TENANT-16-COLLIDES-WITH-HOLEV3 — `token-value-tenant-v1` (merged #1072) assigns an ordinal already reserved (2026-08-28) **BLOCKS D-TVT-2.** `.claude/plans/token-value-tenant-v1.md` §2 assigns diff --git a/.claude/plans/mul-ewa-trust-propagation-v1.md b/.claude/plans/mul-ewa-trust-propagation-v1.md index bfe095432..d98444dee 100644 --- a/.claude/plans/mul-ewa-trust-propagation-v1.md +++ b/.claude/plans/mul-ewa-trust-propagation-v1.md @@ -170,8 +170,9 @@ run and requires a re-pin with the change stated: | ties | equal readout ⇒ equal rank (average-rank convention, standard for Spearman) | | missing hop data | chain EXCLUDED wholesale, never imputed; the excluded count is REPORTED | | minimum sample | **n ≥ 200** qualifying chains; below that the probe reports UNDERPOWERED and stops — that is a valid, honest exit | -| split | metric fixed on a held-out half; the F-MEP-2 threshold is evaluated ONCE on the other half, no re-fitting | +| split | **deterministic, chain-level, leakage-safe**: partition key = the chain's ROOT SUBJECT id (never the chain id — two chains sharing a root would otherwise straddle the split and leak); half = `blake3(root_subject_id ‖ "mep-w1-v1")[0] & 1`. Half **0** FITS (the AUC comparison is computed here); half **1** EVALUATES the F-MEP-2 threshold ONCE, no re-fitting. The literal salt is part of the pre-registration, so a re-run reproduces the identical partition. | | comparison metric | AUC of suspicion-rank vs the binary S4 error signal | +| **degenerate AUC** | AUC is UNDEFINED when an evaluation half carries no positive or no negative S4 event, and `n ≥ 200` does **not** prevent that (CodeRabbit, #1074). Both halves' **class counts are REPORTED unconditionally**; if either half is single-class the probe stops as **UNDERPOWERED** — never NO-BUY, since a degenerate split is a statement about the cohort, not about the operator under test, and never a computed AUC on a one-class half. | | BUY threshold | ΔAUC **≥ 0.05** over arm (a) AND clearing the F-MEP-3 null by ≥ 2σ of the shuffle distribution | Anything short of BOTH thresholds is NO-BUY. "Better" has no meaning in this @@ -239,9 +240,18 @@ boundary). **The Σ → `TrustTexture` mapping is PREDECLARED, deterministic, and single** (CodeRabbit, #1074 — "different mappings produce different flip rates"): -using the same `trace(Σ)` readout W1 fixed, and the W1 held-out half to set -its cut points, `TrustTexture` is `Calibrated` below the 50th percentile of -the clean-chain trace distribution, `Uncertain` between the 50th and 90th, +using the same `trace(Σ)` readout W1 fixed, and the W1 **fit half** (half 0, +per the split row above) to set its cut points. **"Clean chain" is defined, +not assumed**: a chain in the fit half carrying **zero** S4 error events — +the same binary signal W1's AUC uses, so the two waves cannot drift apart on +what "clean" means. Cut points are the **50th and 90th percentiles by the +nearest-rank method** (`ceil(p/100 · N)`-th value of the ascending trace +list — no interpolation, so the result is exact and reproducible across +implementations). If the clean set is empty or `N < 50`, W3 stops as +**UNDERPOWERED** and reports the count, rather than deriving cut points from +a sample too small to place a 90th percentile. `TrustTexture` is then +`Calibrated` below the 50th percentile, `Uncertain` between the 50th and +90th, and `Overconfident` at or above the 90th. Ties resolve to the LOWER-suspicion texture (the conservative direction: a tie must not manufacture a flip). `Underconfident` is never produced — nothing in a propagated covariance From f8fd8cc994e5cb7ab6b1eb4d00a4a412d5a55c77 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 23:48:20 +0000 Subject: [PATCH 06/13] plan(mul-ewa): predeclare the W3 flip-rate denominator CodeRabbit outside-diff Major, verified correct: the metric named a flip RATE without saying what it is a fraction of, so the number was not reproducible. Predeclared: the denominator is only those qualifying chains whose LOCAL arm reaches Commit, never all qualifying chains. A chain that never gated Commit locally cannot flip from Commit, so including it would dilute the rate with cases the metric is not about and let the number move purely by cohort composition. Numerator and denominator now share one population. Zero denominator reports N/A with the count, never 0.0 -- zero flips out of zero opportunities measures nothing, and 0% would read as "the propagation changed nothing" when the truth is "the arm never ran". The three inline findings from the same review batch (deterministic split, single-class AUC exit, W3 percentile specification) were already fixed in eb743063; that run had not yet seen it. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1 --- .claude/plans/mul-ewa-trust-propagation-v1.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.claude/plans/mul-ewa-trust-propagation-v1.md b/.claude/plans/mul-ewa-trust-propagation-v1.md index d98444dee..ea3a996dd 100644 --- a/.claude/plans/mul-ewa-trust-propagation-v1.md +++ b/.claude/plans/mul-ewa-trust-propagation-v1.md @@ -268,6 +268,17 @@ trust instead hold or veto under propagated uncertainty. That is the epistemic-pothole detector expressed in the vocabulary the routing primitives can actually speak. Reaching `Plan` would require the open question above to be answered first. + +**The denominator is PREDECLARED** (CodeRabbit, #1074 — without it the rate is +not reproducible): it is **only those qualifying chains whose LOCAL arm +reaches `Commit`**, never all qualifying chains. A chain that never gated +`Commit` locally cannot flip *from* `Commit`, so including it would dilute +the rate with cases the metric is not about — and would let the number move +purely by cohort composition. Numerator and denominator therefore share one +population. **If that denominator is zero, the rate is reported `N/A` with +the count**, never `0.0` — zero flips out of zero opportunities is not a +measurement of anything, and printing `0%` would read as "the propagation +changed nothing" when the truth is "the arm never ran." - F-MEP-5 (two-sided): flips must CONCENTRATE on chains W1's error signal flagged (not uniform noise), AND a can-stay-silent half — short/clean chains must not flip (a gate that flips everything is the 150/150 From 4d94c5dfaad14411c51a26ccad27c1cf72bf378c Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 00:01:19 +0000 Subject: [PATCH 07/13] plan(mul-ewa): close four decision-rule holes in the W1 pre-registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit review d9dee300 against f8fd8cc9 — the first run to evaluate the current content rather than an earlier revision. All four findings are real defects in the pre-registered decision rules, not wording. 1. Zero-trace seed (readout undefined). The seed rules required `s12 = 0` but never required a positive diagonal, so `s11 = s22 = 0` gives `trace(Sigma_0) = 0` and the normalized readout divides by zero. Adds a `trace(Sigma_0) > 0` precondition; a failing chain is excluded wholesale with its count reported, never mapped to 0/1/NaN. Such a seed asserts perfect certainty on both axes, so there is no uncertainty to propagate and including it would tie the two arms for a non-propagation reason. 2. Per-half minimums. `n >= 200` was a cohort-level total and said nothing about how the chains landed either side of the hash split, so a small two-class evaluation half could pass while producing an unstable AUC. Adds independent per-half floors (>= 50 chains, >= 10 positive and >= 10 negative S4 events), declared before any run so they cannot be relaxed after seeing which one bites. Also states explicitly that the ΔAUC the BUY rule reads is computed on half 1 alone; half 0's is diagnostic only. 3. Anti-predictive rankings could BUY. The rule checked only ΔAUC, so `AUC(b) = 0.20` over `AUC(a) = 0.10` cleared the bar while both arms ranked backwards — buying a bigger error. Adds `AUC(b) > 0.5` as a condition alongside ΔAUC >= 0.05 and the 2-sigma null, and requires the probe to report a both-arms-inverted result explicitly rather than filing it as a quiet NO-BUY, since that is a finding about the suspicion construction itself. 4. Percentile boundary contradiction. "at or above the 90th => Overconfident" contradicted the ties-to-lower-suspicion rule, giving a value equal to p90 two possible textures. Replaced with explicit, non-overlapping, exhaustive intervals whose boundaries all close downward, so the tie rule is arithmetic rather than a separate sentence that can disagree with the intervals. Supersession index regenerated (no diff — no ruled symbol changed). --- .claude/plans/mul-ewa-trust-propagation-v1.md | 46 ++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/.claude/plans/mul-ewa-trust-propagation-v1.md b/.claude/plans/mul-ewa-trust-propagation-v1.md index ea3a996dd..57b3a768c 100644 --- a/.claude/plans/mul-ewa-trust-propagation-v1.md +++ b/.claude/plans/mul-ewa-trust-propagation-v1.md @@ -103,6 +103,18 @@ and write down, a construction: off-diagonal `s12` means. A diagonal seed (`s12 = 0`) is the honest default — it asserts no measured correlation — and must be declared as such rather than smuggled in. + **Positive-trace precondition (CodeRabbit, #1074):** `s12 = 0` alone does + NOT make the readout well-defined — a seed with `s11 = s22 = 0` has + `trace(Σ₀) = 0` and the normalized readout below divides by zero. The seed + construction MUST therefore also satisfy `trace(Σ₀) > 0` (equivalently, at + least one variance axis is strictly positive). A chain whose seed fails + this is **EXCLUDED wholesale and its count REPORTED**, exactly like a chain + with missing hop data — never silently mapped to 0, 1, or NaN, each of + which would masquerade as a measured propagation result. Note the exclusion + is not cosmetic: a zero-variance seed asserts perfect certainty about both + axes, so there is no uncertainty for the operator under test to propagate, + and including it would score arms (a) and (b) as tied for a reason that has + nothing to do with propagation. - **`M_k` (the hop transform) — PREDECLARED:** `M_k = √(per-hop trust)·I` (isotropic) is the CONTROL form; the treatment is a margin-scaled anisotropic form, declared in D-MEP-1 before any run. @@ -169,14 +181,14 @@ run and requires a re-pin with the change stated: | readout | **trace(Σ)** — ONE scalar, fixed. (Largest eigenvalue is NOT evaluated; picking between them post-hoc is the defect this row exists to prevent.) | | ties | equal readout ⇒ equal rank (average-rank convention, standard for Spearman) | | missing hop data | chain EXCLUDED wholesale, never imputed; the excluded count is REPORTED | -| minimum sample | **n ≥ 200** qualifying chains; below that the probe reports UNDERPOWERED and stops — that is a valid, honest exit | -| split | **deterministic, chain-level, leakage-safe**: partition key = the chain's ROOT SUBJECT id (never the chain id — two chains sharing a root would otherwise straddle the split and leak); half = `blake3(root_subject_id ‖ "mep-w1-v1")[0] & 1`. Half **0** FITS (the AUC comparison is computed here); half **1** EVALUATES the F-MEP-2 threshold ONCE, no re-fitting. The literal salt is part of the pre-registration, so a re-run reproduces the identical partition. | +| minimum sample | **n ≥ 200** qualifying chains **in total, AND per-half floors that the total does not imply** (CodeRabbit, #1074): each half independently needs **≥ 50 chains, ≥ 10 positive and ≥ 10 negative S4 events**. A cohort-level `n` says nothing about how it landed either side of a hash split, so the total is a necessary and NOT a sufficient condition. Below ANY of these the probe reports UNDERPOWERED with the failing count named, and stops — a valid, honest exit. The floors are declared here, before any run, precisely so they cannot be relaxed after seeing which one bites. | +| split | **deterministic, chain-level, leakage-safe**: partition key = the chain's ROOT SUBJECT id (never the chain id — two chains sharing a root would otherwise straddle the split and leak); half = `blake3(root_subject_id ‖ "mep-w1-v1")[0] & 1`. Half **0** FITS — every free choice (which suspicion ranking, any construction detail left open by D-MEP-1) is fixed here, and its ΔAUC is DIAGNOSTIC ONLY, never the number the BUY rule reads. Half **1** EVALUATES: **the ΔAUC the BUY threshold is applied to is computed on half 1 ALONE, exactly once, with no re-fitting** — reporting a half-0 ΔAUC or a pooled ΔAUC as the decision number is the defect this row exists to prevent. The literal salt is part of the pre-registration, so a re-run reproduces the identical partition. | | comparison metric | AUC of suspicion-rank vs the binary S4 error signal | -| **degenerate AUC** | AUC is UNDEFINED when an evaluation half carries no positive or no negative S4 event, and `n ≥ 200` does **not** prevent that (CodeRabbit, #1074). Both halves' **class counts are REPORTED unconditionally**; if either half is single-class the probe stops as **UNDERPOWERED** — never NO-BUY, since a degenerate split is a statement about the cohort, not about the operator under test, and never a computed AUC on a one-class half. | -| BUY threshold | ΔAUC **≥ 0.05** over arm (a) AND clearing the F-MEP-3 null by ≥ 2σ of the shuffle distribution | +| **degenerate AUC** | AUC is UNDEFINED when a half carries no positive or no negative S4 event, and `n ≥ 200` does **not** prevent that (CodeRabbit, #1074). Both halves' **class counts are REPORTED unconditionally**; if either half is single-class the probe stops as **UNDERPOWERED** — never NO-BUY, since a degenerate split is a statement about the cohort, not about the operator under test, and never a computed AUC on a one-class half. This is the ZERO-count guard only; the ≥ 10-per-class floors in the minimum-sample row are what stop a *technically* two-class half from producing an AUC too unstable to decide on. | +| BUY threshold | **All three, on half 1:** (i) `AUC(b) > 0.5` — the EWA arm must be predictive AT ALL, not merely less anti-predictive than the baseline. Without this, `AUC(b) = 0.20` over `AUC(a) = 0.10` clears a ΔAUC bar while both arms rank *backwards*, and the "win" is a bigger error (CodeRabbit, #1074). (ii) ΔAUC **≥ 0.05** over arm (a). (iii) clearing the F-MEP-3 null by ≥ 2σ of the shuffle distribution. An arm that is anti-predictive (`AUC ≤ 0.5`) is a NO-BUY however large its ΔAUC — and if BOTH arms land below 0.5 the probe reports that inversion explicitly, since a systematically backwards ranking is a finding about the suspicion construction, not a quiet NO-BUY. | -Anything short of BOTH thresholds is NO-BUY. "Better" has no meaning in this -plan outside this table. +Anything short of ALL THREE conditions is NO-BUY. "Better" has no meaning in +this plan outside this table. **W2 — the carrier (GATED on W1 BUY; Opus review, Sonnet transcription).** Mint K1 ONLY if W1 showed derived-per-read Σ (K2) insufficient (e.g. a @@ -249,11 +261,23 @@ nearest-rank method** (`ceil(p/100 · N)`-th value of the ascending trace list — no interpolation, so the result is exact and reproducible across implementations). If the clean set is empty or `N < 50`, W3 stops as **UNDERPOWERED** and reports the count, rather than deriving cut points from -a sample too small to place a 90th percentile. `TrustTexture` is then -`Calibrated` below the 50th percentile, `Uncertain` between the 50th and -90th, -and `Overconfident` at or above the 90th. Ties resolve to the LOWER-suspicion -texture (the conservative direction: a tie must not manufacture a flip). +a sample too small to place a 90th percentile. `TrustTexture` is then read +off **explicit, non-overlapping, exhaustive intervals**, so a value landing +exactly ON a cut point has exactly one outcome (CodeRabbit, #1074 — the +earlier wording paired "at or above the 90th ⇒ `Overconfident`" with a +ties-to-lower-suspicion rule, which assigned a value equal to `p90` two +different textures): + +| condition | texture | +|---|---| +| `trace ≤ p50` | `Calibrated` | +| `p50 < trace ≤ p90` | `Uncertain` | +| `p90 < trace` | `Overconfident` | + +Both boundaries close DOWNWARD, which is the ties-to-lower-suspicion rule +stated as arithmetic rather than as a separate sentence that can contradict +the intervals: a chain sitting exactly on a cut point keeps the calmer +texture, so a tie can never manufacture a flip. `Underconfident` is never produced — nothing in a propagated covariance distinguishes it from `Calibrated`, and inventing that distinction is exactly the coordinate-fabrication the census measured. `FlowState` is held FIXED at From 4d0c5e6117569828797ed7e39d6188cac3588ff9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 00:16:58 +0000 Subject: [PATCH 08/13] plan(mul-ewa): quantify F-MEP-5, fix the W3 readout, close four more holes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit review c4ecebff against 4d94c5d — six findings, all real, all the same class as the previous round: pre-registration holes that would let a decision be made after seeing numbers. Readout (2 sites + the canonical row). W3 derived its p50/p90 cut points from RAW trace(Sigma) while §3 had fixed the comparison readout as trace(Sigma_n)/trace(Sigma_0). Raw traces are not comparable across chains with different seed magnitudes, so those cut points would have sorted chains partly by how uncertain they STARTED rather than by what propagation did — handing Overconfident to any big-seed chain. Fixed in W3, and fixed at the source: the protocol table's readout row said raw trace too, which is where W3 inherited it from. Also removed a leftover "largest eigenvalue or trace" at the arm definition — an unfixed choice sitting one paragraph from the table that fixed it. F-MEP-5 was a vacuous gate. "Flips must CONCENTRATE" and "must not flip" named no statistic, no threshold and no minimum count, and the can-stay- silent half passed AUTOMATICALLY whenever no clean chain reached local Commit: zero flips out of zero opportunities read as "correctly silent" having observed nothing. That is the exact vacuous-guard defect the repo's falsifiability rule exists to catch, reproduced inside the test written to catch it. Now: flagged/silent populations defined, both restricted to local-Commit reachers, minimum 20 opportunities EACH (below either => UNDERPOWERED, not PASS), concentration requiring both a 2x ratio AND a 0.10 absolute gap (a ratio alone passes at 2% vs 1%), silence capped at 0.20. Marked POLICY PINS, not measurements, with a re-pin-don't-relax rule. W3 evaluation population. The split gave half 1 to W1 evaluation but W3 never said which half it measures. On half 0 it would evaluate outcome- derived cut points on the data they came from. Now half 1 only; half 0 fits cut points; W4 consumes the half-1 number and no exploratory half-0 result. F-MEP-1 zero-variance guard. Spearman rho is undefined when either arm's scores are all tied. The NaN would have compared false against 0.95 under IEEE 754 and silently read as "diverged" — waving the run through the gate whose whole job is catching a non-divergent pair. Now: distinct-score counts reported, < 2 distinct => UNDERPOWERED. Observation unit. Undefined, so an event-rich chain could enter the AUC repeatedly and weight it by event count, and the per-half floors would count different things in the two halves. Now exactly one score and one binary label per chain (label = 1 iff >= 1 S4 event); counts still reported as the F-MEP-5 concentration input but never weights. F-MEP-3 null. "Beat a null by 2 sigma" named neither distribution nor side. Now: 1000 redeals with reproducible seeds, statistic is dAUC itself on half 1, one-sided (the claim is directional), and sd(null) == 0 is UNDERPOWERED rather than an automatic pass — a collapsed bar clears on any positive observed value, i.e. a gate that cannot fail. Supersession index regenerated (no diff — no ruled symbol changed). --- .claude/plans/mul-ewa-trust-propagation-v1.md | 121 +++++++++++++++--- 1 file changed, 103 insertions(+), 18 deletions(-) diff --git a/.claude/plans/mul-ewa-trust-propagation-v1.md b/.claude/plans/mul-ewa-trust-propagation-v1.md index 57b3a768c..87f3dca2a 100644 --- a/.claude/plans/mul-ewa-trust-propagation-v1.md +++ b/.claude/plans/mul-ewa-trust-propagation-v1.md @@ -157,10 +157,36 @@ suspicion under two arms: - (a) **scalar baseline** — naive decay: the **product** of per-hop trust (predeclared above; `min` rejected), the default every stack uses; - (b) **EWA arm** — per-hop 2×2 Σ (K2 derivation), sandwich-propagated, - read out as a scalar (largest eigenvalue or trace). + read out as the ONE scalar the protocol table fixes: + `trace(Σ_n)/trace(Σ₀)`. (An earlier draft here read "largest eigenvalue + or trace" — that left the readout unfixed at the arm definition while the + table below fixed it, i.e. exactly the post-hoc choice the + pre-registration exists to close. Trace is the readout, normalized, + everywhere in this plan.) + +**The observation unit is ONE per chain** (CodeRabbit, #1074). Each +qualifying chain contributes **exactly one** suspicion score per arm and +**exactly one** binary S4 label; the label is `1` if the chain carries **≥ 1** +S4 error event and `0` otherwise. Without this, a chain with many S4 events +would enter the AUC repeatedly and silently weight the result by event +count — and the per-half floors would be counting two different things in +the two halves. Event COUNTS are still reported (they are the input to the +F-MEP-5 concentration statistic below), but they never become weights, never +become multiple observations, and never enter the AUC. + - F-MEP-1 (anti-vacuity): the two rankings must DIVERGE non-trivially (Spearman ρ < 0.95 over the suspicion ordering). Identical rankings ⇒ the seam buys nothing ⇒ **NO-BUY immediately**, numbers banked. + **Zero-variance guard (CodeRabbit, #1074):** Spearman ρ is UNDEFINED when + either arm's suspicion scores are all tied — the rank-variance denominator + is zero and the result is NaN, which must never reach a comparison against + `0.95` (in IEEE 754 every such comparison is false, so an unguarded NaN + would silently read as "diverged" and wave the run through the gate whose + entire job is to catch a non-divergent pair). Before computing ρ the probe + REPORTS each arm's **distinct-score count**; if either arm has `< 2` + distinct scores the run stops as **UNDERPOWERED** — not NO-BUY, since an + all-tied arm is a statement about the construction or the cohort, not a + measured verdict on the operator. - F-MEP-2 (the buy signal): on the divergent subset, arm (b) must predict an INDEPENDENT error signal better than (a). Error signal = S4-guarded arena events only (Choice-on-overlap hits, revision conflicts, @@ -168,6 +194,23 @@ suspicion under two arms: (TD-NARS-REVISION-UNGUARDED's lesson as a methodological fence). - F-MEP-3 (null control, `shuffle_beliefs_null` precedent): arm (b)'s advantage must beat a stamp-shuffled null on the same chains. + **Fully specified, because "beat a null by 2σ" names neither a + distribution nor a side** (CodeRabbit, #1074): + - **Redeals: `N_null = 1000`**, each a `shuffle_beliefs_null` redeal of the + S4 stamps across the SAME chains (the existing SplitMix64 Fisher-Yates + shuffle unit and seed formula are unchanged); seeds are + `base_seed + i` for `i` in `0..1000`, so the null set is reproducible. + - **Statistic: `ΔAUC` itself** — recomputed on half 1 under each redeal, + giving a null distribution of the SAME quantity the BUY rule reads. A + null over some other statistic would not bound the decision being made. + - **One-sided.** The claim is directional (EWA predicts error BETTER), + so the criterion is `ΔAUC_observed ≥ mean(null) + 2·sd(null)`. A + two-sided reading would credit a significant result in the wrong + direction. + - **Zero null variance ⇒ UNDERPOWERED, never an automatic pass.** If + `sd(null) == 0` the `+2σ` bar collapses onto the mean and ANY positive + observed ΔAUC would clear it — a gate that cannot fail. The probe + reports the degenerate null and stops. **The statistical protocol is PREDECLARED — every value below is fixed BEFORE the probe runs** (CodeRabbit, #1074: unspecified choices "can be made @@ -178,7 +221,7 @@ run and requires a re-pin with the change stated: | knob | predeclared value | |---|---| | cohort | every S4-guarded arena chain of hop-length **≥ 2** (single-hop chains cannot distinguish propagation from its seed) | -| readout | **trace(Σ)** — ONE scalar, fixed. (Largest eigenvalue is NOT evaluated; picking between them post-hoc is the defect this row exists to prevent.) | +| readout | **`trace(Σ_n)/trace(Σ₀)`** — ONE scalar, fixed, and **normalized**; the seed's `trace(Σ₀) > 0` precondition above is what makes it well-defined. Raw `trace(Σ_n)` is NOT the readout: it is not comparable across chains with different seed magnitudes, so ranking on it would sort partly by how uncertain a chain STARTED rather than by what propagation did to it (CodeRabbit, #1074 — caught where W3 had inherited the raw form). Largest eigenvalue is NOT evaluated; picking between readouts post-hoc is the defect this row exists to prevent. **Every wave uses this one scalar** — W1's ranking and W3's `p50`/`p90` cut points alike. | | ties | equal readout ⇒ equal rank (average-rank convention, standard for Spearman) | | missing hop data | chain EXCLUDED wholesale, never imputed; the excluded count is REPORTED | | minimum sample | **n ≥ 200** qualifying chains **in total, AND per-half floors that the total does not imply** (CodeRabbit, #1074): each half independently needs **≥ 50 chains, ≥ 10 positive and ≥ 10 negative S4 events**. A cohort-level `n` says nothing about how it landed either side of a hash split, so the total is a necessary and NOT a sufficient condition. Below ANY of these the probe reports UNDERPOWERED with the failing count named, and stops — a valid, honest exit. The floors are declared here, before any run, precisely so they cannot be relaxed after seeing which one bites. | @@ -252,16 +295,32 @@ boundary). **The Σ → `TrustTexture` mapping is PREDECLARED, deterministic, and single** (CodeRabbit, #1074 — "different mappings produce different flip rates"): -using the same `trace(Σ)` readout W1 fixed, and the W1 **fit half** (half 0, -per the split row above) to set its cut points. **"Clean chain" is defined, -not assumed**: a chain in the fit half carrying **zero** S4 error events — -the same binary signal W1's AUC uses, so the two waves cannot drift apart on -what "clean" means. Cut points are the **50th and 90th percentiles by the -nearest-rank method** (`ceil(p/100 · N)`-th value of the ascending trace -list — no interpolation, so the result is exact and reproducible across -implementations). If the clean set is empty or `N < 50`, W3 stops as -**UNDERPOWERED** and reports the count, rather than deriving cut points from -a sample too small to place a 90th percentile. `TrustTexture` is then read +using the **NORMALIZED** readout W1 fixed — `trace(Σ_n)/trace(Σ₀)`, not raw +`trace(Σ)`. This correction matters and is not cosmetic (CodeRabbit, #1074): +raw traces are not comparable across chains with different seed magnitudes, +so percentile cut points over raw traces would sort chains largely by **how +uncertain they started**, handing `Overconfident` to any chain with a big +seed regardless of what propagation did to it. The normalized ratio is the +quantity that actually measures propagated change, and it is the same scalar +W1 ranks on — so the two waves cannot disagree about what was measured. + +**W3 is measured on half 1 ONLY** (CodeRabbit, #1074). The two halves keep +exactly the roles the W1 split row gives them: **half 0 FITS** the cut points +(it is the clean-chain sample the percentiles are computed from) and **half 1 +EVALUATES** the flip rate. Measuring the flip rate on half 0 would evaluate +outcome-derived cut points on the very data they were derived from, which +inflates the apparent effect by construction; W4 therefore consumes the +half-1 number ONLY, and any half-0 flip rate is exploratory and explicitly +not eligible for the verdict. + +**"Clean chain" is defined, not assumed**: a chain **in half 0** carrying +**zero** S4 error events — the same binary signal W1's AUC uses, so the two +waves cannot drift apart on what "clean" means. Cut points are the **50th and +90th percentiles by the nearest-rank method** (`ceil(p/100 · N)`-th value of +the ascending normalized-readout list — no interpolation, so the result is +exact and reproducible across implementations). If the clean set is empty or +`N < 50`, W3 stops as **UNDERPOWERED** and reports the count, rather than +deriving cut points from a sample too small to place a 90th percentile. `TrustTexture` is then read off **explicit, non-overlapping, exhaustive intervals**, so a value landing exactly ON a cut point has exactly one outcome (CodeRabbit, #1074 — the earlier wording paired "at or above the 90th ⇒ `Overconfident`" with a @@ -294,8 +353,9 @@ can actually speak. Reaching `Plan` would require the open question above to be answered first. **The denominator is PREDECLARED** (CodeRabbit, #1074 — without it the rate is -not reproducible): it is **only those qualifying chains whose LOCAL arm -reaches `Commit`**, never all qualifying chains. A chain that never gated +not reproducible): it is **only those qualifying HALF-1 chains whose LOCAL +arm reaches `Commit`** (half 1 per the evaluation-population rule above), +never all qualifying chains. A chain that never gated `Commit` locally cannot flip *from* `Commit`, so including it would dilute the rate with cases the metric is not about — and would let the number move purely by cohort composition. Numerator and denominator therefore share one @@ -303,10 +363,35 @@ population. **If that denominator is zero, the rate is reported `N/A` with the count**, never `0.0` — zero flips out of zero opportunities is not a measurement of anything, and printing `0%` would read as "the propagation changed nothing" when the truth is "the arm never ran." -- F-MEP-5 (two-sided): flips must CONCENTRATE on chains W1's error signal - flagged (not uniform noise), AND a can-stay-silent half — short/clean - chains must not flip (a gate that flips everything is the 150/150 - defect). +- F-MEP-5 (two-sided) — **QUANTIFIED, because "concentrate" and "must not + flip" name no statistic and no threshold** (CodeRabbit, #1074). Both + populations are drawn from **half 1** and BOTH are restricted to chains + that reach local `Commit`, matching the flip-rate denominator above: + - **Flagged** `F` = half-1 chains reaching local `Commit` with **≥ 1** S4 + error event. **Silent** `S` = half-1 chains reaching local `Commit` with + **zero** S4 error events. + - **Minimum opportunities: `|F| ≥ 20` and `|S| ≥ 20`, each reported.** + This is the load-bearing addition. Without it the can-stay-silent half + passes AUTOMATICALLY whenever no clean chain happens to reach local + `Commit` — `|S| = 0` gives zero flips out of zero opportunities, which + the gate would read as "correctly silent" while having observed nothing + at all. That is precisely the vacuous-guard defect the repo's own + falsifiability rule exists to catch, reproduced inside the test written + to catch it. Below either floor: **UNDERPOWERED**, not PASS. + - **Can-fire (concentration): `flip_rate(F) ≥ 2 × flip_rate(S)` AND + `flip_rate(F) − flip_rate(S) ≥ 0.10`.** Both, because a ratio alone is + satisfiable at trivial magnitudes (2 % vs 1 % is a 2× "concentration" + carrying no signal), and a difference alone would pass a gate that fires + on nearly everything. + - **Can-stay-silent: `flip_rate(S) ≤ 0.20`.** A gate that also flips a + fifth of the clean chains is the 150/150 defect regardless of how well + it concentrates. + - These four numbers are **POLICY PINS, not measurements** — nothing has + been measured on this cohort yet, and they are written down here only so + they are fixed before the run rather than chosen to fit it. If the probe + runs and the pins prove badly placed, they are RE-PINNED with the + measurement and the reason stated, never quietly relaxed to convert a + failing gate into a passing one. - **No wiring lands in W3.** It is a probe against the existing `advance_on_gate`; changing the gate's signature or default is explicitly out of scope for v1. From 3dcb71b98b65a7d52cb5e8258e04138a5f3bbf24 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 00:21:56 +0000 Subject: [PATCH 09/13] plan(mul-ewa): land the W0 parity mechanism that was resolved but never written MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Self-caught while draining the notification queue. CodeRabbit's merge-risk note on 4d94c5d still named "the required parity comparison" as unresolved, which did not match my belief that the thread was handled — so I checked the artifact instead of the thread state. The thread (Define an executable parity path for W0) was resolved in an earlier round, and the fix never landed. The plan stated the parity REQUIREMENT ("the probe's 2x2 math must reproduce jc's certified propagation on identical seeded inputs") and never the MECHANISM, which was the entire finding: the probe declares contract as its sole dependency and therefore cannot call jc::ewa_sandwich at all. It is also the only resolved thread on the PR carrying no "Addressed in commit" marker — the signal was there to be read. Dependency facts verified in-tree rather than assumed: - crates/jc is a workspace MEMBER, zero-dep by default. - crates/deepnsm-v2 is workspace-EXCLUDED with its own [workspace] table and lance-graph-contract as its SOLE dep, so the probe genuinely cannot reach jc, and adding that edge would break the property its own manifest comment defends. Mechanism is therefore a staged checked fixture, not a link: a jc-side example calls the REAL jc::ewa_sandwich over fixed seeded inputs and emits a committed f64-hex table stamped with the jc source commit; the probe's inlined math must reproduce it bit-for-bit. A jc-side harness that re-types the probe's math was considered and REJECTED, because it makes F-MEP-0 vacuous by construction: with two independent copies, perturbing the probe's copy leaves the harness's copy and the comparison untouched, so the gate could not fail. Against a committed fixture the perturbation goes red, because the fixture is not derived from the code under test. Staleness is the fixture's one real cost over a live call, so it is guarded rather than ignored: the stamped commit must match the jc source in the checkout, and a mismatch regenerates rather than waives. Supersession index regenerated (no diff -- no ruled symbol changed). --- .claude/plans/mul-ewa-trust-propagation-v1.md | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/.claude/plans/mul-ewa-trust-propagation-v1.md b/.claude/plans/mul-ewa-trust-propagation-v1.md index 87f3dca2a..6c19728e8 100644 --- a/.claude/plans/mul-ewa-trust-propagation-v1.md +++ b/.claude/plans/mul-ewa-trust-propagation-v1.md @@ -146,10 +146,48 @@ forked. tightness/PSD numbers. 2. Inline-sandwich parity gate: the probe's 2×2 math must reproduce jc's certified propagation on identical seeded inputs. + + **The executable mechanism is a CHECKED FIXTURE, because the probe cannot + call `jc` and must not re-type its math** (CodeRabbit, #1074 — this was + raised, the thread was resolved, and the mechanism never actually landed; + the requirement was stated without a way to run it). The dependency facts + that force this shape, verified in-tree rather than assumed: + - `crates/jc` is a workspace MEMBER, zero-dep by default; its production + constitution is standalone. + - `crates/deepnsm-v2` is workspace-EXCLUDED and carries its own + `[workspace]` table, with `lance-graph-contract` as its SOLE dependency + — so the probe cannot reach `jc` at all, and adding that edge would + break the very property the crate's own manifest comment defends. + + So the comparison is staged, not linked: + - **Generator (jc side):** a `jc` example calls the REAL + `jc::ewa_sandwich` over a fixed seeded input set and emits a small + committed table of `(Σ₀, M_k…, Σ_n)` triples in f64 hex (bit-exact, no + decimal round-trip), stamped with the **jc source commit** it was + generated from. + - **Assertion (probe side):** the deepnsm-v2 probe's inlined 15 lines run + the same inputs and must reproduce the committed outputs bit-for-bit. + + **F-MEP-0 stays genuinely falsifiable under this shape**, which is the + reason a jc-side harness that *re-types* the probe's math was rejected: + with two independent copies, perturbing the probe's copy would leave the + harness's copy — and therefore the comparison — untouched, so the gate + could not fail and would be vacuous by construction. Against a committed + fixture, perturbing one matrix entry in the inlined sandwich turns the + assertion red, because the fixture is not derived from the code under + test. - F-MEP-0 (disable-verified): perturb one matrix entry in the inlined sandwich → parity goes red. If this cannot be made to fail, the gate is vacuous and W0 is not done. + **Staleness guard — the one real cost of a fixture over a live call.** A + committed table can silently drift from a `jc` that has since changed. + The stamped commit is what makes drift detectable rather than invisible: + W0 is NOT done unless the stamp matches the `jc` source in the checkout, + and a mismatch REGENERATES the fixture rather than being waived. (Step 1 + already runs jc's own Pillar-6/7 provers green in this checkout, so a jc + that has genuinely regressed is caught there, not here.) + **W1 — the information probe (STOP GATE for everything below; Sonnet arms, Opus adjudication).** Over real multi-hop chains (KJV derivation chains and/or anaphora chains with per-hop margins), rank derived beliefs by From 89eb587bdc5df310120680610823dbf16497fc93 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 00:32:32 +0000 Subject: [PATCH 10/13] plan(mul-ewa): fix an inverted texture->gate ordering, plus three review holes CodeRabbit run 0b7f54fe against 3dcb71b9. Three findings valid as stated; the fourth was wrong on its specific but pointed at a worse defect underneath it, found by verifying against source instead of acting on the finding. TEXTURE -> GATE ORDERING WAS INVERTED (the real defect). The finding asked for the W3 buckets to be mapped onto Crystalline/Solid/ Fuzzy/Murky. That is a DIFFERENT TrustTexture: four types share the name (contract::mul, causal-edge::layout, planner::mul::trust with a fifth Dissonant variant, arigraph::orchestrator). The one on this path is contract::mul::TrustTexture, because GateDecision::from_axes takes it, and causal-edge/src/layout.rs carries an explicit in-source ruling against building the requested cast ("Canonical: NONE ... Do not build a cast on the old claim"). So the remap is forbidden, not merely unnecessary. But verifying that exposed a real inversion. The chain is trace -> TrustTexture -> GateDecision::from_axes -> advance_on_gate(&GateDecision) (advance_on_gate never sees a TrustTexture). Reading from_axes: Uncertain => Block under every flow state, Overconfident => Hold. Gate strength runs Flow < Hold < Block. The table put Uncertain in the MIDDLE bucket and Overconfident at the TOP, so the most-suspicious chains produced the MILDER intervention while moderately suspicious ones produced the strongest -- inverting the very quantity W3 measures, since the metric is a Commit->{Hold,Prune} flip rate. Corrected to Calibrated / Overconfident / Uncertain, which is monotone against the gate ordering, with the derivation and the four-enum note written down so it is not re-opened. W0 STAMP: a commit hash alone is not sufficient. A fixture generated from a dirty crates/jc tree carries a stamp that matches perfectly while the bytes it came from exist nowhere in history -- it reports "current" for something nobody can reproduce, which is worse than no stamp because it manufactures confidence. Now both: a content hash of the ewa_sandwich source (catches a stale fixture at verification time) and a clean-tree refusal in the generator (stops an irreproducible one being created). Not redundant -- different moments. F-MEP-5 RE-PIN NOW VOIDS ITS RUN. "Re-pin honestly and state the reason" was not enough: if a run's verdict survives a threshold changed after seeing that run's numbers, the pins were chosen to fit the data and the pre-registration bought nothing -- disclosure makes that visible, not valid. The motivating run is marked VOID (numbers still banked), revised pins declared, and only a fresh run may be judged. F-MEP-3 PERMUTES THE CHAIN LABEL, not the (p,o,n) record. shuffle_beliefs_null permutes records, but W1's observation unit is one label per chain (fixed last round), and record-permutation does not preserve chain-level class counts -- a redeal could concentrate events onto one chain and empty another, making the null a distribution over a different quantity than the observed statistic. Permuting the half-1 label vector preserves both class counts exactly, so only the score-label pairing is destroyed. Degenerate draws are then impossible unless the observed data already failed the floors; if that invariant breaks at runtime the probe stops rather than imputing 0.0, which would drag the null mean down and make the +2 sigma bar easier to clear. Supersession index regenerated (no diff -- no ruled symbol changed). --- .claude/plans/mul-ewa-trust-propagation-v1.md | 125 ++++++++++++++++-- 1 file changed, 111 insertions(+), 14 deletions(-) diff --git a/.claude/plans/mul-ewa-trust-propagation-v1.md b/.claude/plans/mul-ewa-trust-propagation-v1.md index 6c19728e8..470b8cc92 100644 --- a/.claude/plans/mul-ewa-trust-propagation-v1.md +++ b/.claude/plans/mul-ewa-trust-propagation-v1.md @@ -182,11 +182,31 @@ forked. **Staleness guard — the one real cost of a fixture over a live call.** A committed table can silently drift from a `jc` that has since changed. - The stamped commit is what makes drift detectable rather than invisible: - W0 is NOT done unless the stamp matches the `jc` source in the checkout, - and a mismatch REGENERATES the fixture rather than being waived. (Step 1 - already runs jc's own Pillar-6/7 provers green in this checkout, so a jc - that has genuinely regressed is caught there, not here.) + The stamp is what makes drift detectable rather than invisible: W0 is NOT + done unless it matches the `jc` source in the checkout, and a mismatch + REGENERATES the fixture rather than being waived. (Step 1 already runs + jc's own Pillar-6/7 provers green in this checkout, so a jc that has + genuinely regressed is caught there, not here.) + + **A commit hash alone is NOT a sufficient stamp** (CodeRabbit, #1074). A + commit id describes what was *committed*, not what is on disk: a fixture + generated from a dirty working tree — uncommitted edits under + `crates/jc` — carries a stamp that matches perfectly while the bytes it + was produced from exist nowhere in history. The guard would report + "current" for a fixture nobody can reproduce, which is worse than no + stamp, because it manufactures confidence. Both halves are therefore + required: + - **Content hash:** the stamp includes a hash of the actual + `jc::ewa_sandwich` source text the generator ran against, and W0 + verifies it against the checkout's current bytes — so ANY edit, + committed or not, invalidates the fixture. + - **Clean-tree requirement:** the generator REFUSES to emit a fixture + while `crates/jc` has uncommitted changes, so a stamped artifact always + corresponds to a reachable commit as well as to specific bytes. + + The two are not redundant: the content hash catches a stale fixture at + *verification* time, the clean-tree check stops an irreproducible one from + being *created*. **W1 — the information probe (STOP GATE for everything below; Sonnet arms, Opus adjudication).** Over real multi-hop chains (KJV derivation chains @@ -234,10 +254,32 @@ become multiple observations, and never enter the AUC. advantage must beat a stamp-shuffled null on the same chains. **Fully specified, because "beat a null by 2σ" names neither a distribution nor a side** (CodeRabbit, #1074): - - **Redeals: `N_null = 1000`**, each a `shuffle_beliefs_null` redeal of the - S4 stamps across the SAME chains (the existing SplitMix64 Fisher-Yates - shuffle unit and seed formula are unchanged); seeds are - `base_seed + i` for `i` in `0..1000`, so the null set is reproducible. + - **Redeals: `N_null = 1000`**, seeds `base_seed + i` for `i` in `0..1000`, + so the null set is reproducible. The SplitMix64 Fisher-Yates algorithm + and seed formula are `shuffle_beliefs_null`'s, unchanged. + - **The permuted unit is the CHAIN-LEVEL BINARY LABEL, not the individual + `(p, o, n)` record** (CodeRabbit, #1074). `shuffle_beliefs_null` permutes + records, but W1's observation unit is one label per chain (fixed in the + previous round), and permuting records does NOT preserve the chain-level + class counts — a redeal could concentrate several events onto one chain + and empty another, so the null distribution would be over a *different* + quantity than the observed statistic and could not bound it. Each redeal + therefore permutes the half-1 label vector, which preserves the positive + and negative CHAIN counts exactly in every draw. That is what makes it a + genuine permutation null: only the *pairing* between suspicion score and + label is destroyed, never the class balance. + (Noted as an interaction, not a defect in `shuffle_beliefs_null` — the + function is right for the corpus-scale belief-structure question it was + written for; it is the unit that had to follow W1's observation unit.) + - **Degenerate redeal behaviour.** Because the permutation preserves class + counts, a redeal can only be single-class if the observed half-1 data + already was — which the per-half floors (`≥ 10` of each class) and the + single-class guard both reject before the null is ever built. So no + redeal can produce an undefined AUC that the observed run did not + already fail on. If that invariant is ever violated at runtime, the probe + stops as **UNDERPOWERED** and reports the offending draw rather than + substituting a value for its ΔAUC — an imputed `0.0` would silently drag + the null mean toward zero and make the `+2σ` bar easier to clear. - **Statistic: `ΔAUC` itself** — recomputed on half 1 under each redeal, giving a null distribution of the SAME quantity the BUY rule reads. A null over some other statistic would not bound the decision being made. @@ -365,11 +407,55 @@ earlier wording paired "at or above the 90th ⇒ `Overconfident`" with a ties-to-lower-suspicion rule, which assigned a value equal to `p90` two different textures): -| condition | texture | -|---|---| -| `trace ≤ p50` | `Calibrated` | -| `p50 < trace ≤ p90` | `Uncertain` | -| `p90 < trace` | `Overconfident` | +| condition | texture | resulting `GateDecision` | strength | +|---|---|---|---| +| `trace ≤ p50` | `Calibrated` | `Flow` | proceed | +| `p50 < trace ≤ p90` | `Overconfident` | `Hold` | pause | +| `p90 < trace` | `Uncertain` | `Block` | veto | + +**The bucket order is DERIVED from `GateDecision::from_axes`, not from the +variant names' English connotations** — and getting this backwards was a real +defect in an earlier draft of this table (found 2026-08-29 while verifying a +CodeRabbit finding that was itself wrong about which enum applies; see the +note below). The verified call chain is: + +`trace ratio` → `TrustTexture` (this table) → **`GateDecision::from_axes(texture, flow)`** +→ `KanbanColumn::advance_on_gate(&GateDecision)` + +`advance_on_gate` takes a **`&GateDecision`** — it never sees a `TrustTexture` +directly, so this table only matters through `from_axes`, which is documented +in-source as "the ONE place this mapping lives". Reading `from_axes` +(`contract/src/mul.rs`) with `FlowState` held at its locally-assessed value +(this plan never varies it): `Uncertain` → **`Block`** under EVERY flow state; +`Overconfident` → **`Hold`**; `Calibrated | Underconfident` in +`Flow`/`Transition` → **`Flow`**. + +So gate strength runs `Flow < Hold < Block`, and the mapping must be MONOTONE +in suspicion against THAT ordering. The earlier draft put `Uncertain` in the +middle bucket and `Overconfident` at the top, which meant the **most** +suspicious chains produced the **milder** intervention (`Hold`) while +moderately suspicious ones produced the strongest (`Block`) — inverting the +very quantity W3 measures, since the metric is a `Commit→{Hold,Prune}` flip +rate. The corrected order above is monotone: more propagated uncertainty +never yields a weaker gate outcome. + +The naming reads oddly for one bucket and is nonetheless correct: +`Uncertain` is documented as "not enough data to assess", which is exactly a +chain whose propagated covariance has blown up — and the contract routes that +to the strongest intervention. `Overconfident` ("felt >> demonstrated") sits +below it at `Hold`. + +> **⊘ On the enum this table names.** A review pass asked for these buckets to +> be mapped onto `Crystalline / Solid / Fuzzy / Murky`. That is a DIFFERENT +> `TrustTexture` — four distinct types share the name (`contract::mul`, +> `causal-edge::layout`, `lance-graph-planner::mul::trust` with a fifth +> `Dissonant` variant, and `arigraph::orchestrator`). The one on this path is +> **`contract::mul::TrustTexture`**, because that is what `from_axes` takes. +> `causal-edge/src/layout.rs` carries an explicit in-source ruling against +> building the requested cast — *"Canonical: NONE — both are domain-correct +> and should keep distinct names. Do not build a cast on the old claim."* — +> so the remap is not merely unnecessary here, it is forbidden. Recorded so a +> later session does not re-open it. Both boundaries close DOWNWARD, which is the ties-to-lower-suspicion rule stated as arithmetic rather than as a separate sentence that can contradict @@ -430,6 +516,17 @@ changed nothing" when the truth is "the arm never ran." runs and the pins prove badly placed, they are RE-PINNED with the measurement and the reason stated, never quietly relaxed to convert a failing gate into a passing one. + - **A re-pin VOIDS the run it came from** (CodeRabbit, #1074). "Re-pin + honestly and state the reason" is not enough on its own: if the current + run's verdict survives a threshold changed *after* seeing that run's + numbers, the pins were effectively chosen to fit the data and the + pre-registration bought nothing — the disclosure makes it visible, not + valid. So the procedure is: the run that motivated the change is marked + **VOID** and its verdict discarded (its numbers are still banked, as + motivation for the new pins), the revised pins are declared, and only a + FRESH run under those pins may be evaluated pass/fail. There is no path + by which the same execution both justifies a threshold and is judged by + it. - **No wiring lands in W3.** It is a probe against the existing `advance_on_gate`; changing the gate's signature or default is explicitly out of scope for v1. From 0c140b5a0c2932c94aa8b26cef1c2c2650a22bc4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 00:36:13 +0000 Subject: [PATCH 11/13] plan(mul-ewa): qualify the W3 decision column on FlowState (and correct the variant set) CodeRabbit withdrew the TrustTexture finding after running its own verification -- the W3 path is contract::mul::TrustTexture, from_axes owns the texture->decision mapping, advance_on_gate receives only &GateDecision, and docs/TYPE_DUPLICATION_MAP.md forbids the cast it had asked for. It then raised a real residual defect in the corrected table, which this commit fixes. THE DEFECT. from_axes is texture-AND-flow, so the table's decision column was wrong read unconditionally: (Calibrated, Anxiety) yields Hold via the (_, Anxiety) arm, not Flow. TWO CORRECTIONS TO THE FINDING AS STATED. (1) The condition is not "non-Anxiety". FlowState has FOUR variants (Flow, Boredom, Transition, Anxiety), and (Calibrated, Boredom) also yields Hold -- via the `_` fallthrough rather than the Anxiety arm. Requiring only non-Anxiety would have left Boredom masking identically. The condition is FlowState in {Flow, Transition}. (2) It needs no restriction on the cohort, because it is already guaranteed for every chain the metric measures. Verified against source rather than assumed: the flip-rate denominator is chains whose LOCAL arm reaches Commit; advance_on_gate reaches advance() only on GateDecision::Flow; and Flow is emitted by exactly ONE arm of from_axes, (Calibrated | Underconfident, Flow | Transition). So a denominator chain necessarily had FlowState in {Flow, Transition}, and the plan holds FlowState fixed across arms, so the propagated arm reads the same state. Restricting the cohort would have dropped nothing while implying the guarantee was a choice. Recorded anyway, with the inertness consequence spelled out (under Anxiety or Boredom, Calibrated and Overconfident both give Hold, so the p50 cut point is structurally inert and only p90 can flip), because a reader applying the table outside the denominator would be misled, and because a future change to the denominator would silently break the guarantee rather than the table. Supersession index regenerated (no diff -- no ruled symbol changed). --- .claude/plans/mul-ewa-trust-propagation-v1.md | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/.claude/plans/mul-ewa-trust-propagation-v1.md b/.claude/plans/mul-ewa-trust-propagation-v1.md index 470b8cc92..4ffa6cdcc 100644 --- a/.claude/plans/mul-ewa-trust-propagation-v1.md +++ b/.claude/plans/mul-ewa-trust-propagation-v1.md @@ -407,12 +407,41 @@ earlier wording paired "at or above the 90th ⇒ `Overconfident`" with a ties-to-lower-suspicion rule, which assigned a value equal to `p90` two different textures): -| condition | texture | resulting `GateDecision` | strength | +| condition | texture | resulting `GateDecision` † | strength | |---|---|---|---| | `trace ≤ p50` | `Calibrated` | `Flow` | proceed | | `p50 < trace ≤ p90` | `Overconfident` | `Hold` | pause | | `p90 < trace` | `Uncertain` | `Block` | veto | +† **The decision column holds for `FlowState ∈ {Flow, Transition}`, and that +is GUARANTEED for every chain this metric measures — it is not an added +restriction** (CodeRabbit, #1074, raising the unconditional reading; the +variant set below is a correction to the finding as stated). + +`from_axes` is texture-AND-flow, so the column is genuinely conditional: +`(Calibrated, Anxiety)` → `Hold` via the `(_, Anxiety)` arm, and +`(Calibrated, Boredom)` → `Hold` via the `_` fallthrough. **`FlowState` has +FOUR variants** (`Flow`, `Boredom`, `Transition`, `Anxiety`), so the +condition is NOT "non-`Anxiety`" — `Boredom` masks identically. Under either +of those two states `Calibrated` and `Overconfident` BOTH yield `Hold`, so +the `p50` cut point would be structurally **inert** and only the `p90` +boundary could still produce a flip. + +That case cannot arise in the measured population, by construction rather +than by stipulation. The flip-rate denominator is chains whose LOCAL arm +reaches `Commit`; `advance_on_gate` reaches `advance()` only on +`GateDecision::Flow`; and `Flow` is emitted by exactly ONE arm of +`from_axes` — `(Calibrated | Underconfident, Flow | Transition)`. So a chain +in the denominator necessarily had `FlowState ∈ {Flow, Transition}`, and +since this plan holds `FlowState` FIXED across both arms, the propagated arm +reads the same state. Every chain the metric scores therefore sits in the +regime where the column above is exact. + +Stated because a reader applying this table OUTSIDE the denominator — to the +whole cohort, or to a chain that never gated `Commit` — would be misled, and +because a future change to the denominator would silently break the +guarantee rather than the table. + **The bucket order is DERIVED from `GateDecision::from_axes`, not from the variant names' English connotations** — and getting this backwards was a real defect in an earlier draft of this table (found 2026-08-29 while verifying a From 262e22dc1f7bef4698cd78a5cbb3689e9fb636b3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 06:37:37 +0000 Subject: [PATCH 12/13] plan(mul-ewa): state the two hinges, and gate the sign error the stats rounds missed Operator framing (2026-08-29): the whole thing is two hinges -- the MUL revamp (Dunning-Kruger overconfidence vs trusted epistemic knowledge vs counterfactual, as thesis/antithesis/synthesis; the impact of overconfidence, and how grounded the known and indirect intermediate unknowns are) and the EWA sandwich in jc (adjacent to the 3DGS gaussian-splat spatial stack, filling known unknowns with Oberflaechenspannung vs inheriting from the HHTL parent vs dispatching thinking styles to reason) -- with the danger that an epistemic frontier handed to math goes circular, or becomes accidental entropy-based intelligence. Measured against the file: none of it was written down. Dunning 0, counterfactual 0, antithesis 0, Oberflaechenspannung 0, thinking style 0, circular 0, entropy 0, frontier 0, known-unknown 0 -- in 587 lines. Five review rounds hardened the decision procedure around a quantity whose direction nobody had checked. Checking it surfaced three defects, all derivable on paper, none statistical: 1. THE SUSPICION SCORE IS INVERTED. jc's Sigma is a covariance (its own header: world-space 3DGS covariance pushed to image space; consumer is ndarray::hpc::splat3d). Covariance up = uncertainty up = suspicion up, which is the direction the TrustTexture table uses. But the declared control is M_k = sqrt(per-hop trust)*I with trust in [0,1], giving trace ratio = product of t_k, which SHRINKS as trust falls and hops accumulate. So a distrusted 5-hop chain scores Calibrated (proceed) and a trusted 2-hop chain scores Uncertain (veto). Note jc defines M_k as sqrt(Sigma_k), the step-Jacobian of the edge's COVARIANCE; substituting a scalar trust is the transplant, and it carried no unit or direction check. F-MEP-0b now requires D-MEP-1 to declare whether Sigma is a covariance or a precision, and W0 to carry a worked 2-hop numeric example proving lower per-hop trust yields strictly higher suspicion, before any run starts. Bounded inflation is required if covariance -- unbounded 1/sqrt(t) diverges at t -> 0, trading an inverted score for an explosive one. 2. THE READOUT IS HOP-COUNT DOMINATED. product of t_k ~= t_bar^n, so both arms would largely rank by path length; longer chains plausibly do break more often, so the AUC could clear its bar while discovering nothing that needed covariance propagation. That is the accidental-entropy failure, concretely. New protocol row: report Spearman rho(suspicion, hop count) for both arms and AUC stratified by hop count (2/3/4/5+), and require the EWA arm to clear its bar within at least one stratum, not only aggregate. 3. CIRCULARITY WAS NEVER NAMED. If a propagated Sigma becomes a TrustTexture that gates a cycle whose outcome updates the trust seeding the next Sigma, the operator manufactures its own justification. W3's no-wiring fence already prevents this; the epistemic reason for it is now stated. Also records that the fill is a CHOICE of three -- Oberflaechenspannung (EWA), HHTL parent inheritance, thinking-style reasoning -- and that this plan measures only EWA against a scalar baseline, so a BUY licenses "EWA beats naive decay" and NOT "EWA is the right way to fill this gap". Supersession index regenerated. --- .claude/plans/mul-ewa-trust-propagation-v1.md | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/.claude/plans/mul-ewa-trust-propagation-v1.md b/.claude/plans/mul-ewa-trust-propagation-v1.md index 4ffa6cdcc..37fe13b17 100644 --- a/.claude/plans/mul-ewa-trust-propagation-v1.md +++ b/.claude/plans/mul-ewa-trust-propagation-v1.md @@ -29,6 +29,129 @@ anything: **does sandwich-propagated uncertainty rank multi-hop derived beliefs differently — and better — than the naive scalar decay every stack defaults to?** If not, NO-BUY, numbers banked. +## §0b The two hinges, and the danger of handing the epistemic frontier to math + +**(Operator framing, 2026-08-29 — recorded because §0-§4 specified a +measurement protocol in great detail while never stating what it is FOR, +and never stating its main risk. Five review rounds hardened the decision +procedure around a quantity nobody had checked the direction of.)** + +This plan has exactly **two hinges**. + +**HINGE 1 — the MUL revamp.** Dunning-Kruger overconfidence vs. trusted +epistemic knowledge vs. counterfactual, run as hypothesis testing: thesis, +antithesis, synthesis. The question is not "what is the trust value" but +**what is the IMPACT of overconfidence**, and **how grounded are the known +unknowns and the indirect intermediate unknowns, relative to the knowns**. +A multi-hop chain is exactly where those three separate: the endpoints may +be knowns, while every intermediate hop is an indirect unknown whose +uncertainty is *asserted* rather than observed. + +**HINGE 2 — the EWA sandwich, borrowed from 3DGS.** `jc::ewa_sandwich` is +a **rendering** operator: `Σ_image = J·W·Σ·Wᵀ·Jᵀ` pushes a world-space +3DGS covariance to image space, and its production consumer is +`ndarray::hpc::splat3d` (`gaussian.rs` / `project.rs` / `spd3.rs`). Using it +epistemically is a **transplant**, not an application. It fills an +indirect intermediate unknown the way surface tension spans a gap +(*Oberflächenspannung*): the membrane's shape is set by boundary conditions +plus a minimisation principle — by a PRINCIPLE, never by evidence. + +### The fill is a CHOICE between three, and EWA is only one of them + +A known unknown at an intermediate hop can be filled by: + +| # | strategy | what supplies the value | failure mode | +|---|---|---|---| +| 1 | **Oberflächenspannung** — EWA sandwich | a minimisation principle over boundary conditions | plausible everywhere, grounded nowhere | +| 2 | **HHTL parent inheritance** | the parent node in the cascade (`inherits_from`) | inherits the parent's staleness and its errors | +| 3 | **Thinking styles → reasoning** | NARS dispatch (`thinking/style.rs`, `nars/inference.rs`) | costs a real inference step | + +The plan below measures **(1) against a scalar baseline only**. It does NOT +measure (1) against (2) or (3), so a BUY on W1 licenses "EWA beats naive +decay" and **NOT** "EWA is the right way to fill this gap". Stated so a +later session cannot read a BUY as the stronger claim. + +### The danger, named + +**An epistemic frontier handed to math becomes circular, or becomes +accidental entropy-based intelligence.** Three concrete forms, all live in +this plan as written: + +- **Circularity.** If a propagated Σ becomes a `TrustTexture`, which gates + a cycle, whose outcome updates the trust that seeds the next Σ, the loop + closes and the operator manufactures its own justification. W3 must be a + **one-way probe**: read the gate decision, never write back into the trust + that fed it. (This is why W3 is a probe against the existing + `advance_on_gate` and changes no default — the fence already exists for a + different reason; this is the epistemic reason for it.) +- **Accidental entropy-based intelligence.** See F-MEP-0c: under the + declared control, the readout is dominated by hop count, so the probe can + score well while ranking nothing but path length. +- **Direction never checked.** See F-MEP-0b: the plan pairs a + covariance-SHRINKING transform with a covariance-AS-SUSPICION readout. + +### F-MEP-0b (ON-PAPER, before any run) — declare Σ's kind, then check the sign + +**This is derivable without data and MUST be settled before W0 executes.** + +`jc`'s Σ is a **covariance** (`ewa_sandwich_3d.rs`: "world-space covariance +matrices Σ ∈ ℝ³ˣ³ ... pushed forward to image-space"). Under a covariance +reading, larger trace = MORE uncertainty = MORE suspicion, which is the +direction §4's `TrustTexture` table already uses (`p90 < trace` → +`Uncertain` → `Block`). + +But the declared control is `M_k = √(per-hop trust)·I` with trust drawn from +`NarsTruth` (`frequency`/`confidence`, both in `[0,1]`), giving +`Σ_n = (∏ t_k)·Σ₀` and `trace ratio = ∏ t_k`. That quantity **SHRINKS as +trust falls and as hops accumulate**. So as written: + +> a distrusted 5-hop chain scores as `Calibrated` (proceed), and a trusted +> 2-hop chain scores as `Uncertain` (veto). **The suspicion score is +> inverted.** + +Note also that `jc` defines `M_k = sqrt(Σ_k)` — the step-Jacobian **of the +k-th edge's covariance**. Substituting a scalar trust for a step covariance +is the transplant itself, and it carried no unit or direction check. + +**One of the two must flip, and the plan must SAY which:** + +- **Σ as covariance (jc's own reading, the default):** keep the texture + table, and `M_k` must INFLATE with distrust — a bounded form such as + `M_k = (1/√t_k)·I` or a distrust-scaled `√(1 + λ(1−t_k))·I`. A bounded + form is required: unbounded `1/√t` diverges as `t → 0`, replacing an + inverted score with an explosive one. +- **Σ as precision (inverse covariance):** `√(trust)` scaling is then + correct, and the **texture table inverts instead** — low trace becomes + high suspicion. + +**Gate:** D-MEP-1 must state the kind, and W0 must carry a **worked 2-hop +numeric example** showing that a chain with strictly lower per-hop trust +receives a strictly HIGHER suspicion score. If it does not, the run is not +started. This costs minutes on paper and would otherwise burn an entire +probe to rediscover a sign error — the BUY rule's `AUC(b) > 0.5` condition +would catch it, but only after the fact, and only as an unexplained +"both arms inverted". + +### F-MEP-0c (ON-PAPER) — the hop-count confound + +With per-hop trusts of similar magnitude `t̄`, `∏ t_k ≈ t̄ⁿ`: the readout is +**dominated by `n`, the hop count**, not by the epistemics of any hop. Both +arms would then largely rank by path length, and since longer chains +plausibly do carry more S4 errors, the AUC could clear its bar while +measuring nothing but "longer chains break more often" — a true fact that +requires no covariance propagation to discover. That is the +accidental-entropy failure in its concrete form. + +The cohort filter (`hop-length ≥ 2`) does not address this; it removes the +degenerate case only. + +**Gate — added to the §4 protocol:** report the **Spearman ρ between the +suspicion score and hop count** for both arms, and report **AUC stratified +by hop count** (bucketed `2`, `3`, `4`, `5+`). A BUY additionally requires +the EWA arm to clear its ΔAUC bar **within at least one stratum**, not only +in aggregate. An arm that beats the baseline only across strata is ranking +by length, and the plan says so rather than banking it. + ## §1 What is established (verified at HEAD this session, file:line) - **Zero MUL↔EWA wiring exists.** No `jc`/`ewa`/`sandwich` reference in @@ -307,6 +430,7 @@ run and requires a re-pin with the change stated: | minimum sample | **n ≥ 200** qualifying chains **in total, AND per-half floors that the total does not imply** (CodeRabbit, #1074): each half independently needs **≥ 50 chains, ≥ 10 positive and ≥ 10 negative S4 events**. A cohort-level `n` says nothing about how it landed either side of a hash split, so the total is a necessary and NOT a sufficient condition. Below ANY of these the probe reports UNDERPOWERED with the failing count named, and stops — a valid, honest exit. The floors are declared here, before any run, precisely so they cannot be relaxed after seeing which one bites. | | split | **deterministic, chain-level, leakage-safe**: partition key = the chain's ROOT SUBJECT id (never the chain id — two chains sharing a root would otherwise straddle the split and leak); half = `blake3(root_subject_id ‖ "mep-w1-v1")[0] & 1`. Half **0** FITS — every free choice (which suspicion ranking, any construction detail left open by D-MEP-1) is fixed here, and its ΔAUC is DIAGNOSTIC ONLY, never the number the BUY rule reads. Half **1** EVALUATES: **the ΔAUC the BUY threshold is applied to is computed on half 1 ALONE, exactly once, with no re-fitting** — reporting a half-0 ΔAUC or a pooled ΔAUC as the decision number is the defect this row exists to prevent. The literal salt is part of the pre-registration, so a re-run reproduces the identical partition. | | comparison metric | AUC of suspicion-rank vs the binary S4 error signal | +| **hop-count control** | **Report Spearman ρ(suspicion, hop count) for BOTH arms, and AUC stratified by hop count** (buckets `2`, `3`, `4`, `5+`), with per-bucket counts. Required because `∏ t_k ≈ t̄ⁿ` makes the readout length-dominated (F-MEP-0c): an arm can clear its bar while ranking nothing but path length, which is a true fact about long chains that needs no covariance propagation to discover. **A BUY additionally requires the EWA arm to clear ΔAUC ≥ 0.05 within at least ONE stratum meeting the per-half floors** — aggregate-only separation is reported as length-ranking, not banked as a win. | | **degenerate AUC** | AUC is UNDEFINED when a half carries no positive or no negative S4 event, and `n ≥ 200` does **not** prevent that (CodeRabbit, #1074). Both halves' **class counts are REPORTED unconditionally**; if either half is single-class the probe stops as **UNDERPOWERED** — never NO-BUY, since a degenerate split is a statement about the cohort, not about the operator under test, and never a computed AUC on a one-class half. This is the ZERO-count guard only; the ≥ 10-per-class floors in the minimum-sample row are what stop a *technically* two-class half from producing an AUC too unstable to decide on. | | BUY threshold | **All three, on half 1:** (i) `AUC(b) > 0.5` — the EWA arm must be predictive AT ALL, not merely less anti-predictive than the baseline. Without this, `AUC(b) = 0.20` over `AUC(a) = 0.10` clears a ΔAUC bar while both arms rank *backwards*, and the "win" is a bigger error (CodeRabbit, #1074). (ii) ΔAUC **≥ 0.05** over arm (a). (iii) clearing the F-MEP-3 null by ≥ 2σ of the shuffle distribution. An arm that is anti-predictive (`AUC ≤ 0.5`) is a NO-BUY however large its ΔAUC — and if BOTH arms land below 0.5 the probe reports that inversion explicitly, since a systematically backwards ranking is a finding about the suspicion construction, not a quiet NO-BUY. | From 4ab1db60524ae8b79068e5bcd459a2c8b524d044 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 06:43:51 +0000 Subject: [PATCH 13/13] plan(mul-ewa): add F-MEP-0a (capability gate) and seam to the quadrant plan Operator ruling: 262e22dc was a good emergency brake but stopped one layer early. Covariance-vs-precision fixes the OPERATOR; it does not fix what the operator is PERMITTED TO DO. Get the sign right, normalise hop count, bound the inflation, obtain a gorgeous AUC -- and still have built a flawlessly calibrated machine for performing the wrong epistemic operation. F-MEP-0a now gates F-MEP-0b: D-MEP-1 must declare the EWA sandwich's capabilities before specifying it. Redistribute/locate tension: yes. Rank where counterfactual exploration is worth compute: yes. Generate candidate completions: only marked hypothetical. Eliminate worlds by itself: no. Mint evidence: never. Raise empirical trust from its own output: never. Separates three things that were collapsing into the word "uncertainty": epistemic state (provenance), geometric uncertainty (Sigma/EWA/ Oberflaechenspannung), and counterfactual ambiguity (mutually incompatible completions). Sigma represents the second, cannot manufacture the third, and the third must never silently overwrite the first. Sharpens the anti-circularity invariant: no operation whose inputs are entirely graph-internal may convert a hypothesis into epistemic evidence merely by reducing its uncertainty. Internal reasoning CAN create knowledge about the hypothesis space -- constraint elimination and contradiction are real -- so the ban is on laundering that into a claim about external reality. Three mechanisms kept apart: elimination by logical contradiction (may raise logical certainty), discrimination by observation (may raise empirical certainty), preference by propagated geometry (raises neither; changes only where to look). Scope-corrects 0b's own phrase. "Lower trust yields higher suspicion" is a sign check on the operator, NOT an ontology: low trust != uncertainty, high covariance != epistemic ignorance, high entropy != intelligent doubt. MUL compares epistemic posture against epistemic support, so Overconfident means the graph collapsed onto one explanation while viable alternatives were never discriminated. THE SEAM, which is the real finding. This is not new architecture -- epistemic-quadrant-materialization-v1.md (709 lines) and probe_sudoku_teacher.rs (1859 lines) already implement the membrane. Its G3: bifurcation clones the slab as a counterfactual world, propagates to contradiction, and ONLY THE ELIMINATION RETURNS; the fork's positive guess never enters the main slab. G4 measures the cost of refusing to fork. Live surfaces: contract::scenario::ScenarioBranch, contract::pearl_junction. That plan carries MUL x9 and ZERO EWA/TrustTexture/Oberflaechenspannung; this plan carried zero quadrant vocabulary; neither cited the other. Two halves of one architecture in two plans with no seam -- the rediscovery tax CLAUDE.md "Consult before you guess" exists to prevent. D-MEP-1 must read it before specifying Sigma. Adds F-MEP-0d, the closed-graph monotonicity falsifier: no new observations, no oracle calls, run MUL -> EWA -> thinking -> MUL for 1/10/1000 cycles. Reorganised attention, hypotheses, sharper questions and latent contradictions are all permitted; aggregate epistemic authority rising with cycle count is not. A STOP gate rather than a diagnostic, run on synthetic closed graphs before W1's cohort since it needs no S4 events and can fail early and cheap. Supersession index regenerated. --- .claude/plans/mul-ewa-trust-propagation-v1.md | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/.claude/plans/mul-ewa-trust-propagation-v1.md b/.claude/plans/mul-ewa-trust-propagation-v1.md index 37fe13b17..79545da53 100644 --- a/.claude/plans/mul-ewa-trust-propagation-v1.md +++ b/.claude/plans/mul-ewa-trust-propagation-v1.md @@ -29,6 +29,152 @@ anything: **does sandwich-propagated uncertainty rank multi-hop derived beliefs differently — and better — than the naive scalar decay every stack defaults to?** If not, NO-BUY, numbers banked. +## §0a The permission gate — what epistemic operation is this operator ALLOWED to perform? + +**(Operator ruling, 2026-08-29. This gate comes BEFORE F-MEP-0b. Settling +covariance-vs-precision fixes the OPERATOR; it does not fix what the operator +is permitted to DO. Get the sign right, normalise hop count, bound the +inflation, obtain a gorgeous AUC — and you can still have built a flawlessly +calibrated machine for performing the wrong epistemic operation.)** + +### Three things currently collapsing into one word, "uncertainty" + +| # | layer | carrier | what it answers | +|---|---|---|---| +| 1 | **Epistemic state** | provenance: observed / inherited / absent | what is actually known, and *how* it came to be known | +| 2 | **Geometric uncertainty** | Σ, EWA, *Oberflächenspannung* | where tension propagates and concentrates | +| 3 | **Counterfactual ambiguity** | `{W₁ … Wₙ}` — mutually incompatible completions | which admissible worlds still explain the evidence | + +**Σ can represent #2 beautifully. It cannot manufacture #3. And #3 must never +silently overwrite #1.** Everything below follows from those two sentences. + +### F-MEP-0a — the capability declaration (gates F-MEP-0b) + +D-MEP-1 must state, before the operator is specified at all, which of these +the EWA sandwich is permitted to perform: + +| operation | permitted? | +|---|---| +| redistribute / locate epistemic tension | **yes** | +| rank places where counterfactual exploration is worth compute | **yes** | +| generate candidate completions | **maybe — marked HYPOTHETICAL, never merged into #1** | +| eliminate worlds by itself | **no** | +| mint evidence | **never** | +| raise empirical trust from its own output alone | **never** | + +EWA may say *"given what we currently know, unresolved tension concentrates +HERE."* It may **not** say *"therefore this region is now better known."* + +### The anti-circularity invariant, in its sharp form + +> **No operation whose inputs are entirely graph-internal may convert a +> hypothesis into epistemic evidence merely by reducing its uncertainty.** + +This SUPERSEDES the softer §0b wording. Internal reasoning genuinely *can* +create knowledge about the hypothesis SPACE — constraint elimination can prove +only two worlds remain, and a contradiction can kill a world with no new +observation. That is real and must not be forbidden. What is forbidden is +laundering it into a claim about external reality. Three kinds, kept apart: + +| mechanism | may increase | +|---|---| +| hypothesis **elimination by logical contradiction** | logical certainty | +| hypothesis **discrimination by observation** | empirical certainty | +| hypothesis **preference by propagated geometry** | **neither** — it changes only where to look | + +### ⊘ Scope correction to F-MEP-0b's own wording + +§0b's gate phrase *"lower per-hop trust yields strictly HIGHER suspicion"* is +**a sign check on the operator, NOT an ontology.** It is necessary and it is +not a definition. Explicitly: + +- low trust **≠** uncertainty +- high covariance **≠** epistemic ignorance +- high entropy **≠** intelligent doubt + +Those are signals MUL may CONSUME. They are not what MUL IS. MUL's actual job +is comparing epistemic **posture** against epistemic **support** — so +`Overconfident` means *the graph collapsed onto one explanation while viable +alternatives were never discriminated*, and a high-uncertainty reading can be +perfectly **`Calibrated`** when several coherent worlds genuinely remain. + +### The ordering this implies — geometry decides WHERE, never WHAT + +``` +EWA "there is epistemic tension here" (attention geometry) +HHTL "these completions are admissible" (constraint field) +thinking style "here are competing explanations" (the intellectual act) +counterfactual "they disagree at S*" (differential probe) +REALITY "S* = x" ← the only evidence arrow +MUL "NOW you may update trust" +``` + +Thinking styles belong **after** EWA exposes tension, never inside the +interpolation. A renderer is rewarded when an interpolation looks right; a +reasoning system is in deep trouble when a plausible interpolation silently +graduates into a fact. + +### ★ This is NOT new — the membrane is already implemented (seam, not invention) + +**`.claude/plans/epistemic-quadrant-materialization-v1.md` (709 lines) and +`lance-graph-planner/examples/probe_sudoku_teacher.rs` (1859 lines) already +build the counterfactual half of this**, and #1074 cited them **zero** times +until now. That plan's **G3** is exactly the membrane above: + +> *"Bifurcation clones the slab as a counterfactual world, propagates to +> contradiction, and **ONLY the elimination returns** — the main slab changes +> at exactly the sanctioned cell, and the fork's positive (wrong) guess never +> appears in it."* + +The fork's NEGATIVE result (an elimination) may cross back into reality; its +POSITIVE guess may not. Its **G4** then measures the cost of refusing to fork +(a fork-refusing policy on a bifurcation-required puzzle does not fully +migrate toward `Wisdom`) — the can-it-fire twin. Related live surfaces: +`contract::scenario::ScenarioBranch`, `contract::pearl_junction`. + +**The split is the actual defect.** That plan carries `MUL` (9 mentions) and +**zero** `EWA` / `ewa_sandwich` / `TrustTexture` / *Oberflächenspannung*; this +plan carried zero of the quadrant vocabulary. Two halves of ONE architecture, +in two plans, with no seam — the rediscovery tax `CLAUDE.md` § "Consult before +you guess" exists to prevent. **D-MEP-1 must read that plan before specifying +Σ**, and any counterfactual construction here routes through its existing +fork/elimination discipline rather than a second mechanism. + +### The pothole is a TYPED ABSENCE, never a filled cell + +An epistemic hole is not missing data awaiting interpolation. It is a +first-class object carrying its own structure: + +``` +UNKNOWN { + alternatives = {A, B, C} // admissible completions + constraints = {...} // what narrows them + discriminators= {...} // signals on which they disagree + evidence_needed = {...} // what reality would have to supply +} +``` + +*"Reality is known up to this equivalence class of worlds"* is **knowledge**, +not a gap. Filling it with a smooth field destroys the very structure that +makes it answerable. + +### F-MEP-0d — the closed-graph monotonicity falsifier (testable, and it bites) + +Take a graph with **no new observations, no oracle calls, no evidence +receipts.** Run MUL → EWA → thinking-style → MUL for `1`, `10`, `1000` cycles. + +**Permitted:** reorganised attention, generated hypotheses, sharpened +questions, contradictions discovered that were already latent. + +**FORBIDDEN:** aggregate epistemic authority rising monotonically with cycle +count. If trust inflates merely because the system thought longer, the seam is +a philosophical perpetual-motion machine and the answer is NO-BUY regardless +of every other number in this plan. + +This is the testable form of the §0b circularity worry, and it is a **STOP +gate, not a diagnostic**: it runs on synthetic closed graphs before W1's real +cohort, because it needs no S4 events and can therefore fail early and cheap. + ## §0b The two hinges, and the danger of handing the epistemic frontier to math **(Operator framing, 2026-08-29 — recorded because §0-§4 specified a