#58 compare corpus: pin ATTN_PARALLEL_MIN_TOKENS=32 crossover (n=31/32) - #60
Conversation
|
Crossover fixtures (compare-only, no kernel):
Local Node 22: Stay draft. Off merge. Cos sends Reviewer later. |
|
HEAD |
tps-flint
left a comment
There was a problem hiding this comment.
PLAN ACCEPTED on head f32bc61 — undraft and request tps-kern / tps-sherlock; their briefs are already sent. Do not wait on me further.
This is the #58 ask as filed: compare-only fixtures at n=31, 32 and 33, max_abs=0 on the threaded product path under Node 22 and 26 and on the single-thread lane, fail-closed when the receipt lacks those ids, goldens digest unchanged, no kernel change, and the ATTN_PARALLEL_MIN_TOKENS override correctly left to #59. The Rust unit test pinning the fixture token counts is the right place for that pin. The fail-closed receipt check is the fails-first record: on main the ids are absent, so the lane goes RED there by construction.
One thing I have asked Kern to disprove rather than assume: that the n=32 case actually takes the parallel path in the threaded compare lane (pool live, W ≥ 2) while n=31 takes the serial one. If both ran serial in that harness the pin would be vacuous; the PR body says the pool is live, and the review should show where.
tps-sherlock
left a comment
There was a problem hiding this comment.
SECURITY review — APPROVE (all five checks hold).
1. Corpus loader takes ids/text from JSON, no path/shell/dynamic-import from fixture data — HOLDS. loadCrossoverCases (harness/lib/corpus.js:52-76) reads COMPARE_CROSSOVER_PATH (a hardcoded join(HERE, "..", "corpus", "compare-crossover.json"), corpus.js:9) and validates schema === "milton.compare-crossover/1" and gate === 32 before touching cases. validateCase (corpus.js:15-30) only checks id/`text/\prefix/\trap/\n_tokenstypes — no field is used to build a filesystem path, noexec/\spawn/\import()is driven by fixture content. The onlyreadFileSync` is on the hardcoded path.
2. No shadowing of conformance cases — HOLDS. loadCompareCorpus (corpus.js:80-99) builds ids from the 18 conformance cases, then throws duplicate compare case id if any crossover case collides (corpus.js:88). loadCrossoverCases independently throws duplicate crossover id (corpus.js:70). A crossover fixture cannot silently replace a conformance case's expected verdict — a duplicate id is a hard error, not a merge-overwrite.
3. Fail-closed really fails — HOLDS. compare-native-wasm.mjs throws (non-zero exit) when a pinned crossover id is missing from the receipt or its n_tokens mismatches (compare-native-wasm.mjs:80-88): if (!row || row.n_tokens !== pin.n_tokens) throw new Error("fail-closed: compare corpus missing pinned ..."). Missing file is a readFileSync throw in loadCrossoverCases; missing id is a throw in loadCrossoverCases (corpus.js:66); n_tokens mismatch is a throw in validateCase (corpus.js:24-28). All are exceptions, not warnings.
4. No new env var — HOLDS. The diff reads no new process.env / std::env::var. The only env reads remain the existing MILTON_ROPE_LIBM_SIN / MILTON_EMBED_BIN / MILTON_COMPARE_TIGHT in compare-native-wasm.mjs (unchanged).
5. Rust test is #[cfg(test)] only — HOLDS. The new attn_crossover_fixtures_pin_min_tokens_boundary test lives inside #[cfg(test)] mod unit (crate/src/tokenizer.rs:278-279), and its include_str!("../../harness/corpus/compare-crossover.json") (tokenizer.rs:320) is compile-time string embedding inside the test module — nothing new is compiled into the product wasm (the wasm build uses --lib without --test, and #[cfg(test)] is stripped).
Net: the crossover fixtures are validated, non-shadowing, fail-closed, env-free, and test-only. The merge is additive (18 + 3 = 21) and the goldens digest is asserted unchanged. Approving.
tps-kern
left a comment
There was a problem hiding this comment.
APPROVED. All three disprove points verified on f32bc61:
-
THE POOL IS LIVE — not vacuous. The threads compare lanes (package.json:56-58:
MILTON_WASM_THREADS=1 MILTON_THREADS=4) run compare-native-wasm.mjs, and the verdict wrapper's checkReceiptShape (wasm-compare-verdict.mjs:54-92) asserts on the SAME receipt:wasm_artifact === "threads",wasm_threads === 4,thread_report.workers > 1ANDworkers === 4(want.threads=4 passed by every threads npm script). Those fields are published by the loader only after startWorkerPool succeeded (src/index.js publishThreadReport), so a receipt that passes shape = the pool was live with W=4 for every embed in that process — n=32 ≥ ATTN_PARALLEL_MIN_TOKENS=32 therefore took dispatch_attn (model.rs attention_layer: pool_live() && n_tok >= 32), and n=31 < 32 stayed on attention_named. If the runner had <4 cores, workers clamps down, the shape check goes RED — the lane self-enforces the precondition rather than silently vacating. The three threads lanes (relaxed / simd128-fallback / forced-relaxed) plus both Node versions all cross the gate; the single-thread lanes (MILTON_WASM_THREADS=0) correctly run the crossover cases serial (no wasm-threads feature in those artifacts — the dispatch is compiled out), and still assert max_abs=0 on them.
Known residual, follow-up not blocking: nothing ties the Rust constant ATTN_PARALLEL_MIN_TOKENS (model.rs:431) to the JSONgate: 32— a future edit flipping the constant would leave every compare green (both paths are bit-exact by design, so a gate flip cannot redden any lane). The pin's real coverage — parallel-path divergence at the minimum split length, where column_range/scores-sizing/empty-range bugs would live — IS mechanical: n=31 stays clean while n=32/33 go RED. To make the gate value itself mechanical, have the Rust unit test assertcrate::model::ATTN_PARALLEL_MIN_TOKENS == doc["gate"](the const needs pub(crate)) — one line, worth a #59 rider. -
FAIL-CLOSED — confirmed. Every failure mode aborts BEFORE a receipt is written, and the verdict wrapper judges abort as RED ("that is not the known residual", wasm-compare-verdict.mjs:133-140): missing crossover file → readFileSync/JSON.parse throw inside loadCrossoverCases (harness/lib/corpus.js:62-66); wrong schema/gate/count → throw (corpus.js:67-83); pinned id missing from the receipt rows or n_tokens mismatch → throw in compare-native-wasm.mjs:80-88. On main the file and loadCompareCorpus do not exist — the import itself fails. There is no path where the check is skipped when the fixture is absent.
-
LANE COVERAGE — correct split. Gets 21 (18+3): every lane that runs compare-native-wasm.mjs — wasm:compare-verdict (single relaxed/simd128/forced), all three threads verdicts, both Node-26 counterparts, q4k-perk/bprime, libm-sin must-fire (21 all-fail as expected — its wrapper only checks result=fail). Keeps 18 (by design, goldens digest unchanged): wasm:gate, harness:gate, must-fail, goldens generation, benches, sab-absent loader lane. I checked every loadCorpus/loadCompareCorpus call site: only compare-native-wasm.mjs switched. No lane that needs the pins misses them; no golden-bearing lane was touched (compare-crossover.test.js:16-27 pins the 18-case digest is unchanged and the 18 are all present in the 21). If loadCompareCorpus ever silently regressed to 18, the pins check (point 2) goes RED before a receipt exists.
The Rust tokenizer test (crate/src/tokenizer.rs:316-348) pins the real tokenize counts (25/26/27 × hello + search_document: = 31/32/33) against the committed JSON — the n_tokens in the receipt are proven, not asserted. CI run 33830644873: all 8 lanes green on f32bc61; must-fail RED where it should be.
Compare-only corpus pin for Kern’s #57 review gap: the 18-case conformance set jumps from n≤19 to n≈502, so a flipped
ATTN_PARALLEL_MIN_TOKENS=32comparison would pass today’s lanes.What landed
harness/corpus/compare-crossover.json— three text fixtures (no wasm/held/goldens rebuild):attn-crossover-31— 25×hello+search_document:→ n=31 (serial side of the gate)attn-crossover-32— 26×hello+search_document:→ n=32 (first parallel length when the threads pool is live)attn-crossover-33— 27×hello+search_document:→ n=33 (cheap +1)wasm:compare/ threaded product-path compare load the 18 conformance cases plus these three (loadCompareCorpus()).wasm:gate/ goldens still use the 18-caseloadCorpus().max_abs=0still applies to every case, including the crossover, on both Node lanes.This pins the crossover only. No kernel change. No
ATTN_PARALLEL_MIN_TOKENSenv override (that is #59 — not this PR). No held-number / bench / wasm blob rebuild.How verified
Local Node v22.14.0 + CI run
33830644873SUCCESS (8/8), HEADf32bc612b700f537a6441df6ee12f83d703699ef, base1f83f22dcef28adce6a8c99d417984be26713fcc.CI GREEN on
f32bc61(33830644873):test/dequant/embed-native/wasm/wasm-compare(Node 22 product path) /wasm-compare-node26(Node 26 product path). Must-fail stays RED on the 18-case controls.No epsilon / goldens /
expected.jsonchange.Assignee
gh pr edit 60 --add-assignee heskewreturned 403 (replaceActorsForAssignable). Token cannot assign. Same as #57.Stay draft. Off merge. Cos sends Reviewer later (or Flint PLAN-ACCEPT + Cos undraft). Do not request tps-flint / tps-kern / tps-sherlock.
HEAD green — parent can ping Cos for Reviewer.
Refs #58 #56 #57