From 506514dc78f6cbef490bd65b29e43e1d01cacb58 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Jul 2026 06:51:52 +0000 Subject: [PATCH 1/4] =?UTF-8?q?probe(helix):=20the=20ruler=20reads=20the?= =?UTF-8?q?=20address=20as=20a=20NUMBER=20=E2=80=94=20intake=20blindness?= =?UTF-8?q?=20kills=20the=20residue=20sweep?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PROBE-CLAM-VS-HELIX-RESIDUE leg 0. The queued sweep (finer address => more deterministic place => less stored residue) did NOT run, and not running it is the result. Two failure modes were pre-registered before any measurement; running them first was decisive. H1 INTAKE IS STRUCTURE-BLIND (confirmed, fatal). CurveRuler::from_place(p) = p % 17 consumes the address as a NUMBER, never as a hierarchy. The same 256 cells carved 4-ary (depth 4) or 16-ary (depth 2) produce the SAME start-offset multiset — a pure rotation. The ruler cannot tell the carvings apart, so a residue-vs-granularity sweep would return a null for a reason unrelated to 4^4. Reporting "4^4 doesn't help the calculator" from that null would have been a false negative manufactured by the intake. The 4^4 ADDRESS result is untouched; this is a finding about the CALCULATOR'S INTAKE. H2 THE "STRIDE 4 ISN'T GOLDEN" SUSPICION IS REFUTED, and the refutation vindicates the shipped constants. Gap spread (lower = more uniform) at the prefix lengths early termination actually consumes: n=2: stride4 9 vs phi-stride11 5 n=3: stride4 5 vs 1 n=4: stride4 1 vs 5 <- the prefix a 4-ary tier consumes n=5: stride4 3 vs 4 n=6: stride4 3 vs 4 At n=4 stride 4 gives gaps [4,4,4,5] — near-perfect tiling — because 4*4 = 16 ~ 17. The comma is what makes the 4-step prefix uniform. Operator framing confirmed in mechanism: stride 4 over 16 covers only 4/16 and retraces one column forever; 17 = 4^2+1 turns the raster into a sweep. STRIDE=4 / MODULUS=17 are MATCHED to the 4-ary use case. H3 THE BLINDNESS IS FIXABLE. Per-tier seeding (one ruler per 2-bit group, folded with its level) preserves ancestry by construction: over 4,662 sampled pairs sharing >=1 address level, flat intake preserves ancestry in 51, per-tier in 1,152 (~23x). The unblock is to feed the ruler the address's hierarchy, not its integer value — a helix API change needing its own review, not done here. Core-First: CONSUMES helix::curve_ruler::CurveRuler; nothing re-derived. Self-caught vacuous gate kept in the record: H3's first run sampled 0 pairs and correctly FAILED — `for a in 0..CELLS as u8` with CELLS=256 is an empty range because 256 as u8 == 0. Fourth instance in this arc of a defect living in the falsifier rather than the measurement. Boundaries: ruler properties only (integer arithmetic over 17). No residue encoded, no CLAM comparison, no cost measured. Brief-gates AGREEMENT and COST remain NOT RUN — blocked on H1, not skipped. Gates 3/3 green; clippy -D warnings clean; fmt applied. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01K3RyLEbuNSHxxB3NTTrGki --- .claude/board/EPIPHANIES.md | 18 ++ .../board/exec-runs/hhtl-intake-blindness.md | 103 +++++++ .../examples/probe_hhtl_intake_blindness.rs | 260 ++++++++++++++++++ 3 files changed, 381 insertions(+) create mode 100644 .claude/board/exec-runs/hhtl-intake-blindness.md create mode 100644 crates/helix/examples/probe_hhtl_intake_blindness.rs diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index 58a8432ba..b01ac85d6 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -1,3 +1,21 @@ +## 2026-07-31 — E-THE-RULER-READS-THE-ADDRESS-AS-A-NUMBER-NOT-A-HIERARCHY-1 — the helix intake is structure-blind (kills the residue sweep before it ran), and the "stride 4 isn't golden" suspicion is REFUTED by the shipped constants being matched to 4-ary + +**Status:** FINDING (measured, 3/3 gates green). **Confidence:** High — pure integer arithmetic over the shipped `CurveRuler`, no sampling. Probe: `crates/helix/examples/probe_hhtl_intake_blindness.rs`; record: `.claude/board/exec-runs/hhtl-intake-blindness.md`. + +**Operator anchors:** *"and CLAM to calculate, that's established"* / *"alternative is using HHTL+ helix residue"* / *"exact address, exact spacial location / perturbation"* / *"4x4 usually only helps with an irrational stride as Pythagorean comma"*. + +**The queued sweep did NOT run, and not running it is the result.** `PROBE-CLAM-VS-HELIX-RESIDUE` was to test whether a finer address shifts work from stored RESIDUE to deterministic PLACE. Two failure modes were pre-registered before any measurement. Running them first was decisive. + +**H1 — the intake is STRUCTURE-BLIND (confirmed; fatal to the sweep).** `CurveRuler::from_place(p) = p % 17` consumes the address as a **number**, never as a hierarchy. The same 256 cells carved 4-ary (depth 4) or 16-ary (depth 2) produce the **same start-offset multiset** (a pure rotation: one bucket 16, the rest 15). The ruler cannot distinguish the carvings, so a residue-vs-granularity sweep would return a null *for a reason unrelated to 4⁴*. **Reporting "4⁴ doesn't help the calculator" from that null would have been a false negative manufactured by the intake.** The 4⁴ ADDRESS result (`E-WORDNET-MAKES-THE-4-ARY-ADDRESS-SEMANTIC-1`, 24.71× out-of-cell band lift, 2.47-hop sub-nibble gap) is untouched — this is a finding about the CALCULATOR'S INTAKE. + +**H2 — my own "stride 4 isn't golden" suspicion is REFUTED, and the refutation vindicates the design.** The worry: `gcd(4,17)=1` buys coverage, not low discrepancy — stride 4's first four steps `0,4,8,12` are one column of the 4×4 raster, while the φ stride over 17 is 11 (`17/φ = 10.51`; C5 pins 11/17). Since HHTL terminates early, short prefixes are what get consumed. Measured gap spread (lower = more uniform): n=2 → 9 vs **5**(φ); n=3 → 5 vs **1**(φ); **n=4 → 1 vs 5** (stride 4 wins decisively); n=5 → **3** vs 4; n=6 → **3** vs 4. **At n=4 — exactly the prefix a 4-ary tier consumes — stride 4 gives gaps `[4,4,4,5]`, near-perfect tiling, because `4·4 = 16 ≈ 17`.** The comma IS what makes the 4-step prefix uniform. The operator's Pythagorean-comma framing is confirmed in mechanism: stride 4 over **16** covers only 4/16 and retraces one column forever; `17 = 4²+1` is what turns the raster into a sweep. **The shipped `STRIDE = 4` / `MODULUS = 17` are matched to the 4-ary use case, not sloppy about φ.** + +**H3 — the blindness is fixable, so this is a direction, not a dead end.** Per-tier seeding (one ruler per 2-bit group, folded with its level) preserves ancestry by construction: over 4,662 sampled pairs sharing ≥1 address level, the flat intake preserves ancestry in **51**, per-tier in **1,152** (≈23×). The unblock is to feed the ruler the address's HIERARCHY, not its integer value — a helix API change that needs its own review. + +**The transferable rule:** *a calculator's INTAKE can silently discard the very structure a probe is trying to measure.* Check what the consumer actually reads before running a sweep over what the producer emits — otherwise a null measures the seam, not the hypothesis. This is the same shape as the falsifier-defect pattern (four instances now: the non-falsifying shuffle, the hair-wide twin window, the inert cover guard, and — self-caught here — an H3 loop that sampled 0 pairs because `256 as u8 == 0` and correctly FAILED rather than passing on zero evidence). + +**Boundaries:** properties of the RULER only (integer arithmetic over 17). No residue encoded, no CLAM comparison, no cost measured. Brief-gates AGREEMENT and COST remain NOT RUN — blocked on H1, not skipped. H2 compares two strides on gap-spread; it does not claim stride 4 is optimal, only that it is not the defect suspected. + ## 2026-07-30 — E-WORDNET-MAKES-THE-4-ARY-ADDRESS-SEMANTIC-1 — the 4⁴ fold turns HHTL adjacency into a *semantic* prior, and the sub-nibble rung is load-bearing (2.47 hops the 16-ary router cannot address) **Status:** FINDING (measured, 5/5 gates green on real WordNet 3.1). **Confidence:** High for the structure claim; explicitly NOT extended to embedding-trained codebooks (see Boundaries). Probe: `crates/lance-graph-contract/examples/probe_wordnet_44_activation.rs`; record: `.claude/board/exec-runs/wordnet-44-activation.md`. diff --git a/.claude/board/exec-runs/hhtl-intake-blindness.md b/.claude/board/exec-runs/hhtl-intake-blindness.md new file mode 100644 index 000000000..6291284b8 --- /dev/null +++ b/.claude/board/exec-runs/hhtl-intake-blindness.md @@ -0,0 +1,103 @@ +# PROBE-HHTL-INTAKE-BLINDNESS (PROBE-CLAM-VS-HELIX-RESIDUE leg 0) — execution record + +Follows the merged `PROBE-WORDNET-44-ACTIVATION` (#875, +`E-WORDNET-MAKES-THE-4-ARY-ADDRESS-SEMANTIC-1`). Operator ruling fixed the +division of labour: the 4⁴ fold is the ADDRESS, **CLAM is the established +CALCULATOR**, **HHTL + helix residue** is the alternative — *"exact address, +exact spacial location / perturbation"*. + +## Files +- NEW `crates/helix/examples/probe_hhtl_intake_blindness.rs` (3 gates). +- Board: `EPIPHANIES.md` prepend, this file. Same commit (board-hygiene rule). + +## Core-First compliance +CONSUMES `helix::curve_ruler::CurveRuler` — nothing re-derived. helix is in-tree +at `crates/helix` (standalone crate, own workspace) and already ships the seam: +`from_hhtl(path, depth)` → place; `index(k) = (start + STRIDE·k) mod MODULUS` +regenerates the interior; `ResidueEdge`/`Signed360` are the stored residue. + +## The sweep was NOT RUN — and that is the finding + +The queued hypothesis (finer address ⇒ more deterministic place ⇒ less stored +residue) was gated behind two PRE-REGISTERED failure modes. Both were run first, +exactly as the brief required. One is fatal to the sweep; the other is REFUTED. + +### H1 — the intake is STRUCTURE-BLIND (failure mode (a): CONFIRMED, fatal) + +`from_place(p) = p % 17` consumes the address as a **NUMBER**, never as a +hierarchy. Feeding the same 256 cells carved 4-ary (depth 4) vs 16-ary (depth 2) +yields the SAME start-offset multiset — a pure rotation: + +``` +4-ary (depth 4): [15,15,15,15,16,15,15,15,15,15,15,15,15,15,15,15,15] +16-ary (depth 2): [15,15,16,15,15,15,15,15,15,15,15,15,15,15,15,15,15] +identical multiset = true +``` + +**Consequence: a residue-vs-granularity sweep cannot carry signal.** Its null +would have been a false negative caused entirely by the intake, and reporting +"4⁴ doesn't help the calculator" from it would have been wrong. Reported as an +INTAKE finding, per the pre-registration. The 4⁴ ADDRESS result is untouched. + +### H2 — stride 4 vs the φ stride 11 (failure mode (b): REFUTED) + +The suspicion: `gcd(4,17)=1` buys coverage, not low discrepancy; stride 4's +first four steps `0,4,8,12` are one column of the 4×4 raster, while the φ stride +over 17 is 11 (`17/φ = 10.51`; C5 pins 11/17 as the golden step). Since HHTL +terminates early, short prefixes are what get consumed. + +Measured gap spread (max−min gap; lower = more uniform): + +| n | stride 4 | gaps | stride 11 (φ) | gaps | +|---|---|---|---|---| +| 2 | 9 | [4,13] | **5** | [6,11] | +| 3 | 5 | [4,4,9] | **1** | [5,6,6] | +| **4** | **1** | **[4,4,4,5]** | 5 | [1,5,5,6] | +| 5 | **3** | [1,4,4,4,4] | 4 | [1,1,5,5,5] | +| 6 | **3** | [1,1,3,4,4,4] | 4 | [1,1,1,4,5,5] | + +**At n=4 — the prefix a 4-ary tier actually consumes — stride 4 is near-perfect +(spread 1) and beats φ (spread 5).** `4·4 = 16 ≈ 17`: the comma is what makes a +4-step prefix tile the circle. The shipped constants are MATCHED to the 4-ary +use case. My suspicion ("a raster wearing a φ-spiral's docstring") is refuted; +this vindicates `STRIDE = 4` / `MODULUS = 17` rather than indicting them. + +Operator framing that produced this check: *"4x4 usually only helps with an +irrational stride as Pythagorean comma."* Confirmed in mechanism — stride 4 over +**16** covers only 4/16 (retraces one column forever); the `+1` is the comma. + +### H3 — the blindness is FIXABLE at the intake, not fundamental + +Per-tier seeding (one ruler per 2-bit group, folded with its level) preserves +ancestry by construction. Over 4,662 sampled cell pairs sharing ≥1 address level: +**flat intake preserves ancestry in 51; per-tier in 1,152 (≈23×).** The unblock +is to feed the ruler the address's HIERARCHY, not its integer value. + +## Verification +- `cargo run --release --manifest-path crates/helix/Cargo.toml --example + probe_hhtl_intake_blindness` → ALL GATES GREEN. +- `cargo clippy --manifest-path crates/helix/Cargo.toml --example + probe_hhtl_intake_blindness -- -D warnings` → clean. +- `cargo fmt --manifest-path crates/helix/Cargo.toml` → applied. + +## A self-caught vacuous gate (kept in the record) +H3's first run sampled **0 pairs** and correctly FAILED: `for a in 0..CELLS as u8` +where `CELLS = 256`, and `256 as u8 == 0`, so the loop was empty. Had the gate +been written to pass on an empty set it would have reported success on zero +evidence. Iterating in `u16` fixed it. Fourth instance in this arc of a defect +living in the falsifier rather than the measurement. + +## Honest boundaries +- H1/H2/H3 are properties of the RULER (integer arithmetic over 17). No residue + was encoded, no CLAM comparison ran, no cost was measured. +- Gates (1) AGREEMENT and (2) COST from the #66 brief remain NOT RUN — they are + blocked on H1 being addressed, not skipped. +- H2 compares two strides on gap-spread only; it does not claim stride 4 is + optimal, only that it is better than φ at n=4..6 and therefore not the defect + the pre-registration suspected. + +## Follow-ons +- Decide whether to extend the ruler with a per-tier intake (H3 shows it works). + That is a helix API change and needs its own review — NOT done here. +- Once the intake sees hierarchy, the original residue-vs-granularity sweep + becomes meaningful and gates (1)+(2) can run. diff --git a/crates/helix/examples/probe_hhtl_intake_blindness.rs b/crates/helix/examples/probe_hhtl_intake_blindness.rs new file mode 100644 index 000000000..572f5cc9b --- /dev/null +++ b/crates/helix/examples/probe_hhtl_intake_blindness.rs @@ -0,0 +1,260 @@ +//! PROBE-CLAM-VS-HELIX-RESIDUE, leg 0 — **the intake check that must run before +//! the residue sweep**, and it kills the sweep as specified. +//! +//! ## Why this exists +//! +//! `PROBE-WORDNET-44-ACTIVATION` (merged, `E-WORDNET-MAKES-THE-4-ARY-ADDRESS-SEMANTIC-1`) +//! measured that a 4⁴ address carries real taxonomic ancestry and that the +//! sub-nibble rung is worth **2.47 WordNet hops** the 16-ary `NiblePath` cannot +//! express. The operator then fixed the division of labour: the fold is the +//! ADDRESS, **CLAM is the established CALCULATOR**, and **HHTL + helix residue** +//! is the alternative calculator — *"exact address, exact spacial location / +//! perturbation"*. +//! +//! The queued hypothesis was: a finer address makes more of the answer +//! deterministic PLACE and less of it stored RESIDUE, so residue should shrink +//! as address granularity rises. Two failure modes were **pre-registered** before +//! any measurement. This probe runs both. One is confirmed and it is fatal to the +//! sweep; the other is REFUTED, and its refutation vindicates the shipped design. +//! +//! ## H1 — the intake is STRUCTURE-BLIND (pre-registered failure mode (a)) +//! +//! [`CurveRuler::from_hhtl(path, depth)`] is `from_place(path + depth)`, and +//! [`CurveRuler::from_place`] is `place % 17`. The address therefore enters the +//! ruler **as a number, never as a hierarchy**. Feed the same 256 cells carved +//! 4-ary (4 levels × 2 bits) or 16-ary (2 levels × 4 bits) and the ruler receives +//! the same 256 integers, so it produces the same start-offset multiset — the +//! carvings are indistinguishable to it. +//! +//! **Consequence: the residue-vs-granularity sweep cannot produce a signal, and a +//! null from it would say nothing about 4⁴.** Reporting "4⁴ doesn't help the +//! calculator" from that null would have been a false negative caused entirely by +//! the intake. This is an INTAKE finding, exactly as pre-registered. +//! +//! ## H2 — stride 4 vs the φ stride 11 (pre-registered failure mode (b), REFUTED) +//! +//! The suspicion was: `gcd(4,17) = 1` buys *coverage*, not *low discrepancy*; +//! stride 4's first four steps are `0,4,8,12` (one column of the 4×4 raster, +//! clumped) while the φ stride over 17 is **11** (`17/φ = 10.51`, and the +//! workspace's C5 pins `11/17` as the proven golden step). Since HHTL terminates +//! early, only short prefixes are ever consumed — so short-prefix uniformity is +//! what matters, and stride 4 looked like a raster wearing a φ-spiral's docstring. +//! +//! **The measurement refutes it at exactly the prefix length a 4-ary tier +//! consumes.** After 4 steps, stride 4 gives gaps `[4,4,4,5]` — near-perfect +//! tiling of the 17-circle — because `4·4 = 16 ≈ 17`. The comma (`17 = 4² + 1`) +//! is what makes a 4-step prefix land almost uniformly. Stride 11 is better at +//! n=2..3 and worse at n=4..6. The shipped constants are *matched to the 4-ary +//! use case*, not sloppy about it. +//! +//! ## H3 — the intake blindness is FIXABLE, not fundamental +//! +//! A per-tier intake (one ruler seed per 2-bit group, folded with its level) +//! preserves ancestry by construction: two cells sharing a k-level address prefix +//! share their first k seeds. The flat intake destroys that. H3 measures both, so +//! the finding lands as a *design direction* rather than a dead end. +//! +//! Run: `cargo run --manifest-path crates/helix/Cargo.toml --example probe_hhtl_intake_blindness` + +use helix::curve_ruler::CurveRuler; + +const CELLS: usize = 256; + +// ── gate harness ──────────────────────────────────────────────────────────── + +static FAILURES: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); + +fn gate(name: &str, pass: bool, detail: String) { + println!( + " [{}] {name}\n {detail}", + if pass { "PASS" } else { "FAIL" } + ); + if !pass { + FAILURES.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + } +} + +/// Start-offset histogram over all 256 cells for a carving of the given depth. +/// `depth` is what `NiblePath` would report: 4 for the 4-ary (4×2-bit) address, +/// 2 for the 16-ary (2×4-bit) address. Same cells, same numbers, different +/// *claimed* structure. +fn start_histogram(depth: u8) -> [usize; 17] { + let mut h = [0usize; 17]; + for cell in 0..CELLS { + let r = CurveRuler::from_hhtl(cell as u64, depth); + h[r.start_offset() as usize] += 1; + } + h +} + +/// Gap structure of the first `n` points of `(stride·k) mod 17`, as +/// (max_gap − min_gap). Lower = more uniform. This is the three-distance view: +/// a low-discrepancy prefix leaves near-equal gaps around the circle. +fn prefix_gap_spread(stride: u8, n: usize) -> (u8, Vec) { + let m = CurveRuler::MODULUS; + let mut pts: Vec = (0..n) + .map(|k| ((stride as u32 * k as u32) % m as u32) as u8) + .collect(); + pts.sort_unstable(); + pts.dedup(); + let mut gaps: Vec = Vec::with_capacity(pts.len()); + for i in 0..pts.len() { + let a = pts[i]; + let b = pts[(i + 1) % pts.len()]; + let d = (b as i16 - a as i16).rem_euclid(m as i16) as u8; + gaps.push(if d == 0 { m } else { d }); + } + let spread = gaps.iter().max().copied().unwrap_or(0) - gaps.iter().min().copied().unwrap_or(0); + let mut sorted = gaps.clone(); + sorted.sort_unstable(); + (spread, sorted) +} + +/// Levels of shared prefix between two 4-ary addresses (0..=4). +fn shared_levels_4ary(a: u8, b: u8) -> usize { + for k in 0..4 { + let shift = 2 * (4 - k) - 2; + if (a >> shift) != (b >> shift) { + return k; + } + } + 4 +} + +/// FLAT intake (what ships): the whole address collapses to one seed. +fn flat_seed(cell: u8) -> u8 { + CurveRuler::from_hhtl(cell as u64, 4).start_offset() +} + +/// PER-TIER intake (the proposed fix): one seed per 2-bit group, folded with its +/// level so the same group value at different depths starts elsewhere — the same +/// `depth` fold `from_hhtl` already applies, but applied PER LEVEL instead of once. +fn tiered_seeds(cell: u8) -> [u8; 4] { + let mut out = [0u8; 4]; + for (level, slot) in out.iter_mut().enumerate() { + let shift = 2 * (4 - 1 - level); + let group = (cell >> shift) & 0b11; + *slot = CurveRuler::from_hhtl(group as u64, level as u8).start_offset(); + } + out +} + +fn shared_seed_prefix(a: [u8; 4], b: [u8; 4]) -> usize { + (0..4).take_while(|&i| a[i] == b[i]).count() +} + +fn main() { + println!("PROBE-HHTL-INTAKE-BLINDNESS (PROBE-CLAM-VS-HELIX-RESIDUE, leg 0)"); + println!( + "helix CurveRuler: MODULUS={} STRIDE={} (17 = 4²+1 — the comma)\n", + CurveRuler::MODULUS, + CurveRuler::STRIDE + ); + + // ── H1: the two carvings are indistinguishable to the ruler ───────────── + let h4 = start_histogram(4); // 4-ary, 4 levels × 2 bits + let h16 = start_histogram(2); // 16-ary, 2 levels × 4 bits + let mut s4: Vec = h4.to_vec(); + let mut s16: Vec = h16.to_vec(); + s4.sort_unstable(); + s16.sort_unstable(); + let identical_multiset = s4 == s16; + gate( + "H1 intake is STRUCTURE-BLIND (the sweep-killer)", + identical_multiset, + format!( + "start-offset histograms over the SAME 256 cells:\n \ + 4-ary(depth 4): {h4:?}\n 16-ary(depth 2): {h16:?}\n \ + identical multiset = {identical_multiset} (a pure rotation). \ + `from_place(p) = p % 17` consumes the address as a NUMBER, so 4⁴ \ + ancestry never reaches the ruler — a residue-vs-granularity sweep \ + would return a null for a reason unrelated to 4⁴." + ), + ); + + // ── H2: prefix uniformity, shipped stride 4 vs the φ stride 11 ────────── + let phi_stride = 11u8; // nearest integer to 17/φ = 10.5066 (workspace C5) + let mut rows = Vec::new(); + for n in 2..=6 { + let (d4, g4) = prefix_gap_spread(CurveRuler::STRIDE, n); + let (d11, g11) = prefix_gap_spread(phi_stride, n); + rows.push(format!( + "n={n}: stride4 spread={d4} gaps={g4:?} | stride11(φ) spread={d11} gaps={g11:?}" + )); + } + let (d4_at4, g4_at4) = prefix_gap_spread(CurveRuler::STRIDE, 4); + let (d11_at4, _) = prefix_gap_spread(phi_stride, 4); + // The 4-ary tier consumes exactly ARITY=4 steps. That is the prefix that must + // be uniform. Falsifier for the REFUTATION: stride 4 worse than φ at n=4. + gate( + "H2 shipped stride 4 is MATCHED to the 4-step prefix (my suspicion REFUTED)", + d4_at4 <= d11_at4, + format!( + "{}\n At n=4 — the prefix a 4-ary tier actually consumes — \ + stride 4 gives gaps {g4_at4:?} (spread {d4_at4}) vs φ-stride 11 spread \ + {d11_at4}. 4·4 = 16 ≈ 17, so four steps of 4 nearly tile the circle: \ + the comma is what makes the 4-step prefix land uniformly. \ + Pre-registered suspicion (\"stride 4 is a raster wearing a φ-spiral's \ + docstring\") does NOT hold at the lengths early termination reads.", + rows.join("\n ") + ), + ); + + // ── H3: a per-tier intake preserves ancestry; the flat one destroys it ── + let mut flat_agree = 0usize; + let mut tier_agree = 0usize; + let mut pairs = 0usize; + // NOTE: iterate in u16. `CELLS as u8` is `256 as u8` == 0, which silently + // made this loop empty on the first run — the gate reported 0 pairs and + // correctly FAILED rather than passing vacuously. + for a16 in 0u16..CELLS as u16 { + for b16 in (a16 + 1)..CELLS as u16 { + if !(a16 as usize + b16 as usize).is_multiple_of(7) { + continue; // deterministic thinning; still thousands of pairs + } + let (a, b) = (a16 as u8, b16 as u8); + pairs += 1; + let k = shared_levels_4ary(a, b); + // FLAT: one seed each. "Ancestry preserved" would mean cells sharing a + // prefix share the seed — which mod 17 cannot arrange. + if k > 0 && flat_seed(a) == flat_seed(b) { + flat_agree += 1; + } + // PER-TIER: cells sharing k levels must share their first k seeds. + if k > 0 && shared_seed_prefix(tiered_seeds(a), tiered_seeds(b)) >= k { + tier_agree += 1; + } + } + } + gate( + "H3 blindness is FIXABLE at the intake (per-tier seeds keep ancestry)", + tier_agree > flat_agree * 4 && pairs > 100, + format!( + "over {pairs} sampled cell pairs sharing ≥1 address level: \ + FLAT intake preserves ancestry in {flat_agree} (mod 17 scrambles it); \ + PER-TIER intake preserves it in {tier_agree} — by construction, since \ + each 2-bit group seeds its own ruler. The fix is to feed the ruler the \ + address's HIERARCHY, not its integer value." + ), + ); + + let failures = FAILURES.load(std::sync::atomic::Ordering::Relaxed); + println!( + "\n{}", + if failures == 0 { + "ALL GATES GREEN".to_string() + } else { + format!("{failures} GATE(S) FAILED") + } + ); + println!( + "\nVERDICT: the residue-vs-granularity sweep is NOT RUN — H1 shows it cannot\n\ + carry signal through the current intake. That is a finding about the\n\ + CALCULATOR'S INTAKE, not about the 4⁴ address, whose ancestry result stands\n\ + (E-WORDNET-MAKES-THE-4-ARY-ADDRESS-SEMANTIC-1). H2 additionally clears the\n\ + shipped constants of the discrepancy suspicion. H3 names the unblock." + ); + if failures > 0 { + std::process::exit(1); + } +} From a096114c6f26bccc3228c36dfc0b9887cbc35460 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Jul 2026 07:02:23 +0000 Subject: [PATCH 2/4] =?UTF-8?q?probe(helix):=20fix=20all=20three=20codex?= =?UTF-8?q?=20findings=20=E2=80=94=20H1=20gate,=20H2=20overclaim,=20H3=20s?= =?UTF-8?q?ample=20size?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All three confirmed. None was fixed by adjusting a threshold. P1 #1 — H1 SORTED THE HISTOGRAMS. Comparing sorted multisets discards which cell maps to which offset, and the UNSORTED histograms genuinely differ (peak at index 4 vs 2), so the gate passed while hiding that every cell had moved. An aggregate distribution cannot establish per-cell invariance. Replaced with the argument that actually holds, in two halves: (i) type-level, stated not measured — from_hhtl(path, depth) has no parameter that could carry a carving; (ii) empirical — the entire difference between the carvings is a CONSTANT rotation of 2 across all 256 cells with variance 0, and a cell-dependent shift is the falsifier. P1 #2 — H2 DREW A DESIGN CONCLUSION FROM AN UNWIRED PREMISE. I claimed the shipped constants are "MATCHED to the 4-ary use case". Verified against the source: ResidueEncoder::encode reads only start_offset (n=1, residue.rs:157); index/arc appear solely in walk_spectrum and crate tests; the shipped NiblePath is FAN_OUT=16. Nothing that ships consumes a 4-step prefix, so the n=4 comparison is a property of a hypothetical consumer. Claim withdrawn. What survives is narrower and still worth recording: the pre-registered suspicion is NOT CONFIRMED — a retraction of a concern, not a vindication of a design. P2 #3 — H3 PUBLISHED A FALSE SAMPLE SIZE. The counter incremented before the k > 0 eligibility check, so "4,662 pairs sharing >=1 address level" was really every thinned pair. Codex's inference was exactly right: since tiered agreement holds for every eligible pair by construction, 1,152 IS the eligible population. Corrected figures: flat 51/1,152 = 4.4% (~ the 1/17 = 5.9% chance level, i.e. the flat intake keeps ancestry about as often as chance would); per-tier 1,152/1,152 = 100% BY CONSTRUCTION, now asserted as a construction check rather than as evidence. The ~23x ratio was arithmetic on a mislabelled denominator and is retracted. Both board records corrected in the same commit with dated inline markers, per the append-only rule. Gates 3/3 green; clippy -D warnings clean; fmt applied. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01K3RyLEbuNSHxxB3NTTrGki --- .claude/board/EPIPHANIES.md | 6 +- .../board/exec-runs/hhtl-intake-blindness.md | 49 +++-- .../examples/probe_hhtl_intake_blindness.rs | 170 ++++++++++++------ 3 files changed, 159 insertions(+), 66 deletions(-) diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index b01ac85d6..a55cc36c7 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -6,11 +6,11 @@ **The queued sweep did NOT run, and not running it is the result.** `PROBE-CLAM-VS-HELIX-RESIDUE` was to test whether a finer address shifts work from stored RESIDUE to deterministic PLACE. Two failure modes were pre-registered before any measurement. Running them first was decisive. -**H1 — the intake is STRUCTURE-BLIND (confirmed; fatal to the sweep).** `CurveRuler::from_place(p) = p % 17` consumes the address as a **number**, never as a hierarchy. The same 256 cells carved 4-ary (depth 4) or 16-ary (depth 2) produce the **same start-offset multiset** (a pure rotation: one bucket 16, the rest 15). The ruler cannot distinguish the carvings, so a residue-vs-granularity sweep would return a null *for a reason unrelated to 4⁴*. **Reporting "4⁴ doesn't help the calculator" from that null would have been a false negative manufactured by the intake.** The 4⁴ ADDRESS result (`E-WORDNET-MAKES-THE-4-ARY-ADDRESS-SEMANTIC-1`, 24.71× out-of-cell band lift, 2.47-hop sub-nibble gap) is untouched — this is a finding about the CALCULATOR'S INTAKE. +**H1 — the carving cannot reach the ruler (confirmed; fatal to the sweep).** `from_hhtl(path: u64, depth: u8)` has no parameter that could carry a carving, so level structure has no channel into `from_place(p) = p % 17`. Measured: the whole difference between the two carvings is a **constant rotation of 2 across all 256 cells, variance 0**. ⊘ The first gate compared SORTED histograms — sorting discards which cell maps to which offset, and the unsorted histograms genuinely differ, so it passed while hiding that every cell had moved (codex P1 on #876). The ruler cannot distinguish the carvings, so a residue-vs-granularity sweep would return a null *for a reason unrelated to 4⁴*. **Reporting "4⁴ doesn't help the calculator" from that null would have been a false negative manufactured by the intake.** The 4⁴ ADDRESS result (`E-WORDNET-MAKES-THE-4-ARY-ADDRESS-SEMANTIC-1`, 24.71× out-of-cell band lift, 2.47-hop sub-nibble gap) is untouched — this is a finding about the CALCULATOR'S INTAKE. -**H2 — my own "stride 4 isn't golden" suspicion is REFUTED, and the refutation vindicates the design.** The worry: `gcd(4,17)=1` buys coverage, not low discrepancy — stride 4's first four steps `0,4,8,12` are one column of the 4×4 raster, while the φ stride over 17 is 11 (`17/φ = 10.51`; C5 pins 11/17). Since HHTL terminates early, short prefixes are what get consumed. Measured gap spread (lower = more uniform): n=2 → 9 vs **5**(φ); n=3 → 5 vs **1**(φ); **n=4 → 1 vs 5** (stride 4 wins decisively); n=5 → **3** vs 4; n=6 → **3** vs 4. **At n=4 — exactly the prefix a 4-ary tier consumes — stride 4 gives gaps `[4,4,4,5]`, near-perfect tiling, because `4·4 = 16 ≈ 17`.** The comma IS what makes the 4-step prefix uniform. The operator's Pythagorean-comma framing is confirmed in mechanism: stride 4 over **16** covers only 4/16 and retraces one column forever; `17 = 4²+1` is what turns the raster into a sweep. **The shipped `STRIDE = 4` / `MODULUS = 17` are matched to the 4-ary use case, not sloppy about φ.** +**H2 — my own "stride 4 isn't golden" suspicion is NOT CONFIRMED; the design claim I drew from that is WITHDRAWN (codex P1 on #876).** The worry: `gcd(4,17)=1` buys coverage, not low discrepancy — stride 4's first four steps `0,4,8,12` are one column of the 4×4 raster, while the φ stride over 17 is 11 (`17/φ = 10.51`; C5 pins 11/17). Since HHTL terminates early, short prefixes are what get consumed. Measured gap spread (lower = more uniform): n=2 → 9 vs **5**(φ); n=3 → 5 vs **1**(φ); **n=4 → 1 vs 5** (stride 4 wins decisively); n=5 → **3** vs 4; n=6 → **3** vs 4. **At n=4 — exactly the prefix a 4-ary tier consumes — stride 4 gives gaps `[4,4,4,5]`, near-perfect tiling, because `4·4 = 16 ≈ 17`.** The comma IS what makes the 4-step prefix uniform. The operator's Pythagorean-comma framing is confirmed in mechanism: stride 4 over **16** covers only 4/16 and retraces one column forever; `17 = 4²+1` is what turns the raster into a sweep. **⊘ But "matched to the 4-ary use case" is withdrawn:** the premise that a 4-ary tier consumes 4 ruler steps is not wired into anything that ships — `ResidueEncoder::encode` reads only `start_offset` (n=1, `residue.rs:157`), `index`/`arc` appear solely in `walk_spectrum` and crate tests, and the shipped `NiblePath` is `FAN_OUT = 16`. The n=4 result is a property of a HYPOTHETICAL consumer. This RETRACTS a suspicion; it does not vindicate a design. -**H3 — the blindness is fixable, so this is a direction, not a dead end.** Per-tier seeding (one ruler per 2-bit group, folded with its level) preserves ancestry by construction: over 4,662 sampled pairs sharing ≥1 address level, the flat intake preserves ancestry in **51**, per-tier in **1,152** (≈23×). The unblock is to feed the ruler the address's HIERARCHY, not its integer value — a helix API change that needs its own review. +**H3 — the blindness is fixable, so this is a direction, not a dead end.** Per-tier seeding (one ruler per 2-bit group, folded with its level) preserves ancestry by construction: eligible population **1,152** pairs sharing ≥1 address level; flat intake preserves ancestry in **51/1,152 = 4.4 %** (≈ the 1/17 ≈ 5.9 % chance level), per-tier in **1,152/1,152 = 100 % by construction** (asserted as a construction check, not as evidence). ⊘ The originally published "4,662 pairs" and "≈23×" were wrong — the counter incremented before the eligibility check, so the denominator was every thinned pair (codex P2 on #876). The unblock is to feed the ruler the address's HIERARCHY, not its integer value — a helix API change that needs its own review. **The transferable rule:** *a calculator's INTAKE can silently discard the very structure a probe is trying to measure.* Check what the consumer actually reads before running a sweep over what the producer emits — otherwise a null measures the seam, not the hypothesis. This is the same shape as the falsifier-defect pattern (four instances now: the non-falsifying shuffle, the hair-wide twin window, the inert cover guard, and — self-caught here — an H3 loop that sampled 0 pairs because `256 as u8 == 0` and correctly FAILED rather than passing on zero evidence). diff --git a/.claude/board/exec-runs/hhtl-intake-blindness.md b/.claude/board/exec-runs/hhtl-intake-blindness.md index 6291284b8..c300e1a00 100644 --- a/.claude/board/exec-runs/hhtl-intake-blindness.md +++ b/.claude/board/exec-runs/hhtl-intake-blindness.md @@ -22,18 +22,26 @@ The queued hypothesis (finer address ⇒ more deterministic place ⇒ less store residue) was gated behind two PRE-REGISTERED failure modes. Both were run first, exactly as the brief required. One is fatal to the sweep; the other is REFUTED. -### H1 — the intake is STRUCTURE-BLIND (failure mode (a): CONFIRMED, fatal) +### H1 — the carving cannot reach the ruler (failure mode (a): CONFIRMED, fatal) -`from_place(p) = p % 17` consumes the address as a **NUMBER**, never as a -hierarchy. Feeding the same 256 cells carved 4-ary (depth 4) vs 16-ary (depth 2) -yields the SAME start-offset multiset — a pure rotation: +Two halves. **Type-level (stated, not measured):** `from_hhtl(path: u64, +depth: u8)` has no parameter that could carry a carving, so level structure has +no channel into `from_place(p) = p % 17`. **Empirical (the gate):** the entire +difference between the two carvings, as the ruler sees it, is a **constant +rotation of 2 across all 256 cells, variance 0** — a cell-dependent shift is the +falsifier. Unsorted histograms: ``` 4-ary (depth 4): [15,15,15,15,16,15,15,15,15,15,15,15,15,15,15,15,15] 16-ary (depth 2): [15,15,16,15,15,15,15,15,15,15,15,15,15,15,15,15,15] -identical multiset = true ``` +> **⊘ Gate corrected pre-merge (codex P1 on #876).** The first version sorted +> both histograms and compared multisets. Sorting discards which cell maps to +> which offset, and the UNSORTED histograms genuinely differ — so the gate +> passed while hiding that every cell had moved. An aggregate distribution does +> not establish per-cell invariance; the constant-shift measurement does. + **Consequence: a residue-vs-granularity sweep cannot carry signal.** Its null would have been a false negative caused entirely by the intake, and reporting "4⁴ doesn't help the calculator" from it would have been wrong. Reported as an @@ -56,11 +64,17 @@ Measured gap spread (max−min gap; lower = more uniform): | 5 | **3** | [1,4,4,4,4] | 4 | [1,1,5,5,5] | | 6 | **3** | [1,1,3,4,4,4] | 4 | [1,1,1,4,5,5] | -**At n=4 — the prefix a 4-ary tier actually consumes — stride 4 is near-perfect -(spread 1) and beats φ (spread 5).** `4·4 = 16 ≈ 17`: the comma is what makes a -4-step prefix tile the circle. The shipped constants are MATCHED to the 4-ary -use case. My suspicion ("a raster wearing a φ-spiral's docstring") is refuted; -this vindicates `STRIDE = 4` / `MODULUS = 17` rather than indicting them. +At n=4 stride 4 is near-perfect (spread 1) and beats φ (spread 5); `4·4 = 16 ≈ 17`. + +> **⊘ DESIGN CLAIM WITHDRAWN (codex P1 on #876).** The first version concluded +> "the shipped constants are MATCHED to the 4-ary use case". That premise — that +> a 4-ary tier consumes 4 ruler steps — is **not wired into anything that +> ships**: `ResidueEncoder::encode` reads only `start_offset` (n=1, +> `residue.rs:157`); `index`/`arc` appear solely in `walk_spectrum` and this +> crate's tests; the shipped `NiblePath` is `FAN_OUT = 16`. The n=4 comparison is +> therefore a property of a HYPOTHETICAL consumer. What survives: the +> pre-registered suspicion is **NOT CONFIRMED** — a retraction of a concern, not +> a vindication of a design. Operator framing that produced this check: *"4x4 usually only helps with an irrational stride as Pythagorean comma."* Confirmed in mechanism — stride 4 over @@ -69,9 +83,18 @@ irrational stride as Pythagorean comma."* Confirmed in mechanism — stride 4 ov ### H3 — the blindness is FIXABLE at the intake, not fundamental Per-tier seeding (one ruler per 2-bit group, folded with its level) preserves -ancestry by construction. Over 4,662 sampled cell pairs sharing ≥1 address level: -**flat intake preserves ancestry in 51; per-tier in 1,152 (≈23×).** The unblock -is to feed the ruler the address's HIERARCHY, not its integer value. +ancestry by construction. Eligible population = **1,152** pairs sharing ≥1 +address level: **flat intake preserves ancestry in 51/1,152 = 4.4 %** (≈ the +1/17 ≈ 5.9 % chance level — the flat intake keeps ancestry about as often as +chance would); **per-tier in 1,152/1,152 = 100 % BY CONSTRUCTION**, asserted as a +construction check rather than as evidence. The unblock is to feed the ruler the +address's HIERARCHY, not its integer value. + +> **⊘ SAMPLE SIZE CORRECTED (codex P2 on #876).** The first version incremented +> the counter BEFORE the `k > 0` eligibility check, so the published "4,662 pairs +> sharing ≥1 address level" was really every thinned pair — a false sample size +> in the probe output and in both board records. The ≈23× ratio was arithmetic +> on a mislabelled denominator; the honest figures are 4.4 % vs 100 %. ## Verification - `cargo run --release --manifest-path crates/helix/Cargo.toml --example diff --git a/crates/helix/examples/probe_hhtl_intake_blindness.rs b/crates/helix/examples/probe_hhtl_intake_blindness.rs index 572f5cc9b..1067dd53f 100644 --- a/crates/helix/examples/probe_hhtl_intake_blindness.rs +++ b/crates/helix/examples/probe_hhtl_intake_blindness.rs @@ -15,23 +15,30 @@ //! deterministic PLACE and less of it stored RESIDUE, so residue should shrink //! as address granularity rises. Two failure modes were **pre-registered** before //! any measurement. This probe runs both. One is confirmed and it is fatal to the -//! sweep; the other is REFUTED, and its refutation vindicates the shipped design. +//! sweep; the other is NOT CONFIRMED — which retracts a suspicion without +//! licensing any design claim in its place (see the ⚠ under H2). //! //! ## H1 — the intake is STRUCTURE-BLIND (pre-registered failure mode (a)) //! //! [`CurveRuler::from_hhtl(path, depth)`] is `from_place(path + depth)`, and //! [`CurveRuler::from_place`] is `place % 17`. The address therefore enters the -//! ruler **as a number, never as a hierarchy**. Feed the same 256 cells carved -//! 4-ary (4 levels × 2 bits) or 16-ary (2 levels × 4 bits) and the ruler receives -//! the same 256 integers, so it produces the same start-offset multiset — the -//! carvings are indistinguishable to it. +//! ruler **as a number, never as a hierarchy** — `from_hhtl` has no parameter +//! that could carry a carving, so how the byte's bits group into levels cannot +//! influence the output. The measurable half: the entire difference between the +//! two carvings, as the ruler sees it, is a CONSTANT rotation with ZERO per-cell +//! variance; a cell-dependent shift would falsify it. +//! +//! (An earlier version of this gate compared SORTED histograms. That was wrong: +//! sorting discards which cell maps to which offset, and the unsorted histograms +//! genuinely differ, so the gate passed while hiding that every cell had moved — +//! codex P1 on #876.) //! //! **Consequence: the residue-vs-granularity sweep cannot produce a signal, and a //! null from it would say nothing about 4⁴.** Reporting "4⁴ doesn't help the //! calculator" from that null would have been a false negative caused entirely by //! the intake. This is an INTAKE finding, exactly as pre-registered. //! -//! ## H2 — stride 4 vs the φ stride 11 (pre-registered failure mode (b), REFUTED) +//! ## H2 — stride 4 vs the φ stride 11 (pre-registered (b): NOT CONFIRMED) //! //! The suspicion was: `gcd(4,17) = 1` buys *coverage*, not *low discrepancy*; //! stride 4's first four steps are `0,4,8,12` (one column of the 4×4 raster, @@ -40,19 +47,26 @@ //! early, only short prefixes are ever consumed — so short-prefix uniformity is //! what matters, and stride 4 looked like a raster wearing a φ-spiral's docstring. //! -//! **The measurement refutes it at exactly the prefix length a 4-ary tier -//! consumes.** After 4 steps, stride 4 gives gaps `[4,4,4,5]` — near-perfect -//! tiling of the 17-circle — because `4·4 = 16 ≈ 17`. The comma (`17 = 4² + 1`) -//! is what makes a 4-step prefix land almost uniformly. Stride 11 is better at -//! n=2..3 and worse at n=4..6. The shipped constants are *matched to the 4-ary -//! use case*, not sloppy about it. +//! The measurement does NOT confirm it: after 4 steps stride 4 gives gaps +//! `[4,4,4,5]` — near-perfect tiling of the 17-circle — because `4·4 = 16 ≈ 17`. +//! Stride 11 is better at n=2..3 and worse at n=4..6. +//! +//! **⚠ The design conclusion is WITHDRAWN (codex P1 on #876).** The premise that +//! a 4-ary tier consumes four ruler steps is not wired into anything that ships: +//! `ResidueEncoder::encode` reads only `start_offset` (n=1, `residue.rs:157`), +//! `index`/`arc` appear solely in `walk_spectrum` and this crate's tests, and the +//! shipped router `NiblePath` is `FAN_OUT = 16`. So this RETRACTS a suspicion; it +//! does not establish that the constants are "matched to 4-ary" — that would need +//! a consumer that reads 4 steps, and none currently exists. //! //! ## H3 — the intake blindness is FIXABLE, not fundamental //! //! A per-tier intake (one ruler seed per 2-bit group, folded with its level) //! preserves ancestry by construction: two cells sharing a k-level address prefix -//! share their first k seeds. The flat intake destroys that. H3 measures both, so -//! the finding lands as a *design direction* rather than a dead end. +//! share their first k seeds — so per-tier agreement equals the eligible +//! population exactly, and the gate asserts that equality as a CONSTRUCTION +//! CHECK, not as evidence. The informative number is the FLAT rate. The finding +//! lands as a *design direction* rather than a dead end. //! //! Run: `cargo run --manifest-path crates/helix/Cargo.toml --example probe_hhtl_intake_blindness` @@ -151,24 +165,49 @@ fn main() { CurveRuler::STRIDE ); - // ── H1: the two carvings are indistinguishable to the ruler ───────────── - let h4 = start_histogram(4); // 4-ary, 4 levels × 2 bits - let h16 = start_histogram(2); // 16-ary, 2 levels × 4 bits - let mut s4: Vec = h4.to_vec(); - let mut s16: Vec = h16.to_vec(); - s4.sort_unstable(); - s16.sort_unstable(); - let identical_multiset = s4 == s16; + // ── H1: the carving cannot reach the ruler ────────────────────────────── + // + // The FIRST version of this gate sorted both histograms and compared + // multisets. That was wrong twice over (codex P1): sorting discards which + // cell maps to which offset, and the UNSORTED histograms genuinely differ + // (the peak sits at index 4 vs index 2), so a "shared aggregate + // distribution" gate passed while hiding that every cell's start moved. + // + // The real argument is not aggregate at all. It has two halves: + // + // (i) TYPE-LEVEL: `from_hhtl(path: u64, depth: u8)` has no parameter + // that could carry a carving. How the byte's bits group into levels + // is not an input, so it cannot influence the output. No measurement + // can establish this and none should pretend to — it is stated. + // (ii) EMPIRICAL, and this is what the gate measures: the entire + // difference between the two carvings, as the ruler sees it, is a + // CONSTANT rotation with ZERO per-cell variance. If the ruler + // carried any per-cell structure, the shift would vary by cell. + // A varying shift is the falsifier. + let h4 = start_histogram(4); // "4-ary" — 4 levels × 2 bits + let h16 = start_histogram(2); // "16-ary" — 2 levels × 4 bits + let shifts: Vec = (0..CELLS) + .map(|cell| { + let a = CurveRuler::from_hhtl(cell as u64, 4).start_offset() as i16; + let b = CurveRuler::from_hhtl(cell as u64, 2).start_offset() as i16; + (a - b).rem_euclid(CurveRuler::MODULUS as i16) as u8 + }) + .collect(); + let first = shifts[0]; + let constant_shift = shifts.iter().all(|&s| s == first); gate( - "H1 intake is STRUCTURE-BLIND (the sweep-killer)", - identical_multiset, + "H1 the carving cannot reach the ruler (constant-shift, zero variance)", + constant_shift, format!( - "start-offset histograms over the SAME 256 cells:\n \ - 4-ary(depth 4): {h4:?}\n 16-ary(depth 2): {h16:?}\n \ - identical multiset = {identical_multiset} (a pure rotation). \ - `from_place(p) = p % 17` consumes the address as a NUMBER, so 4⁴ \ - ancestry never reaches the ruler — a residue-vs-granularity sweep \ - would return a null for a reason unrelated to 4⁴." + "per-cell start-offset shift between the two carvings is CONSTANT at \ + {first} for all {CELLS} cells (variance 0) — the depth term moves every \ + cell identically and the carving contributes nothing. Falsifier: any \ + cell-dependent shift.\n Unsorted histograms (shown because \ + sorting them would hide exactly this): 4-ary(depth 4) {h4:?}; \ + 16-ary(depth 2) {h16:?} — a rotation, not a reshaping.\n \ + Type-level half (stated, not measured): `from_hhtl(path: u64, depth: u8)` \ + takes no carving parameter, so 4⁴ level structure has no channel into \ + `from_place(p) = p % 17`." ), ); @@ -184,18 +223,31 @@ fn main() { } let (d4_at4, g4_at4) = prefix_gap_spread(CurveRuler::STRIDE, 4); let (d11_at4, _) = prefix_gap_spread(phi_stride, 4); - // The 4-ary tier consumes exactly ARITY=4 steps. That is the prefix that must - // be uniform. Falsifier for the REFUTATION: stride 4 worse than φ at n=4. + // ⚠ CLAIM DOWNGRADED (codex P1). The first version asserted the shipped + // constants are "MATCHED to the 4-ary use case". That premise — that a + // 4-ary tier consumes 4 ruler steps — is NOT WIRED anywhere that ships: + // • `ResidueEncoder::encode` reads ONLY `start_offset` (residue.rs:157), + // i.e. n = 1; + // • `CurveRuler::index` / `arc` appear only in `walk_spectrum` and this + // crate's own tests; + // • the shipped router `NiblePath` is FAN_OUT = 16, not 4. + // So the n=4 comparison is a property of a HYPOTHETICAL 4-step consumer. + // What survives is narrower and still worth recording: the pre-registered + // suspicion (stride 4 clumps where φ would not) is NOT CONFIRMED. That is a + // retraction of a concern, not a vindication of a design. gate( - "H2 shipped stride 4 is MATCHED to the 4-step prefix (my suspicion REFUTED)", + "H2 the \"stride 4 clumps\" suspicion is NOT CONFIRMED (design claim withdrawn)", d4_at4 <= d11_at4, format!( - "{}\n At n=4 — the prefix a 4-ary tier actually consumes — \ - stride 4 gives gaps {g4_at4:?} (spread {d4_at4}) vs φ-stride 11 spread \ - {d11_at4}. 4·4 = 16 ≈ 17, so four steps of 4 nearly tile the circle: \ - the comma is what makes the 4-step prefix land uniformly. \ - Pre-registered suspicion (\"stride 4 is a raster wearing a φ-spiral's \ - docstring\") does NOT hold at the lengths early termination reads.", + "{}\n At n=4, stride 4 gives gaps {g4_at4:?} (spread {d4_at4}) vs \ + φ-stride 11 spread {d11_at4} — 4·4 = 16 ≈ 17, so four steps of 4 nearly \ + tile the circle.\n ⚠ SCOPE: no shipped path consumes a 4-step \ + prefix. `ResidueEncoder::encode` reads only `start_offset` (n=1); \ + `index`/`arc` are used solely by `walk_spectrum` and crate tests; the \ + shipped `NiblePath` is FAN_OUT=16. This therefore RETRACTS the \ + pre-registered suspicion and does NOT establish that the constants are \ + matched to 4-ary — that would need a consumer that reads 4 steps, which \ + does not currently exist.", rows.join("\n ") ), ); @@ -213,28 +265,44 @@ fn main() { continue; // deterministic thinning; still thousands of pairs } let (a, b) = (a16 as u8, b16 as u8); - pairs += 1; let k = shared_levels_4ary(a, b); + if k == 0 { + continue; // not eligible: no shared address level to preserve + } + // COUNT ONLY ELIGIBLE PAIRS (codex P2). The first version incremented + // before this predicate, so the published "4,662 pairs sharing ≥1 + // address level" was really every thinned pair — a false sample size + // in the probe output AND in both board records. + pairs += 1; // FLAT: one seed each. "Ancestry preserved" would mean cells sharing a // prefix share the seed — which mod 17 cannot arrange. - if k > 0 && flat_seed(a) == flat_seed(b) { + if flat_seed(a) == flat_seed(b) { flat_agree += 1; } // PER-TIER: cells sharing k levels must share their first k seeds. - if k > 0 && shared_seed_prefix(tiered_seeds(a), tiered_seeds(b)) >= k { + if shared_seed_prefix(tiered_seeds(a), tiered_seeds(b)) >= k { tier_agree += 1; } } } + // Per-tier agreement is TRUE BY CONSTRUCTION for every eligible pair, so it + // should equal the eligible population exactly; asserting that is the + // construction check, and the informative number is the FLAT rate. + let flat_rate = flat_agree as f64 / pairs as f64; gate( "H3 blindness is FIXABLE at the intake (per-tier seeds keep ancestry)", - tier_agree > flat_agree * 4 && pairs > 100, + tier_agree == pairs && flat_rate < 0.25 && pairs > 100, format!( - "over {pairs} sampled cell pairs sharing ≥1 address level: \ - FLAT intake preserves ancestry in {flat_agree} (mod 17 scrambles it); \ - PER-TIER intake preserves it in {tier_agree} — by construction, since \ - each 2-bit group seeds its own ruler. The fix is to feed the ruler the \ - address's HIERARCHY, not its integer value." + "eligible population = {pairs} pairs sharing ≥1 address level (thinned \ + deterministically).\n FLAT intake preserves ancestry in \ + {flat_agree}/{pairs} = {:.1}% — mod 17 scrambles it.\n PER-TIER \ + intake preserves it in {tier_agree}/{pairs} = 100% BY CONSTRUCTION \ + (each 2-bit group seeds its own ruler); the gate asserts the equality \ + as a construction check, not as evidence.\n The informative \ + quantity is the flat rate, and the honest reading is \"the flat intake \ + keeps ancestry about as often as chance would\" — NOT a tuned ratio. \ + The fix is to feed the ruler the address's HIERARCHY, not its integer.", + 100.0 * flat_rate ), ); @@ -251,8 +319,10 @@ fn main() { "\nVERDICT: the residue-vs-granularity sweep is NOT RUN — H1 shows it cannot\n\ carry signal through the current intake. That is a finding about the\n\ CALCULATOR'S INTAKE, not about the 4⁴ address, whose ancestry result stands\n\ - (E-WORDNET-MAKES-THE-4-ARY-ADDRESS-SEMANTIC-1). H2 additionally clears the\n\ - shipped constants of the discrepancy suspicion. H3 names the unblock." + (E-WORDNET-MAKES-THE-4-ARY-ADDRESS-SEMANTIC-1). H2 RETRACTS a suspicion it\n\ + does not replace with a design claim — no shipped path reads a 4-step prefix.\n\ + H3 names the unblock and reports the flat rate (4.4%, ≈ the 1/17 ≈ 5.9%\n\ + chance level), not a ratio." ); if failures > 0 { std::process::exit(1); From 33c9d63629c54b9199cfb56b7c1fe56acf93f890 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Jul 2026 07:09:17 +0000 Subject: [PATCH 3/4] =?UTF-8?q?probe(helix):=20remove=20the=20thinning=20?= =?UTF-8?q?=E2=80=94=20H3's=20denominator=20was=20wrong=20twice,=20for=20o?= =?UTF-8?q?ne=20root=20cause?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit on #876: the corrected 1,152 is the eligible THINNED SAMPLE, not the eligible POPULATION (8,064). That is the same population-label error one level up from the codex P2 fix it replaced — a second mislabel stacked on the fix for the first. Rather than relabel again, the cause is removed. The `(a+b) % 7` thinning bought nothing: 256*255/2 = 32,640 pairs is trivial to enumerate exhaustively. With no sample there is no sample to mislabel. Full-population figures, independently reproduced by the reviewer: eligible population 8,064 pairs sharing >=1 address level flat intake 360/8,064 = 4.5% (~ the 1/17 = 5.9% chance level) per-tier intake 8,064/8,064 = 100% BY CONSTRUCTION The conclusion is unchanged — the flat intake keeps ancestry about as often as chance would — but it now rests on the whole population rather than on a thinned sample carrying a wrong label. Probe output, EPIPHANIES and the exec-run updated together so no artifact keeps a stale denominator; the PR description follows. Both board records carry a dated marker recording that the denominator was wrong twice and naming the single root cause. Gates 3/3 green; clippy -D warnings clean; fmt applied. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01K3RyLEbuNSHxxB3NTTrGki --- .claude/board/EPIPHANIES.md | 2 +- .../board/exec-runs/hhtl-intake-blindness.md | 21 ++++++++------ .../examples/probe_hhtl_intake_blindness.rs | 28 +++++++++++-------- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index a55cc36c7..ba388e182 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -10,7 +10,7 @@ **H2 — my own "stride 4 isn't golden" suspicion is NOT CONFIRMED; the design claim I drew from that is WITHDRAWN (codex P1 on #876).** The worry: `gcd(4,17)=1` buys coverage, not low discrepancy — stride 4's first four steps `0,4,8,12` are one column of the 4×4 raster, while the φ stride over 17 is 11 (`17/φ = 10.51`; C5 pins 11/17). Since HHTL terminates early, short prefixes are what get consumed. Measured gap spread (lower = more uniform): n=2 → 9 vs **5**(φ); n=3 → 5 vs **1**(φ); **n=4 → 1 vs 5** (stride 4 wins decisively); n=5 → **3** vs 4; n=6 → **3** vs 4. **At n=4 — exactly the prefix a 4-ary tier consumes — stride 4 gives gaps `[4,4,4,5]`, near-perfect tiling, because `4·4 = 16 ≈ 17`.** The comma IS what makes the 4-step prefix uniform. The operator's Pythagorean-comma framing is confirmed in mechanism: stride 4 over **16** covers only 4/16 and retraces one column forever; `17 = 4²+1` is what turns the raster into a sweep. **⊘ But "matched to the 4-ary use case" is withdrawn:** the premise that a 4-ary tier consumes 4 ruler steps is not wired into anything that ships — `ResidueEncoder::encode` reads only `start_offset` (n=1, `residue.rs:157`), `index`/`arc` appear solely in `walk_spectrum` and crate tests, and the shipped `NiblePath` is `FAN_OUT = 16`. The n=4 result is a property of a HYPOTHETICAL consumer. This RETRACTS a suspicion; it does not vindicate a design. -**H3 — the blindness is fixable, so this is a direction, not a dead end.** Per-tier seeding (one ruler per 2-bit group, folded with its level) preserves ancestry by construction: eligible population **1,152** pairs sharing ≥1 address level; flat intake preserves ancestry in **51/1,152 = 4.4 %** (≈ the 1/17 ≈ 5.9 % chance level), per-tier in **1,152/1,152 = 100 % by construction** (asserted as a construction check, not as evidence). ⊘ The originally published "4,662 pairs" and "≈23×" were wrong — the counter incremented before the eligibility check, so the denominator was every thinned pair (codex P2 on #876). The unblock is to feed the ruler the address's HIERARCHY, not its integer value — a helix API change that needs its own review. +**H3 — the blindness is fixable, so this is a direction, not a dead end.** Per-tier seeding (one ruler per 2-bit group, folded with its level) preserves ancestry by construction: EXHAUSTIVE over all 256·255/2 = 32,640 cell pairs; eligible population **8,064** sharing ≥1 address level; flat intake preserves ancestry in **360/8,064 = 4.5 %** (≈ the 1/17 ≈ 5.9 % chance level), per-tier in **8,064/8,064 = 100 % by construction** (asserted as a construction check, not as evidence). ⊘ **The denominator was wrong TWICE:** first "4,662" (counter incremented before the eligibility check — codex P2), then "1,152" (the eligible *thinned sample* mislabelled as the *population* — CodeRabbit). Root cause of both: a `(a+b) % 7` thinning that bought nothing. Removing it eliminates the class of error, and the reviewer independently reproduced 360/8,064. The unblock is to feed the ruler the address's HIERARCHY, not its integer value — a helix API change that needs its own review. **The transferable rule:** *a calculator's INTAKE can silently discard the very structure a probe is trying to measure.* Check what the consumer actually reads before running a sweep over what the producer emits — otherwise a null measures the seam, not the hypothesis. This is the same shape as the falsifier-defect pattern (four instances now: the non-falsifying shuffle, the hair-wide twin window, the inert cover guard, and — self-caught here — an H3 loop that sampled 0 pairs because `256 as u8 == 0` and correctly FAILED rather than passing on zero evidence). diff --git a/.claude/board/exec-runs/hhtl-intake-blindness.md b/.claude/board/exec-runs/hhtl-intake-blindness.md index c300e1a00..87613faac 100644 --- a/.claude/board/exec-runs/hhtl-intake-blindness.md +++ b/.claude/board/exec-runs/hhtl-intake-blindness.md @@ -83,18 +83,23 @@ irrational stride as Pythagorean comma."* Confirmed in mechanism — stride 4 ov ### H3 — the blindness is FIXABLE at the intake, not fundamental Per-tier seeding (one ruler per 2-bit group, folded with its level) preserves -ancestry by construction. Eligible population = **1,152** pairs sharing ≥1 -address level: **flat intake preserves ancestry in 51/1,152 = 4.4 %** (≈ the +ancestry by construction. **EXHAUSTIVE over all 256·255/2 = 32,640 cell pairs — +no sampling, no thinning.** Eligible population = **8,064** pairs sharing ≥1 +address level: **flat intake preserves ancestry in 360/8,064 = 4.5 %** (≈ the 1/17 ≈ 5.9 % chance level — the flat intake keeps ancestry about as often as -chance would); **per-tier in 1,152/1,152 = 100 % BY CONSTRUCTION**, asserted as a +chance would); **per-tier in 8,064/8,064 = 100 % BY CONSTRUCTION**, asserted as a construction check rather than as evidence. The unblock is to feed the ruler the address's HIERARCHY, not its integer value. -> **⊘ SAMPLE SIZE CORRECTED (codex P2 on #876).** The first version incremented -> the counter BEFORE the `k > 0` eligibility check, so the published "4,662 pairs -> sharing ≥1 address level" was really every thinned pair — a false sample size -> in the probe output and in both board records. The ≈23× ratio was arithmetic -> on a mislabelled denominator; the honest figures are 4.4 % vs 100 %. +> **⊘ SAMPLE SIZE CORRECTED TWICE (codex P2, then CodeRabbit, both on #876).** +> (1) The counter incremented BEFORE the `k > 0` eligibility check, so the +> published "4,662 pairs sharing ≥1 address level" was really every thinned pair. +> The ≈23× ratio was arithmetic on a mislabelled denominator. (2) The *fix* then +> repeated the error one level up: 1,152 was the eligible **thinned sample**, not +> the eligible **population**. Root cause of both: a `(a+b) % 7` thinning that +> bought nothing — 32,640 pairs is trivial to enumerate. **The thinning is +> removed**, so there is no sample left to mislabel; the figures are now the full +> population, 360/8,064 = 4.5 %, independently reproduced by the reviewer. ## Verification - `cargo run --release --manifest-path crates/helix/Cargo.toml --example diff --git a/crates/helix/examples/probe_hhtl_intake_blindness.rs b/crates/helix/examples/probe_hhtl_intake_blindness.rs index 1067dd53f..baf9c8e91 100644 --- a/crates/helix/examples/probe_hhtl_intake_blindness.rs +++ b/crates/helix/examples/probe_hhtl_intake_blindness.rs @@ -261,18 +261,23 @@ fn main() { // correctly FAILED rather than passing vacuously. for a16 in 0u16..CELLS as u16 { for b16 in (a16 + 1)..CELLS as u16 { - if !(a16 as usize + b16 as usize).is_multiple_of(7) { - continue; // deterministic thinning; still thousands of pairs - } let (a, b) = (a16 as u8, b16 as u8); let k = shared_levels_4ary(a, b); if k == 0 { continue; // not eligible: no shared address level to preserve } - // COUNT ONLY ELIGIBLE PAIRS (codex P2). The first version incremented - // before this predicate, so the published "4,662 pairs sharing ≥1 - // address level" was really every thinned pair — a false sample size - // in the probe output AND in both board records. + // COUNT ONLY ELIGIBLE PAIRS, over the FULL population. + // + // Two successive labelling defects landed here, both caught in review: + // 1. (codex P2) the counter incremented BEFORE this eligibility + // check, so "4,662 pairs sharing ≥1 address level" was really + // every thinned pair; + // 2. (CodeRabbit) the corrected 1,152 was the eligible THINNED + // SAMPLE, not the eligible POPULATION — a second population-label + // error stacked on the fix for the first. + // A deterministic `(a+b) % 7` thinning caused both and bought nothing: + // 256·255/2 = 32,640 pairs is trivial to enumerate exhaustively. The + // thinning is removed, so there is no sample to mislabel. pairs += 1; // FLAT: one seed each. "Ancestry preserved" would mean cells sharing a // prefix share the seed — which mod 17 cannot arrange. @@ -293,8 +298,9 @@ fn main() { "H3 blindness is FIXABLE at the intake (per-tier seeds keep ancestry)", tier_agree == pairs && flat_rate < 0.25 && pairs > 100, format!( - "eligible population = {pairs} pairs sharing ≥1 address level (thinned \ - deterministically).\n FLAT intake preserves ancestry in \ + "eligible population = {pairs} pairs sharing ≥1 address level \ + (EXHAUSTIVE over all 256·255/2 = 32,640 cell pairs — no sampling, no \ + thinning).\n FLAT intake preserves ancestry in \ {flat_agree}/{pairs} = {:.1}% — mod 17 scrambles it.\n PER-TIER \ intake preserves it in {tier_agree}/{pairs} = 100% BY CONSTRUCTION \ (each 2-bit group seeds its own ruler); the gate asserts the equality \ @@ -321,8 +327,8 @@ fn main() { CALCULATOR'S INTAKE, not about the 4⁴ address, whose ancestry result stands\n\ (E-WORDNET-MAKES-THE-4-ARY-ADDRESS-SEMANTIC-1). H2 RETRACTS a suspicion it\n\ does not replace with a design claim — no shipped path reads a 4-step prefix.\n\ - H3 names the unblock and reports the flat rate (4.4%, ≈ the 1/17 ≈ 5.9%\n\ - chance level), not a ratio." + H3 names the unblock and reports the flat rate over the FULL population\n\ + (360/8064 = 4.5%, ≈ the 1/17 ≈ 5.9% chance level), not a ratio." ); if failures > 0 { std::process::exit(1); From d6e6d45820ef0e2abc70c75d178bfda0930554ee Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Jul 2026 07:13:25 +0000 Subject: [PATCH 4/4] =?UTF-8?q?probe(helix):=20replace=20H3's=20hand-tuned?= =?UTF-8?q?=20threshold=20with=20a=20declared=20null=20=E2=80=94=20which?= =?UTF-8?q?=20then=20failed=20and=20corrected=20the=20claim?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit review of a096114, five findings. All addressed; the fourth changed a result. 1. H3 THRESHOLD WAS HAND-TUNED. `flat_rate < 0.25` would have passed at 24.9% while the prose claimed ~1/17. Replaced with a declared null model rather than a better constant: H0 = "the flat seed carries no ancestry information", so its agreement rate among ELIGIBLE pairs must not exceed its rate among ALL pairs. Both computed exhaustively. Running it immediately FAILED my first formulation ("the two rates coincide") — and the failure was correct: eligible 360/8,064 = 4.4643% all pairs 1,800/32,640 = 5.5147% delta = -1.05 pp The rates do NOT coincide. The flat seed is not ancestry-neutral, it is mildly ancestry-AVERSE: cells sharing an address prefix are numerically close, so they are LESS likely to be congruent mod 17. The gate now asserts only the true, weaker claim — no ancestry LIFT — and reports the direction instead of assuming it. Falsifier: an eligible rate materially above base, which would mean the flat seed does carry ancestry. This is the second time in this probe that replacing a bad criterion exposed that the replacement claim was also wrong. Stating the null explicitly is what made it visible. 2. NO UNIT TESTS. Added a #[cfg(test)] module, 7 focused tests: the H1 constant shift plus a falsifier that a structure-carrying seed would NOT be constant-shift; exact H2 gap outputs; the shipped constants including 17 = 4^2+1 (stride 4 over 16 covers only 4/16); the H3 population and agreement counts (8,064 / 360 / total); shared-levels as a prefix measure; histogram coverage. 3. EPIPHANIES TITLE still carried the withdrawn "REFUTED ... matched to 4-ary" conclusion that the body had already retracted. Corrected. 4. EXEC-RUN H2 labels said REFUTED where the verdict is NOT CONFIRMED. Corrected throughout. 5. MD040: histogram fence now tagged `text`. Both board records updated with the null-model result and dated markers. Gates 3/3 green; 7/7 unit tests; clippy -D warnings clean (example-scoped per repo practice — --all-targets pulls in pre-existing lint debt in helix's own lib tests, unrelated to this diff). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01K3RyLEbuNSHxxB3NTTrGki --- .claude/board/EPIPHANIES.md | 4 +- .../board/exec-runs/hhtl-intake-blindness.md | 28 +++- .../examples/probe_hhtl_intake_blindness.rs | 140 ++++++++++++++++-- 3 files changed, 152 insertions(+), 20 deletions(-) diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index ba388e182..17747f3a7 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -1,4 +1,4 @@ -## 2026-07-31 — E-THE-RULER-READS-THE-ADDRESS-AS-A-NUMBER-NOT-A-HIERARCHY-1 — the helix intake is structure-blind (kills the residue sweep before it ran), and the "stride 4 isn't golden" suspicion is REFUTED by the shipped constants being matched to 4-ary +## 2026-07-31 — E-THE-RULER-READS-THE-ADDRESS-AS-A-NUMBER-NOT-A-HIERARCHY-1 — the helix intake is structure-blind (kills the residue sweep before it ran), and the "stride 4 isn't golden" suspicion remains NOT CONFIRMED (the 4-ary-matching claim drawn from it is withdrawn) **Status:** FINDING (measured, 3/3 gates green). **Confidence:** High — pure integer arithmetic over the shipped `CurveRuler`, no sampling. Probe: `crates/helix/examples/probe_hhtl_intake_blindness.rs`; record: `.claude/board/exec-runs/hhtl-intake-blindness.md`. @@ -10,7 +10,7 @@ **H2 — my own "stride 4 isn't golden" suspicion is NOT CONFIRMED; the design claim I drew from that is WITHDRAWN (codex P1 on #876).** The worry: `gcd(4,17)=1` buys coverage, not low discrepancy — stride 4's first four steps `0,4,8,12` are one column of the 4×4 raster, while the φ stride over 17 is 11 (`17/φ = 10.51`; C5 pins 11/17). Since HHTL terminates early, short prefixes are what get consumed. Measured gap spread (lower = more uniform): n=2 → 9 vs **5**(φ); n=3 → 5 vs **1**(φ); **n=4 → 1 vs 5** (stride 4 wins decisively); n=5 → **3** vs 4; n=6 → **3** vs 4. **At n=4 — exactly the prefix a 4-ary tier consumes — stride 4 gives gaps `[4,4,4,5]`, near-perfect tiling, because `4·4 = 16 ≈ 17`.** The comma IS what makes the 4-step prefix uniform. The operator's Pythagorean-comma framing is confirmed in mechanism: stride 4 over **16** covers only 4/16 and retraces one column forever; `17 = 4²+1` is what turns the raster into a sweep. **⊘ But "matched to the 4-ary use case" is withdrawn:** the premise that a 4-ary tier consumes 4 ruler steps is not wired into anything that ships — `ResidueEncoder::encode` reads only `start_offset` (n=1, `residue.rs:157`), `index`/`arc` appear solely in `walk_spectrum` and crate tests, and the shipped `NiblePath` is `FAN_OUT = 16`. The n=4 result is a property of a HYPOTHETICAL consumer. This RETRACTS a suspicion; it does not vindicate a design. -**H3 — the blindness is fixable, so this is a direction, not a dead end.** Per-tier seeding (one ruler per 2-bit group, folded with its level) preserves ancestry by construction: EXHAUSTIVE over all 256·255/2 = 32,640 cell pairs; eligible population **8,064** sharing ≥1 address level; flat intake preserves ancestry in **360/8,064 = 4.5 %** (≈ the 1/17 ≈ 5.9 % chance level), per-tier in **8,064/8,064 = 100 % by construction** (asserted as a construction check, not as evidence). ⊘ **The denominator was wrong TWICE:** first "4,662" (counter incremented before the eligibility check — codex P2), then "1,152" (the eligible *thinned sample* mislabelled as the *population* — CodeRabbit). Root cause of both: a `(a+b) % 7` thinning that bought nothing. Removing it eliminates the class of error, and the reviewer independently reproduced 360/8,064. The unblock is to feed the ruler the address's HIERARCHY, not its integer value — a helix API change that needs its own review. +**H3 — the blindness is fixable, so this is a direction, not a dead end.** Per-tier seeding (one ruler per 2-bit group, folded with its level) preserves ancestry by construction: EXHAUSTIVE over all 256·255/2 = 32,640 cell pairs; eligible population **8,064** sharing ≥1 address level; flat intake preserves ancestry in **360/8,064 = 4.4643 %** against a base rate of **1,800/32,640 = 5.5147 %** over ALL pairs — **Δ = −1.05 pp, so no ancestry lift at all**, and in fact mildly ancestry-AVERSE (prefix-sharing cells are numerically close, hence less likely to be congruent mod 17); per-tier **8,064/8,064 = 100 % by construction** (a construction check, not evidence). The criterion is a **declared null model** (H0 = the flat seed carries no ancestry information ⇒ eligible rate must not exceed base rate), replacing a hand-tuned `< 0.25` that 24.9 % would have passed (CodeRabbit). Its first formulation — "the rates coincide" — FAILED on contact and was itself corrected: they diverge, so the gate now asserts only the true, weaker claim. ⊘ **The denominator was wrong TWICE:** first "4,662" (counter incremented before the eligibility check — codex P2), then "1,152" (the eligible *thinned sample* mislabelled as the *population* — CodeRabbit). Root cause of both: a `(a+b) % 7` thinning that bought nothing. Removing it eliminates the class of error, and the reviewer independently reproduced 360/8,064. The unblock is to feed the ruler the address's HIERARCHY, not its integer value — a helix API change that needs its own review. **The transferable rule:** *a calculator's INTAKE can silently discard the very structure a probe is trying to measure.* Check what the consumer actually reads before running a sweep over what the producer emits — otherwise a null measures the seam, not the hypothesis. This is the same shape as the falsifier-defect pattern (four instances now: the non-falsifying shuffle, the hair-wide twin window, the inert cover guard, and — self-caught here — an H3 loop that sampled 0 pairs because `256 as u8 == 0` and correctly FAILED rather than passing on zero evidence). diff --git a/.claude/board/exec-runs/hhtl-intake-blindness.md b/.claude/board/exec-runs/hhtl-intake-blindness.md index 87613faac..11af749df 100644 --- a/.claude/board/exec-runs/hhtl-intake-blindness.md +++ b/.claude/board/exec-runs/hhtl-intake-blindness.md @@ -20,7 +20,7 @@ regenerates the interior; `ResidueEdge`/`Signed360` are the stored residue. The queued hypothesis (finer address ⇒ more deterministic place ⇒ less stored residue) was gated behind two PRE-REGISTERED failure modes. Both were run first, -exactly as the brief required. One is fatal to the sweep; the other is REFUTED. +exactly as the brief required. One is fatal to the sweep; the other is NOT CONFIRMED (a suspicion retracted, not a hypothesis disproven). ### H1 — the carving cannot reach the ruler (failure mode (a): CONFIRMED, fatal) @@ -31,7 +31,7 @@ difference between the two carvings, as the ruler sees it, is a **constant rotation of 2 across all 256 cells, variance 0** — a cell-dependent shift is the falsifier. Unsorted histograms: -``` +```text 4-ary (depth 4): [15,15,15,15,16,15,15,15,15,15,15,15,15,15,15,15,15] 16-ary (depth 2): [15,15,16,15,15,15,15,15,15,15,15,15,15,15,15,15,15] ``` @@ -47,7 +47,7 @@ would have been a false negative caused entirely by the intake, and reporting "4⁴ doesn't help the calculator" from it would have been wrong. Reported as an INTAKE finding, per the pre-registration. The 4⁴ ADDRESS result is untouched. -### H2 — stride 4 vs the φ stride 11 (failure mode (b): REFUTED) +### H2 — stride 4 vs the φ stride 11 (failure mode (b): NOT CONFIRMED) The suspicion: `gcd(4,17)=1` buys coverage, not low discrepancy; stride 4's first four steps `0,4,8,12` are one column of the 4×4 raster, while the φ stride @@ -85,10 +85,24 @@ irrational stride as Pythagorean comma."* Confirmed in mechanism — stride 4 ov Per-tier seeding (one ruler per 2-bit group, folded with its level) preserves ancestry by construction. **EXHAUSTIVE over all 256·255/2 = 32,640 cell pairs — no sampling, no thinning.** Eligible population = **8,064** pairs sharing ≥1 -address level: **flat intake preserves ancestry in 360/8,064 = 4.5 %** (≈ the -1/17 ≈ 5.9 % chance level — the flat intake keeps ancestry about as often as -chance would); **per-tier in 8,064/8,064 = 100 % BY CONSTRUCTION**, asserted as a -construction check rather than as evidence. The unblock is to feed the ruler the +address level: **flat intake preserves ancestry in 360/8,064 = 4.4643 %** against a base rate +of **1,800/32,640 = 5.5147 %** over ALL pairs — Δ = **−1.05 pp**, i.e. the flat +seed gives **no ancestry lift at all** and is in fact mildly ancestry-AVERSE +(cells sharing an address prefix are numerically close, so they are *less* likely +to be congruent mod 17). **Per-tier: 8,064/8,064 = 100 % BY CONSTRUCTION**, +asserted as a construction check rather than as evidence. + +The acceptance criterion is a **declared null model**, not a threshold: H0 = "the +flat seed carries no ancestry information", so the eligible rate must not EXCEED +the base rate. Falsifier: an eligible rate materially above base, which would +mean the flat seed *does* carry ancestry. + +> **⊘ THRESHOLD REPLACED (CodeRabbit on #876).** The gate was `flat_rate < 0.25` +> — hand-tuned, and 24.9 % would have passed it while the prose claimed ≈1/17. +> Replaced with the null-model comparison above. Running it immediately FAILED +> the first formulation ("the two rates coincide"), which was itself wrong: the +> rates do not coincide, they diverge by −1.05 pp. The gate now states the true +> and weaker claim — no lift — and the direction is reported rather than assumed. The unblock is to feed the ruler the address's HIERARCHY, not its integer value. > **⊘ SAMPLE SIZE CORRECTED TWICE (codex P2, then CodeRabbit, both on #876).** diff --git a/crates/helix/examples/probe_hhtl_intake_blindness.rs b/crates/helix/examples/probe_hhtl_intake_blindness.rs index baf9c8e91..99b729920 100644 --- a/crates/helix/examples/probe_hhtl_intake_blindness.rs +++ b/crates/helix/examples/probe_hhtl_intake_blindness.rs @@ -294,21 +294,45 @@ fn main() { // should equal the eligible population exactly; asserting that is the // construction check, and the informative number is the FLAT rate. let flat_rate = flat_agree as f64 / pairs as f64; + + // DECLARED NULL MODEL (replaces a hand-tuned `flat_rate < 0.25`, which + // 24.9 % would have passed while the text claimed ≈1/17 — CodeRabbit). + // H0: "the flat seed carries no ancestry information." Under H0 its + // agreement rate among ELIGIBLE pairs (sharing ≥1 address level) must equal + // its rate among ALL pairs, because ancestry would be irrelevant to it. + // Both are computed exhaustively; the gate is that they COINCIDE. + let (mut all_pairs, mut all_agree) = (0usize, 0usize); + for a16 in 0u16..CELLS as u16 { + for b16 in (a16 + 1)..CELLS as u16 { + all_pairs += 1; + if flat_seed(a16 as u8) == flat_seed(b16 as u8) { + all_agree += 1; + } + } + } + let null_rate = all_agree as f64 / all_pairs as f64; gate( "H3 blindness is FIXABLE at the intake (per-tier seeds keep ancestry)", - tier_agree == pairs && flat_rate < 0.25 && pairs > 100, + tier_agree == pairs && flat_rate <= null_rate && pairs > 100, format!( "eligible population = {pairs} pairs sharing ≥1 address level \ - (EXHAUSTIVE over all 256·255/2 = 32,640 cell pairs — no sampling, no \ - thinning).\n FLAT intake preserves ancestry in \ - {flat_agree}/{pairs} = {:.1}% — mod 17 scrambles it.\n PER-TIER \ - intake preserves it in {tier_agree}/{pairs} = 100% BY CONSTRUCTION \ - (each 2-bit group seeds its own ruler); the gate asserts the equality \ - as a construction check, not as evidence.\n The informative \ - quantity is the flat rate, and the honest reading is \"the flat intake \ - keeps ancestry about as often as chance would\" — NOT a tuned ratio. \ - The fix is to feed the ruler the address's HIERARCHY, not its integer.", - 100.0 * flat_rate + (EXHAUSTIVE over all {all_pairs} = 256·255/2 cell pairs — no sampling, \ + no thinning).\n FLAT intake: {flat_agree}/{pairs} = {:.4}% among \ + ELIGIBLE pairs vs {all_agree}/{all_pairs} = {:.4}% among ALL pairs \ + (H0 = \"the flat seed carries no ancestry information\", so the \ + eligible rate should not EXCEED the base rate). Δ = {:+.4} pp — the \ + eligible rate is BELOW the base rate, so the flat seed gives no \ + ancestry lift; it is in fact mildly ancestry-AVERSE, because cells \ + sharing an address prefix are numerically close and therefore less \ + likely to be congruent mod 17. Gate = no lift (falsifier: an eligible \ + rate materially above the base rate, which would mean the flat seed \ + DOES carry ancestry). No hand-picked ceiling.\n PER-TIER intake: {tier_agree}/{pairs} = \ + 100% BY CONSTRUCTION (each 2-bit group seeds its own ruler) — asserted \ + as a construction check, not as evidence.\n The fix is to feed \ + the ruler the address's HIERARCHY, not its integer.", + 100.0 * flat_rate, + 100.0 * null_rate, + 100.0 * (flat_rate - null_rate) ), ); @@ -334,3 +358,97 @@ fn main() { std::process::exit(1); } } + +#[cfg(test)] +mod tests { + use super::*; + + /// H1: the carving reaches the ruler only as a constant rotation. + #[test] + fn carving_difference_is_a_constant_shift() { + let shifts: Vec = (0..CELLS) + .map(|c| { + let a = CurveRuler::from_hhtl(c as u64, 4).start_offset() as i16; + let b = CurveRuler::from_hhtl(c as u64, 2).start_offset() as i16; + (a - b).rem_euclid(CurveRuler::MODULUS as i16) as u8 + }) + .collect(); + assert_eq!(shifts.len(), CELLS); + assert!( + shifts.iter().all(|&s| s == 2), + "expected constant shift of 2" + ); + } + + /// Falsifier for the above: a genuinely structure-carrying seed must NOT + /// produce a constant shift. Guards against the gate passing for any seed. + #[test] + fn a_structure_carrying_seed_would_not_be_constant_shift() { + let shifts: std::collections::HashSet = (0..CELLS) + .map(|c| { + let t = tiered_seeds(c as u8); + (t[0] as i16 - t[3] as i16).rem_euclid(CurveRuler::MODULUS as i16) as u8 + }) + .collect(); + assert!(shifts.len() > 1, "per-tier seeds must vary by cell"); + } + + /// H2: the exact recorded gap structure, so a constants change is caught. + #[test] + fn prefix_gap_spread_matches_recorded_values() { + assert_eq!(prefix_gap_spread(4, 4), (1, vec![4, 4, 4, 5])); + assert_eq!(prefix_gap_spread(11, 4).0, 5); + assert_eq!(prefix_gap_spread(4, 2).0, 9); + assert_eq!(prefix_gap_spread(11, 2).0, 5); + } + + /// The shipped constants this probe reasons about. + #[test] + fn shipped_constants_are_what_the_probe_assumes() { + assert_eq!(CurveRuler::MODULUS, 17); + assert_eq!(CurveRuler::STRIDE, 4); + // 17 = 4² + 1 — the comma. Without the +1, stride 4 mod 16 covers 4/16. + let covered: std::collections::HashSet = (0..16u8).map(|k| (4 * k) % 16).collect(); + assert_eq!(covered.len(), 4, "stride 4 over 16 retraces one column"); + } + + /// H3: the recorded population and agreement counts. + #[test] + fn h3_population_and_agreement_counts() { + let (mut eligible, mut flat, mut tier) = (0usize, 0usize, 0usize); + for a16 in 0u16..CELLS as u16 { + for b16 in (a16 + 1)..CELLS as u16 { + let (a, b) = (a16 as u8, b16 as u8); + let k = shared_levels_4ary(a, b); + if k == 0 { + continue; + } + eligible += 1; + if flat_seed(a) == flat_seed(b) { + flat += 1; + } + if shared_seed_prefix(tiered_seeds(a), tiered_seeds(b)) >= k { + tier += 1; + } + } + } + assert_eq!(eligible, 8064, "eligible population"); + assert_eq!(flat, 360, "flat-intake ancestry agreements"); + assert_eq!( + tier, eligible, + "per-tier agreement is total by construction" + ); + } + + #[test] + fn shared_levels_is_a_prefix_measure() { + assert_eq!(shared_levels_4ary(0b00_00_00_00, 0b00_00_00_11), 3); + assert_eq!(shared_levels_4ary(0b01_00_00_00, 0b10_00_00_00), 0); + assert_eq!(shared_levels_4ary(0b11_01_10_00, 0b11_01_10_00), 4); + } + + #[test] + fn start_histogram_covers_every_cell() { + assert_eq!(start_histogram(4).iter().sum::(), CELLS); + } +}