Skip to content

feat(vindex-factory): PREFLIGHT->RELEASE build driver (§7) - #196

Merged
chrishayuk merged 74 commits into
mainfrom
feat/dec-funnel-v0-4
Aug 1, 2026
Merged

feat(vindex-factory): PREFLIGHT->RELEASE build driver (§7)#196
chrishayuk merged 74 commits into
mainfrom
feat/dec-funnel-v0-4

Conversation

@chrishayuk

Copy link
Copy Markdown
Owner

Summary

  • larql-factory::build::run_build orchestrates FETCH → EXTRACT → SLICE → MANIFEST → VERIFY → PUBLISH → RELEASE as subprocess calls into larql itself, behind a CommandRunner trait (SubprocessRunner for real builds, MockRunner for tests) — the whole pipeline's stage ordering and failure handling is unit-tested without spawning a process or touching credentials.
  • PUBLISH always uploads --private; RELEASE only flips a repo public once every output has verified (§8's "nothing goes public unverified"). Extends larql-vindex's publish path with a private/visibility option (verified against the real HF OpenAPI spec) and a new larql hf visibility command to make that possible.
  • MIRROR (R2) and REGISTER (chuk-experiments-server) stay external per the earlier scope decision — nothing in this codebase talks to either, and the spec's own text assumes both are the rig worker's job. A BuildRecord JSON is the hand-off point, matching dec-bench's --output-file pattern.
  • Wired in as larql recipe build <FILE> [--scratch-dir DIR].
  • README, ROADMAP, ROADMAP_STATUS, docs/cli.md, crates/larql-factory/README.md updated; two factual corrections landed in docs/vindex-factory.md itself (driver command name, VERIFY scope).

Test plan

  • cargo build -p larql-factory -p larql-cli
  • cargo test -p larql-factory --lib (192 tests) and cargo test -p larql-cli recipe_cmd
  • cargo clippy -p larql-factory -p larql-cli --all-targets -- -D warnings
  • cargo fmt -p larql-factory -p larql-cli -- --check
  • cargo llvm-cov -p larql-factory --lib — every new build/ file at or above the 90% line-coverage floor

@chrishayuk
chrishayuk force-pushed the feat/dec-funnel-v0-4 branch from 521d7b5 to a75d305 Compare July 30, 2026 22:50
`larql-factory::build::run_build` orchestrates FETCH/EXTRACT/SLICE/
MANIFEST/VERIFY/PUBLISH/RELEASE as subprocess calls into `larql`
itself, behind a CommandRunner trait (SubprocessRunner for real
builds, MockRunner for tests). PUBLISH always goes private first;
RELEASE only flips a repo public once every output has verified
(§8's "nothing goes public unverified"). MIRROR and REGISTER stay
external per the earlier scope decision — a BuildRecord JSON is the
hand-off point, matching dec-bench's --output-file pattern.

Extends larql-vindex's publish path with a private/visibility option
and a new `larql hf visibility` command to make the two-phase publish
possible. Wired in as `larql recipe build <FILE> [--scratch-dir DIR]`.

Every new file at or above the 90% coverage floor.
estimate::http::tests::hf_token_reads_the_env_var and
hf_token_treats_blank_as_absent both mutate the process-wide HF_TOKEN
env var — a real race under parallel test execution (CI's
test-windows-latest job just hit it). #[serial_test::serial] them,
matching the pattern larql-vindex already uses for its own env-var
tests.
Closes the last unpriced bandwidth surface. Real geometry rather than assumed
(30 layers, kv_dim 2048 from num_key_value_heads 8 x head_dim 256,
sliding_window 1024, explicit layer_types = 5 global / 25 sliding), against the
measured 3.63 GB/token budget rather than a summed weight estimate — that
sidesteps an unresolved question about whether the tied lm_head is read at f16
or q4, which would move a weight-based denominator by about a gigabyte.

f16 KV would buy 3-8% of a token at chat-typical context. More decisively it
cannot cause the decay it was proposed for: the sliding window already bounds
25 of 30 layers, so past ctx 1024 the byte slope collapses six-fold to
0.65 us/ctx-token against a recorded decay implying ~12. Bytes are a hard lower
bound on time, so C10's original attribution to GQA compute was substantially
right. The bytes thesis died for a couple of hours of arithmetic instead of a
KV-quantisation build.

It keeps value on the DEC-2 ledger as capacity, not speed: f32 costs 480 KB per
context-token per client, ~1 GB per client at ctx 2048, which is what caps
clients-per-box — so halving it roughly doubles client density.

Registry: experiment c0-bandwidth-roofline (programme dec), run
RUN-20260729-221048-00532, artifact 998.
The dispatcher's completion barrier was an unbounded `spin_loop()`. `worker_loop`
received a spin -> yield -> park backoff when the pool went default-on; this
barrier was left as a pure spin, so a dispatcher burns a whole core waiting on
`completed` while the worker that owes the last chunk may not be able to get
scheduled. With more runnable threads than cores — several concurrent
dispatchers, the pool's own persistent workers, and a test harness running other
tests alongside — every spinner holds its core for a full quantum and forward
progress can stall for seconds.

Escalates to `yield_now` and stays there. It must never park: nothing unparks a
dispatcher, since workers signal only through the `completed` counter, so a
parked dispatcher would sleep to its own timeout with no one to wake it.
Yielding is what actually cedes the core to the worker being waited on.

Fast path unchanged — decode-shaped dispatches complete in microseconds and
never leave the spin phase, so the measured +28% is preserved by construction.

SCOPE, stated honestly: this is hardening, NOT a fix for
`stress_concurrent_realistic_decode_shape_no_corruption`. That test asserts
buffer *corruption*, which means the barrier released before workers finished —
a different and more serious failure than starvation, and one this change does
not address. What this change does cover is the *hang* mode, which reproduces on
pre-existing code (5 in 22 runs at --test-threads=8 on b39061f, before any of
this session's spin-pool edits, so the defect is not a regression from the
performance-core cap). An earlier hypothesis for the hang — strided chunk
ownership defeating the hardware prefetcher — was wrong and killed by
measurement: contiguous blocks did not close the gap, participant count did.

The corruption mode needs its own investigation with the actual assertion output
(chunk index, buffer, expected vs actual). The obvious candidates are all sound:
static ownership means no participant can skip or double-run a chunk; num_chunks
(32-320) always exceeds participant count so no participant owns zero chunks and
the barrier cannot pass without every worker reporting; and the
fetch_add(Release)/load(Acquire) pair does make writes visible.

Tests: 797 larql-compute / 193 larql-compute-metal / 1309 larql-inference /
766 larql-kv / 1141 larql-vindex / 446 larql-models green; clippy clean.
…der spec

GPT-OSS attention uses a learned per-head sink logit that competes in the
softmax and is then discarded, so attention weights over real positions
deliberately sum to less than one. Until now larql neither extracted nor applied
it — along with all four projection biases — so 5 of 11 attention tensors per
layer (120 tensors) were silently dropped and the forward pass was systematically
wrong. The architecture module's header claimed both while every accessor
returned None, and extraction never requested attention biases for ANY
architecture, so Qwen/GPT-2/StarCoder2 declared them and lost them too.

Measured sink magnitudes (mean +2.45, max +8.19, positive in every layer) make
omitting them a systematic error rather than a rounding one.

Now extracted (manifest 145 -> 265 entries) and applied on every path:
- one shared `softmax_in_place` replacing four byte-identical copies
- one shared sink resolver (`attention/sinks.rs`)
- three Metal kernels (attn_fused, fused_attention, kv_append_attend_fused)

Parity: NumPy reference < 1e-7 on CPU; CPU-reference < 1e-4 for Metal prefill
and kv_append_attend_fused; and an exact analytic invariant for attn_fused — a
sink rescales each head uniformly, a fingerprint no other bug reproduces.

Also lands `docs/k3-funnel.md` v0.1, splitting the model-side K3 work out of
DEC-6 into a three-rung adapter ladder: R1 GPT-OSS-20B (expert/format pipeline)
-> R2 Kimi Linear 48B-A3B (the KDA stack, against a local reference
implementation) -> R3 K3 as a delta, with phases P1-P6, gates G0/GA/GB/GC/GD and
registry programme `k3`. The rationale is verification economics: R2 is K3's
direct architectural ancestor and fits on the Mac, so Gate B becomes a
layer-by-layer f32 diff instead of API-oracle triangulation.

Known gap: `larql shannon score` (and therefore `shannon verify`) uses a
dense-only FFN backend, so it cannot score any MoE model, GPT-OSS included. The
end-to-end bits/char cost of the sink bug is therefore still unmeasured — and
the same gap means the C6 wire-fidelity gate in DEC-1A cannot cover the 26B
routed path either.

Docs also carry the C0 bandwidth-roofline follow-ups: the C-ladder's acceptance
criterion should be judged against x86's own attainable ceiling rather than
Apple-Silicon per-core (measure it with the membw probe while the DEC-0.5 box is
up), and DEC-2 gains client-side KV footprint as a capacity axis — f32 costs
~1 GB of cache per client at ctx 2048, which may bind the N-at-SLO table before
tier bandwidth does.

Tests: 797 larql-compute / 193 larql-compute-metal / 1309 larql-inference /
766 larql-kv / 1141 larql-vindex / 446 larql-models green; clippy clean.
The k-quant writers pad every row to the 256-element super-block
boundary (pad_rows_to_block), but the dequant caches
(kquant_ffn_layer{,_once}) and dequantize_matrix decoded assuming an
unpadded [rows, cols] layout — on models with hidden % 256 != 0
(GPT-OSS-20B 2880, Gemma 3 1B 1152) every row after the first read
shifted bytes: silent garbage, no diagnostic. One of three readers
(kquant_forward/walk_ffn.rs) had been fixed for down only; the same
class also hit the attention Q/K/V/O dequant in tensors.rs.

- larql-models: k_quant_padded_cols() — one shared stride formula for
  the writer and both readers.
- larql-vindex: new ffn_store/kquant_decode.rs owns the row-padded
  decode + feature-major down transpose; both caches rewired through
  it.
- larql-inference: dequantize_matrix is padding-aware (callers pass
  logical dims); three hand-rolled down special-cases deleted.
- Non-256-aligned (hidden=320) regression fixtures at three levels:
  pure decode, cache path through load_interleaved_kquant, and
  dequantize_matrix. Every prior Q4K fixture was 256-aligned, so the
  suite structurally could not catch this class.

Review: docs/audits/vindex-walkffn-review-2026-07-30.md (H1).
- Overlay gate cache: a zero-length gate vector poisoned
  layer_gate_cache row alignment — nondeterministic slice panic or
  wrong KNN scores depending on HashMap iteration order (H3). The
  guard now bails to the safe slow path, and Vindexfile INSERT no
  longer stores empty gate vectors (slot claiming moved to the meta
  override).
- Loader: out-of-range layer entries in index.json return
  VindexError::Parse instead of panicking (H4), in both
  load_vindex_with_range and synthesize_gate_from_q4k.
- format/le_floats.rs: shared explicit-LE f32 slice codec replacing
  three unaligned &[u8]->&[f32] transmutes (UB) in patch/format.rs,
  quant/convert.rs, config/dtype.rs. Encoders are explicit LE —
  byte-identical on all supported targets, and the on-disk format is
  now portable (M4).
- try_apply_patch: all-or-nothing patch application. Vector decode
  failures (including the base64 decoder's previously-silent
  truncated tail, now an error) no longer half-apply metadata (M5).
  apply_patch stays as an infallible wrapper that drops corrupt
  patches wholesale; migrating larql-server/larql-lql callers to
  try_apply_patch is a tracked follow-up.
- Tombstones: Update resurrects a Deleted slot, matching Insert, so
  feature_meta() and gate_knn() agree in both directions; a pinned
  None meta is dropped when the Update carries no replacement (M6).
- Deletion oversampling: BASE_KNN_OVERSAMPLE_FACTOR (2x) escalates
  4x -> all-features when tombstones would under-fill top-k (M11);
  layers without deletions keep the single cheap query.
- Delete the orphaned src/walk/ module — never declared, never
  compiled, stale WalkFfnConfig duplicate.

Review: docs/audits/vindex-walkffn-review-2026-07-30.md.
Fixes (2026-07-30 review):
- The Q4_0 ladder step gated on supports_quant(Q4_K), admitting
  CpuBackend (which advertises Q4-family matvec kernels but leaves
  q4_matvec_pair_batch at the trait default) into an unwrap-on-None
  panic on the first layer (H2). Gate on Q4_0 — the format the data
  actually is — and probe the batch API by calling it, with a
  C-kernel fallback on decline. First-ever tests for
  interleaved_q4.rs, including exact CPU-vs-backendless equality.
- Overridden layers whose sparse walk fails now route directly to
  the override-aware weights_fallback (ladder step 10, extracted)
  instead of falling through the L1 cache and the override-blind
  whole-layer paths (M7). Regression pins that the L1 cache can
  never serve an overridden layer.
- activation_floor is live: effective_activation_floor() =
  max(user floor, named ACTIVATION_NOISE_FLOOR), applied on all
  three sparse accumulate loops; behavioral test (M3).
- Dispatch thresholds named in thresholds.rs: FULL_K_DENSITY 4/5
  (helper doc now states the [80%,100%) K band routes dense — M1),
  PARALLEL_DOWN_MIN_HITS, GATHER_MIN_FEATURES.
- selector:fallback dispatch-trace entries + selector_fallback_count()
  so joint-selector A/B sweeps can no longer silently degrade to
  GateOnly under the wrong label (M10).
- NaN contract unified: selection_weight_cmp_desc panics on NaN
  (matching top_k_by_abs's pinned contract) across the four former
  partial_cmp().unwrap_or(Equal) sites.
- Gather kernel caveat ("not yet correct for production down")
  removed as stale: it dates from task #24's transposed-down
  striding; task #25's hard feature-major-sidecar requirement
  resolved it (validated vs dense, |err|/||ref|| ~ 6e-3). The
  correctness condition and history are documented in
  sparse_gather.rs.

Split (no behavior change; walk_ffn suite 60 -> 63 tests):
- mod.rs 1054 -> 396: builders.rs (constructors/builders),
  timings.rs (PhaseTimingsHandle + env trace), dispatch_tests.rs.
  mod.rs keeps the routing table, struct, ladder, trace accessors.
- sparse.rs 890 -> 386: sparse_gemv.rs (full-K path),
  sparse_route.rs (hit/route selection), sparse_parallel.rs
  (parallel Q4K down), sparse_gather.rs (gather kernel). sparse.rs
  keeps entry, preflight, and the serial correctness baseline.
- CellRouter -> vindex/cell_router.rs, re-exported so existing use
  paths are unchanged.

Review: docs/audits/vindex-walkffn-review-2026-07-30.md.
Full review record in docs/audits/vindex-walkffn-review-2026-07-30.md
(two-reader subsystem review of larql-vindex and the walk-FFN engine,
merged with an external strategic review and a kernel deep-dive; the
four high-severity findings and two structural claims hand-verified).

ROADMAP.md gains the 24-item programme under Codebase hardening.
Items 1-12 and 14 landed same-day on this branch. Remaining: item 13
(the HNSW hot-path decision), Tier 2 (forward/forward_observed split,
base+delta patched execution, runtime trace emission, execution
planner, two-stage selection), and Tier 3 (walk-vs-dense parity
suite, KnnStore unification, v1 conformance contract, doc drift,
hygiene).
…tch path

The kv_dispatch helpers ran attention -> clip -> run_ffn only, dropping the
issue-#98 PLE/layer_scalar fix that lived solely in the legacy path
NoCacheEngine uses, so the two exact baselines disagreed on Gemma-4-class
archs. All four loops (sync/async x prefill/decode) now mirror the legacy
order; MM prefill-from-hidden passes token_ids: None (vision rows have no
token identities). Bit-exact parity tests added (prefill + 4 decode steps,
sync and async). The E2B fixture is now seeded non-zero with layer_scalar
0.75 - the zero-filled fixture is what had masked the bug.
…-loop PLE sweep

markov-rs/-codec: walk and executor decode paths read shape()[0] where the
2026-05-19 doubling-capacity migration made hot_len/cold_len canonical -
windowed configs attended zero-padded phantom rows at shifted RoPE
positions; the codec walk dropped the cold tier from attention permanently
after the first overflow (cold_kv cleared, cold_encoded never consulted).
Both fixed, plus a structural kv_recomputable_from_residuals() gate at
every prefill entry enforcing the exactness preconditions (spec section 4:
stateless norms, RoPE-only position, direct K/V projection - no MLA).

turbo-quant: executor decode re-encoded the whole cache per step, decaying
old-row norms ~x0.96/step until prompt K rows vanished; now append-only
via a shared append_row. Codebooks were trained at sigma sqrt(2) too
small; one N(0,1) Lloyd-Max table per bit-width scaled by 1/sqrt(d) lifts
4-bit cos 0.9908 -> 0.9954 and removes the d=64/32 fallback cliff.
Non-power-of-two block dims are a typed error, not a mid-prefill panic.
Contract relabelled bounded_KL -> codec_bounded_state in state-policy.md.

PLE: 25 engine-internal per-layer loops skipped the PLE + layer_scalar
tail on Gemma-4-class archs; all now apply it via a shared
apply_ple_and_layer_scalar helper, with engine-vs-legacy parity tests on
the seeded E2B fixture.
Every walk_ffn file now >= 90% line coverage (was: three split files
at 29-82%, selector at 35%, and exact/full_mmap/interleaved at 0%).
The load-bearing additions are the parity tests the 2026-07-30 review
flagged as absent (roadmap item 20):

- serial-vs-parallel parity at >= PARALLEL_DOWN_MIN_HITS asserting
  output via per-feature linearity (sum of two serial half-routes),
  with the parallel path's all-zero activation pinned so the Tier-2
  forward_observed fix flips it loudly.
- gather-vs-serial parity on a constructed feature-major down sidecar
  (first CI-resident correctness check for the gather kernel;
  tolerance derives from the two independent down quantisations).
- walk-vs-dense parity against WeightFfn for the full-K gemv path and
  the previously-untested dense trio (exact, full_mmap, interleaved),
  incl. non-gated bias arms and walk-only fallback.
- all six FeatureSelector variants through joint_gate_knn with
  raw-score and criterion-ranking assertions; norm caches bit-exact
  vs the dequant cache; fallback-counting pins.
- interleaved_q4 batch-branch == C-kernel-branch bit-equality via a
  mock batch backend, incl. the vecmat-decline fallback.
- sparse override arms: zero up/down overrides remove exactly the
  overridden feature's contribution, rest bit-equal.

Fixture support: parameterised q4k test weights (Q4K_TEST_INTER_WIDE
768 to reach the parallel branch), model-gate vindex (enables
walk-vs-dense parity), down/up sidecar attachment helpers. The new
interleaved-vs-dense parity test immediately caught a latent fixture
bug — attach_interleaved_f32_to_test_vindex wrote down untransposed —
fixed; no production code changed.
…core)

The deep-idle park was fake: park_timeout(50us) woke each parked worker
20k times/sec, and the yield bridge ran 128k sched_yield calls per idle
transition. Measured 4.27 CPU-s per 5s of idle (85% of a core, ~836k
involuntary context switches, 11 workers) on the process-lifetime
global() pool. Now: yield bridge cut to 64 iterations, park backstop
50us -> 1s, Drop unparks workers. After: 0.042 CPU-s per 5s idle (0.8%),
51 context switches; wake-after-idle 80us -> 59us; decode at parity on
quiet M3 Max (fixed 35.9/36.3/35.9 vs baseline 35.1/34.9/35.8 tok/s,
26B --cpu -n 50, interleaved pairs).

Also: gate the two production-scale stress repros behind heavy_tests
(default --lib suite 112s -> 3.5s; run via
make larql-compute-test-integration) and set LARQL_SPIN_POOL=0 in CI —
a 2-core runner has no core to spin on; the both_arms override tests
keep both scheduler arms covered regardless.
…nostics

In-flight work across two streams, committed to move the branch:

- larql-models: tensor_keys module (qk_norm / moe_experts /
  mxfp4_dequantised / attn_bias), architecture detect updates across
  deepseek/gemma/gpt2/gpt-oss/granite/olmoe/qwen/starcoder2, gguf
  orient + safetensors loading, mxfp4 quant
- larql-compute: ffn expert_weight tracing (serde_json promoted to a
  lib dependency), MoE reference parity test + generator script
- larql-compute-metal: attention sinks stage
- larql-factory: build exec stage, build tests, test_support,
  card/estimate/recipe/build_id revisions
- larql-cli: moe_locality diagnostics
- docs: k3-funnel spec, ROADMAP updates
…ended

Roadmap item 13 resolved — the audit's claim was inverted. gate_walk
was placed FIRST in the walk's selection chain on 2026-04-04 with the
comment "falls back to gate_knn (HNSW or brute)", but the GateLookup
trait default (None, "override in VectorIndex") was never overridden:
through &dyn GateIndex every walk selection fell to gate_knn, so
enable_hnsw() silently leaked APPROXIMATE, recall-80-95% selection
into walk numerics for four months. All fidelity work (full-K parity
KL=0.0000, FeatureSelector experiments) assumes exact top-K; the
docs' evidence (no HNSW speedup at 10K dense features; wins only for
wide MoE) supports exact-first for the walk.

- VectorIndex::gate_walk delegation shim to the exact gemv, with a
  populated-index delegation test (an empty-index test cannot
  distinguish delegation from the default — how this survived).
- PatchedVindex::gate_walk declines on layers with gate overrides or
  tombstones so the overlay-aware merge stays authoritative (3 pins).
- enable_hnsw() doc maps exactly which paths consult HNSW (gate_knn,
  gate_knn_expert) and which never will, with the leak window noted.
- Pins: gate_walk_ignores_hnsw_toggle;
  walk_ffn_sparse_hot_path_ignores_enable_hnsw (fails pre-shim).

Behavior change only when enable_hnsw() is on AND the gate is
dense-resolvable: walk selection becomes exact, as documented.
Default runs byte-identical; Q4K-only gates and patched layers keep
gate_knn_q4/gate_knn, preserving the wide-MoE HNSW win.
The larql-models and larql-compute coverage gates merge the lib-test and
integration-binary instantiations of each crate, so code exercised only
by in-crate unit tests still reads uncovered from the integration
binary's copy. Three files sat under their floors with green tests:

- models/config.rs + olmoe.rs: add trait-default and OLMoE sweeps both
  in-crate (config::tests, olmoe::tests) and through the public API in
  tests/test_architectures.rs; gate passes at 90.40% total, all files
  at floor.
- compute expert_weight/trace.rs: split open_sink/record_into out of
  the OnceLock/global wrappers so both failure arms are unit-testable,
  and drive the env-resolved sink end-to-end from two one-test
  integration binaries (tests/test_moe_route_trace_{on,off}.rs — one
  test per binary because the sink resolves once per process and
  set_var needs a quiet process). The residual instantiation-union gap
  is debt-baselined at 76 in coverage-policy.json with rationale;
  merged lcov of the same profdata reads 85%+.
- serde_json returns to dev-dependencies: trace.rs hand-writes its
  JSON lines by design and only the tests parse them back.
- cargo fmt debt across 13 files committed unformatted in a75d305
  (walk_ffn, kv engines, gguf orient, vindex patch overlays) — pure
  rustfmt of the committed content, verified against the formatted
  blobs; no semantic change.
…nce boilerplate

§4.7.6 concluded larql's 3.221 bits/token was "the only figure in that table
that is even plausible" and therefore that the reference was the suspect. Both
halves are withdrawn: the comparison was between two different texts.

The first 384 bytes of data/gutenberg/frankenstein.txt are the Project
Gutenberg licence header, not the novel. 3.221 is a boilerplate score. At 2048
bytes larql gives 8.338 against HF bf16's 8.028 — the engines agree to 4%, not
2.5x apart.

Adds the control §4.7.6 never had: on the same 2048 bytes through the same
ExpertWeightFfn tier, Qwen3-30B-A3B scores 1.400 bits/token. A healthy figure
from the same code path exonerates the scorer, so OLMoE's gap is a real bug
(strengthened, not withdrawn) while GPT-OSS's forward is simply unvalidated.
GPT-OSS is demoted to a secondary rung for any forward-pass metric; Qwen3-30B-A3B
becomes the working R1-class reference.

§4.7.8 records finding 5's third recurrence and why the trait default was the
mechanism rather than the symptom. The remaining structural fix — no behavioural
default, plus a lint for fixtures omitting fields the policy branches on — is
carried explicitly rather than bundled here.

Method note, now the third measurement-shaped error in §4.7: a corpus slice is
a fixture. Any bits/char figure must state its byte range, and both sides of a
comparison must use the same one.
…alsified

Poses the MoE redundancy question over the EXPERT axis: reshape the bank to
[E, d_out*d_in], one flattened expert per row, so rank caps at E and the
statistic can return "independent". Stacking to [E*d_out, d_in] instead caps
rank at the ambient d_in, which boxes the observable ratio into roughly [1,2]
and reports "redundant" by construction — the same failure as reading a
near-zero cosine between two flattened weight matrices as orthogonality.

Measures both compression routes, because they fail independently and testing
only one would overclaim:

  cross-expert (shared atoms)  99% energy needs rank 126-127 of 128; at a 5x
                              budget only 21-27% of energy survives against a
                              19.5% flat null
  per-expert (low rank)        99% energy needs rank 715-723 of 768, against a
                              breakeven of 559 — negative compression

Qwen3-30B-A3B, 3 projections x 5 layers. Layer 0 is the most structured;
layers 12+ are near-random, so depth offers no escape either. --random-control
measures the flat null rather than trusting the derivation (19.8% vs 19.5%
analytic).

Not closed: this is linear redundancy in raw weight space, so two experts equal
up to a hidden-unit permutation look independent. Canonicalising before
re-measuring is the one follow-up that could overturn it.

Also names the magic values in moe-locality per the standing rule: block sizes,
per-layer block, shuffle seed and the empty-cell placeholder become consts, and
the script samples layers from the model's actual depth instead of a hardcoded
Qwen3-shaped list.
… dense path

Roadmap item 16 (Tier 2). For a layer with patched slots P:

  y_patched = y_base + sum_{i in P} [a_i_new(x)*d_i_new - a_i_old(x)*d_i_old]

is exact for gated FFNs, dropping override cost from O(N) forced-sparse
to O(|P|) extra dots on the fast dense base. Inserts are pure
additions, tombstones pure subtractions. Retires the
override-forces-sparse cliff whenever preconditions hold.

Exactness by construction, each condition enforced by declining
(None -> the pre-existing sparse/fallback override routes):
- OLD terms from the SAME quantised bytes the dense base consumed
  (unified ffn_row_dot/scaled_add dispatch, never f32 twins), with
  storage-coherence checks so the base ladder and row dispatch can
  never read different bytes (exactly one of Q4K/f32-interleaved, no
  FP4/Q4_0/full-mmap/feature-major diversions).
- Old down rows from the feature-major Q4K sidecar or native f32
  interleaved (the transposed interleaved down is not row-gatherable).
- Dense base via forward_unpatched_whole_layer — the same ladder body
  (steps 4-9) unpatched routing runs, so base numerics are identical.
- Half-covered slots (gate without up row) and wrong-width overrides
  decline rather than subtract a wrong old term.

larql-vindex: OverrideSlot + PatchOverrides::override_slots_at
(exhaustive per-layer slot enumeration incl. tombstone flag; default
None so point-query-only indexes decline the path);
PatchedVindex implementation.

Tests (base_delta_tests.rs): parity vs the sparse override walk for
up/down/gate-override, insert, and tombstone cases; the no-op-edit pin
(override writing back the ORIGINAL row bytes reproduces the unpatched
base output — the same-bytes condition made testable); trace pins for
override:base_delta and every decline route; unpatched-layer
byte-identical pin.
…atch

A worker whose block in dispatch N was empty is not needed by N's completion
barrier, so N can finish and N+1 re-publish the task slot while that worker
sits between its epoch read and its slot read. It then ran N+1's chunks
attributed to N and again on re-observing the epoch - over-counting
'completed', releasing N+1's barrier early, and dropping the closure while
another participant still executed it (use-after-free; SIGSEGV under
coverage instrumentation where the window is widest). The task slot is now
seqlock-guarded and generation-stamped: a participant runs only the dispatch
whose epoch it observed. Pinned by an alternating 1-wide/7-wide exactly-once
stress test.
unlimited-context: window checkpoints read the backend cache at an absolute
index (every window after the first checkpointed the wrong row on Metal);
dispatch prefill now segments the prompt into windows like the CPU path; the
W10-disabled decode-after-close panic re-allocates the window shadow; lost
window shadows are typed errors instead of silent context loss; window_size=0
rejected. boundary-kv: prefill emits a frame per interior chunk boundary
(spec 6.1) via chunked prefill that keeps final state bit-identical;
re-prefill starts a new generation chain. boundary-per-layer: decode failures
no longer destroy the store (dense-walk fallback now reachable); dispatch
cold-tier recompute uses the dequant scratch (panicked on real Q4K);
cold-kv extension is atomic; stale kv_handle cleared on plain prefill.
standard: window honored on the quant path (coarse declined when windowed);
per-layer quant fallback builds a real WalkFfn instead of running the
harness's NullFfn; recorded prefill dispatch mode replaces the
handles.len()==1 heuristic (panicked on 1-layer models). apollo: BOS is a
parameter with structural fallback (never hardcoded 2); injection-layer
misconfiguration is a fail-closed prefill error; deterministic routing
tie-break; store/npy integrity validation. gate telemetry: hard-reject path
computes boundary_fragile instead of stamping false.
The vindexfile/mod.rs coverage floor (72%) failed CI at 71.50%: every
existing build test stopped at an error arm before the base loaded, so
the whole directive loop — INSERT/DELETE/LABELS/EXPOSE, the build
history, and both bake-down branches — was untested. Save a tiny
synthetic base through the real save path and run the loop end-to-end
(plus the no-overrides clone branch). File now measures 96.33%.
b813c01 landed it unformatted; the larql-inference fmt job fails the
whole workflow in 20 seconds before anything compiles.
…re races

`forward_with_activation` returned `(out, activation)` and every backend that
had nothing real to report fabricated the second element as a clone of the
first. `forward_observed` replaces it with `FfnActivations`, which can say
Dense, emit sparse pairs, or say Absent — never invent zeros. The eight stub
impls across larql-kv, larql-server and larql-inference drop their overrides
entirely: the trait default returns `FfnActivations::unobserved()`, which is
what their call sites now assert with `obs.is_absent()`.

Also fixes two instances of the same test-infrastructure bug. `ResidualCapture`
and `StageCapture` each pointed a process-global env var at a per-call tempdir
with no synchronisation, while `cargo test` runs each suite's cases as threads
in one process. Two concurrent captures redirected each other mid-run, so a
case either found its own directory empty or read dumps a sibling had written
*for a different model*. That produced `parity_mistral_7b_prefill` failing
deterministically, `parity_llama2_7b_prefill` failing intermittently with
cos=0.0056 — a cross-model comparison that looked exactly like a catastrophic
kernel regression — and `stage_bisect_mistral_7b` failing while its three
siblings passed. Both helpers now share one lock, since two different helpers
racing corrupts a capture as thoroughly as two calls to the same one. The
existing tests could not catch this: they are single-threaded, and the hazard
only exists under concurrency. The new one spawns 8 callers and fails with the
lock removed (5 of 8 observed a sibling's directory).

The previous code documented the hazard — "racing `cargo test --test-threads=N`
would stomp; tests in this suite run with `--test-threads=1` upstream" — but
nothing enforced it and the default invocation violates it.

Metal parity suite: 4/4, twice. Workspace: 6479 lib tests green, fmt and
clippy clean.
…ed NaN

`full_pipeline_seq1_produces_nonzero` failed about one run in ten of
`test_metal_shaders`, returning an all-NaN hidden state. It passed in
isolation and never reproduced under `--test-threads=1`.

Building a backend compiles the whole shader library from source and creates
several dozen pipeline state objects, all against the shared
`Device::system_default()`. That test binary calls `MetalBackend::new()` once
per test, so it builds 54 backends concurrently; doing so intermittently
yields a backend whose pipelines compute garbage. Serialising construction
fixes it: 0 failures in 85 runs, against a ~10% per-run rate before
(P(fluke) ~ 1e-4).

Two hypotheses were tested and refuted first, so the lock is not a guess at a
symptom. Zeroing every scratch buffer on hand-out did not help, ruling out an
uninitialised-memory read — `bufs.output()` recycles non-zeroed buffers, which
was the obvious suspect. And the failure never appears serially, ruling out
anything input- or dimension-dependent.

The lock is held across the whole function rather than just the library
compile: pipeline creation shares the same device, and narrowing it would be
re-guessing at which half is unsafe. Cost is nil where it matters — production
builds one backend per process, so it is uncontended outside test binaries.

Also splits the assertion that hid this. `v.abs() > 1e-6` is false for NaN, so
an all-NaN result failed with "output should be nonzero", which sent the first
look hunting for a zeroed buffer when every element was NaN. Finiteness and
magnitude are now separate assertions with distinct messages.

Adds `concurrently_constructed_backends_all_compute_correctly`: 8 backends
built on 8 threads, each running the same deterministic matmul, asserting
every result is finite and agrees with its peers. Being a race, it reproduces
probabilistically — a net that catches removal of the lock across repeated CI
runs, not a single-run proof. Its doc comment says so.
Completes roadmap item 15 (the bulk landed in 4e592c6): base_delta
observation suite (post-patch slot activations, tombstone-observes-0,
decline branches — base_delta.rs 20%->95.5% line coverage), dispatch
and sparse observation pins, WeightFfn dense-observation test,
no-shard Absent pins for the sharded backend, roadmap tick.

Split summary now fully landed: forward is allocation-free by
construction (single forward_routed body, Observe::{Skip,Record});
FfnActivations::{Dense,Sparse,Absent} replaces fabricated zeros —
the parallel Q4K path reports real activations (pinned parity test
flipped from all-zeros to bit-equality with serial), L1-cache hits
recompute under observation instead of serving zeros, and
forward_with_activation is deleted. All four crate suites green,
workspace clippy clean. Known debt: remote http/sharded transport
paths remain under-covered pending a mock-server harness.
Roadmap item 17. take_trace() used to record residuals during the
forward and RE-RUN plain gate_knn afterwards — the recorded trace
could be a different feature set than the walk executed (ignoring
selector/pools/cell-router, and racing index mutations), and carried
gate scores, not contributions. Traces now emit from the executed
kernels at execution time, riding the item-15 Observe seam — no
parallel capture channel.

Schema (walk_ffn/trace.rs, chuk-introspect vocabulary, no dep):
TracedFeature { feature, rank, gate_score?, up_score?, activation,
down_row_norm? } per (position, layer, feature);
LayerTraceRecord { layer, position, path, residual_delta_norm,
features } per (position, layer). Dense whole-layer paths emit
summaries with empty features — declined, never faked. down_row_norm
is peeked from the selector's lazy cache only if already built
(pinned: tracing never triggers a norm pass). Target-logit
contribution needs lm_head access — documented follow-up.

take_trace rebuilds WalkTrace from runtime records (hits = EXECUTED
features, historic last-position semantics); take_runtime_trace gives
full fidelity. WalkHit extended additively. Consumers migrated:
vindex gate_knn dispatch + overlay walk, infer_patched (documented as
post-hoc view), lql infer_trace, cli walk_cmd, python PyWalkHit.

Proof test (trace_tests.rs): a DecoyKnnIndex whose gate_knn returns a
disjoint set while a pool route executes — the trace must equal the
executed pool route with real activations/ranks; the old take_trace
structurally reports the decoy. Suites green across inference (1385),
vindex (1202), compute, server, lql, cli; workspace clippy clean;
trace.rs 100% line coverage.
Also removes an untracked orphan: crates/larql-compute/examples/
bench_mxfp4_dequant.rs was a first draft written before the crate split
mattered. larql-compute deliberately cannot see larql-compute-metal
(ADR-019: GPU backends are sibling crates), so a bench comparing CPU
against Metal has to live in larql-compute-metal, which is where the
surviving copy is. Leaving both would have left a stale duplicate that
could not measure what its own docs claimed.
…tests

Roadmap item 24 — the programme's final item, done in priority order
with the file-split remainder documented rather than rushed.

- Generic-engine rule: English word lists, pattern classes, and the
  Wikidata vocabulary are OUT of engine code into data/
  (entity_patterns.json, stop_words.json, wikidata_categories.json)
  behind one resolver (clustering/data_files.rs: LARQL_DATA_DIR ->
  workspace data/, never cwd; explicit-path loaders; loud minimal
  fallbacks). The two new vocabulary files are force-added past the
  data/ gitignore: unlike the script-fetched wikidata file they have
  no generator — they ARE the extracted data. The identical
  cwd-probe in pair_matching/database.rs fixed with the same
  resolver. Thresholds named (MIN_CATEGORY_SIMILARITY,
  PATTERN_MATCH_FRACTION — doc/code disagreement resolved in favour
  of code and pinned).
- Activation dispatch: 27 live copies (audit counted 8) of the
  GeluTanh-vs-SiLU match unified on
  Activation::uses_gelu_tanh_gate_up() in larql-models — exhaustive,
  wildcard-free, so a new activation variant is a compile error, not
  a silent SiLU. Two drifted copies (weight.rs, cached.rs matched
  GeluTanh only, dropping exact Gelu to SiLU) fixed by the dedupe.
- 16384 -> 10240: ffn-cache.md and capture.rs corrected against
  gemma3.rs; ffn-cache's stale top_k*2 gate claim fixed to the
  FULL_K_DENSITY 4/5 truth.
- FFN_GATE/FFN_UP constants added beside FFN_DOWN (vindex pub +
  compute mirror with compile-time equality pins); all bare 0/1/2
  component indices unified.
- First-ever tests for the three untested load-bearing files: hnsw.rs
  97.3% (recall@10=0.97 vs brute), mutate/mod.rs 96.0%, write_f32.rs
  92.6%. NEW FINDING pinned honestly: HNSW level-0 fragments beyond
  ~64 nodes (recall@10=0.16 at n=200) — naive add_connection eviction
  orphans nodes; recorded as a standing follow-up since gate-KNN
  consumers at 10K+ features may be silently degraded.
- Also records the down_meta.rs -> down_meta/ deletion the item-22
  commit missed.

Deferred (documented in the tick): the one-concept file splits for
download/mod.rs (1329), overlay.rs (1071), convert.rs (653).

Programme status: ALL 24 ITEMS CLOSED. Standing follow-ups listed in
the audit header: try_apply_patch migration, remote transport
coverage, logit-contribution traces, thresholds -> WalkFfnConfig,
HNSW fragmentation, file-split remainder.
`q4k_grouped_experts` declared `Q4KG_ROWS_PER_TG`, a name `q4kf_qkv_proj`
already owns. Every shader is concatenated into one Metal program source,
so the second declaration is a redefinition and the entire program fails
to compile — not just the new kernel.

That took 98 of 216 lib tests with it, every one of them reporting the
generic "Metal device available on test host" panic rather than the
shader error, so the cause sat two layers below the symptom.

Renamed the file's constants to `Q4KGE_`, matching the `Q6KG_` prefix the
sibling `q6k_grouped_experts` already uses. A scan across all shaders
confirms this was the only colliding constant.
CI runs `cargo fmt -p larql-compute-metal -- --check` before clippy and
tests, so the format failure aborted the job and hid everything behind
it. Eight files were unformatted; clippy then reported five errors under
-D warnings:

  - manual `%` divisibility in grouped_experts.rs (x2) and mxfp4.rs
  - needless_range_loop zeroing a scale row -> `fill(0)`
  - redundant closure around `quantize_q4_k`
  - two unused imports in bench_grouped_experts

`mxfp4_matmul_small_block` keeps its eight arguments behind an explicit
allow: two weight planes, the activation, three shape scalars and the
layout are the kernel's real signature, and bundling them into a struct
would hide the shape contract the guards check one by one.
Three tests added in ea18d1e asserted that `data/wikidata_categories.json`
and `data/wikidata_triples.json` exist "in a checkout". They do not —
.gitignore excludes `data/` — so they passed only on a machine that
happened to carry the generated artifacts, and failed in CI.

Two different fixes, because the two files are not the same kind of thing:

  - wikidata_categories.json (16 KB) is a vocabulary, the same class as
    entity_patterns.json and stop_words.json, which ea18d1e already
    force-added past the ignore. Tracked now, for the same reason.

  - the reference databases are 2.2 MB of generated triples and do not
    belong in the tree. `load_reference_databases` grows a resolver seam
    mirroring the existing `resolve_data_file_with_env` pattern, so the
    resolver -> loader -> struct wiring is covered from tempdir fixtures,
    including that each filename reaches its own loader. Absence is now
    covered explicitly too, which is the path CI actually takes.

The cwd-independence guard moves to data_files.rs and asserts the
workspace dir is absolute. That was the real 2026-07-30 finding, and it
holds whether or not any data file is present.
Five new pure modules plus the reporting to surface them.

Format floor, closed in two halves:

  - `serving_format` settles the FIXED-width question by counting rather
    than searching. An MXFP4 group reconstructs at most 15 distinct values
    (16 LUT entries less the +0/-0 collapse), so ceil(log2 15) = 4 payload
    bits — exactly what MXFP4 already spends. `formats` reports it and
    takes neither a checkpoint nor the network, so `run` dispatches it
    before any geometry fetch.

  - `symbol_census` prices the remaining loophole, a VARIABLE-rate exact
    code, against a measured distribution read by range request. It
    carries its own trap guard: plug-in entropy over a B-weight tile is
    biased low by about (K-1)/(2*B*ln2) however uniform the source is, and
    that bias scales as exactly 1/B. Real local concentration would not,
    so the diagnostic is the scaling, not the gap.

`scenario` exists because of a specific misquote: the frontier's "8 tok/s
needs ~3.8 payload bits" row was read beside the composed 5.77 tok/s
ceiling as though they answered the same question. They do not — frontier
is a requirement on the dense path at eta 1.00 with routed bytes credited;
ceilings is a ledger at measured per-class eta 0.64-0.89. The banner now
prints the premises, and `exact_floor_tok_s` derives the bar rather than
quoting a literal that could go stale.

Two KDA reads, both blockers rather than curiosities:

  - `kda_a_log` — whether A_log parameterises decay per head or per
    channel. Two self-consistent readings exist, each breaking exactly one
    tensor, and the wrong one corrupts the recurrent state in all 69 KDA
    layers while still producing plausible text.

  - `kda_graph` — which projections share an input, since a folded
    rotation is only cheap if several large matrices read the same tensor.
    The premise that all five share one hidden state is wrong by one, in
    the expensive direction.

`fetch` gains `packed_expert_tensors`; `classes` gains GROUPED_ROUTED_ETA
at the 0.89 K3a measured.
…outes?

The 2026-05-29 scissors measured gate-KNN sparse at ~3x slower than dense
because selection pays a full O(N*d) gate projection. Every candidate
router — HNSW/ANN, LSH, static pools — is an attempt to shrink that
selection term, so testing them one at a time prices the levers before
knowing whether any lever can win.

R4 sets the bytes each lever targets to zero and asks the only question
that gates the thread: with perfect routes supplied for free, is sparse
execution cheaper than dense at all? Three arms — `dense`, `exact` (full
production gate chain), `oracle` (precomputed top-K, zero timed search).
`oracle` vs `dense` is the decision; `exact` vs `oracle` bounds the most
any router could ever recover.

Provisional battery output included: 34 layers, 10240 features/layer,
decode shape, warm = mean of 30 after 5.
The key is `(data.as_ptr(), data.len())`, which is sound only while the
slice points into an allocation that outlives the cache and never changes
— mmap'd weight data, the case it was built for. Any caller passing a
temporary is a silent correctness bug: the allocator may hand the same
address to a later, different temporary of equal length, and the cache
returns the earlier buffer.

Found from the K2 tournament, which built two 64-byte expert-offset tables
as temporaries. The second `get_bytes` returned the first table's buffer,
so every arm read slots 1..15 at the wrong offsets. Slot 0's offset is 0
under both tables, so a slot-0 oracle passed while the outputs were wrong;
the lesson worth keeping is to probe a slot whose offset is non-zero, and
to keep a full cross-arm comparison beside any narrow oracle.

A content compare on every hit is not affordable in release — it is a full
pass over the weights, as expensive as the matvec. So this states the
contract in the doc, verifies it under debug_assert where the cost does
not matter, and adds `uncached_bytes` for data whose address says nothing
about its content.
…not the skeleton

Seven crossed arms at the real K3 expert shape [3584, 3072] x 16 grouped
experts, batched 8/cmdbuf, cold. Crossed so no comparison confounds two
changes: B/C/D/G share a byte-identical artifact, so those deltas are pure
decode; only A changes the bytes, so B-A is the layout effect. A three-arm
tournament without B would have been uninterpretable.

                                    ms      eta
  A split / 16-LUT, 4.25 bpw    0.3721    0.685
  B inter / 16-LUT              0.3782    0.644
  C inter / 256-entry pair-LUT  0.4158    0.586
  D inter / 8-entry mag+sign    0.3368    0.724
  G inter / table-free bit math 0.5374    0.454
  E probe / trivial affine      0.2627    0.928
  F probe / weights only, no X  0.2575    0.947

D wins. The ordering is monotone in table pressure — 8 beats 16 beats 256
— and constructing the f32 from the e2m1 bit fields with no table at all
is the worst of the lot. Interleaving is a small consistent LOSS despite
reading 4.4% fewer bytes, so at this shape the kernel is not bandwidth-
bound and the byte saving cannot pay for itself.

The two probes are why this can stop rather than continue. E keeps every
byte read and every X read but swaps the non-uniform fp4 grid for an
affine map; F drops the X gather entirely. That decomposes D into
skeleton 76%, fp4 decode 22%, X gather 2%. The skeleton streams weights at
eta 0.947-0.965, so there is no deficiency to transplant from the Q6_K
kernel and the whole distance from 0.89 is the value set.

Do not read eta across containers. Q6_K reads 1.62x more bytes per weight,
so it becomes bandwidth-bound and reports a high eta more easily. The
comparable quantity is bpw/eta: Q6_K 7.374 against MXFP4-D 5.611, so D is
1.31x faster on the routed class *despite* the lower eta.

Probes are excluded from the correctness gate by an explicit `checked`
flag, since they deliberately compute something else. Every scoring arm
reproduces a CPU oracle to 6.2e-8. The packer emits both layouts from one
quantisation so the arms encode identical values; note the superblock
spread clamp must move exponents UP, as clamping down pushes amax/scale
past the fp4 range and silently saturates.

Benchmarked layout is 4.0625 bpw — fixed 1-bit delta, the 97.12% common
path. The whole-checkpoint exact format is 4.06731 bpw; the difference is
the 2-bit delta for the 2.88% wide tail plus a mode bit, not any global
scale table.
The coverage policy gate had never run on this branch — the format check
aborted the metal job before it, so three files drifted below the 90%
floor while the K3a grouped-expert and MXFP4 work landed:

  diag/kernel_profile.rs         74.69%
  trait_impl/grouped_experts.rs  87.33%
  trait_impl/mxfp4.rs            88.26%
  TOTAL                          95.38% (min 96%)

What was uncovered was not the kernels but everything around them: both
error `Display` impls in full, the `x`-too-short guards on all three
entry points, the entire `q4k_grouped_experts` guard prologue, and two
whole profiler drivers.

  - `profile_shape_census` and `profile_grouped_experts` get smoke tests
    at minimum params, matching the existing `profile_all` pattern and
    skipping on hosts without a Metal device. The grouped-expert driver
    is the measurement the eta 0.64 -> 0.89 claim rests on, so it should
    not only ever run by hand from the bench example.

  - `measure_batched` gets a pure timing test asserting the result is per
    LAYER and that warmup passes are excluded. Those are the two ways it
    can be wrong, and getting them wrong is what undercounted q6k_matvec
    by 4x in 2026-04-28.

  - the q4k path is checked against the same four shape faults as q6k,
    since it carries its own prologue and a fault caught on the q6k path
    proves nothing about it.

The PerSlot guard test pins the failure mode explicitly: an x sized for
Shared is a legal buffer, so without the guard PerSlot reads the next
slot's activation off the end and returns a plausible wrong answer.
…ddress

`profile_all` and `diag_shape_census` build their cold-rotate weights as
temporaries inside a `map` and hand each to `get_bytes`. That cache keys on
`(ptr, len)`, so every iteration's `Vec` — same size, freed before the next
allocation — landed on one address and returned the first buffer. Probed
directly: 8 handles, 1 distinct address.

The consequence is a measurement bias, not a wrong answer: each "cold" call
re-read a single buffer instead of cycling 172 MB, so anything small enough
to sit partly in L2 reported inflated bandwidth. Re-running the shape census
against a genuine rotation moves gemma down q6k 0.85 -> 0.80 and K3 latent-up
0.87 -> 0.82, while the large shapes barely move (K3 KDA attention q6k stays
0.89) — exactly the shape-dependence you would expect if the old numbers were
partially cache-resident. Composed, the ceiling moves 3.70 -> 3.68, so the
ledger is robust to it even though the harness was not.

The page-aligned path made it worse than a stale read: `get_bytes` hands Metal
the caller's pointer with no copy, so the buffer outlived the `Vec` it aliased.

`uncached_bytes` copies, so the temporary may die and each rotation slot is a
distinct allocation. Regression test asserts 8 slots stay 8 addresses.

Per-class eta constants in `k3_ledger::classes` are NOT updated here — one
re-run is a data point, not a re-bank.
`diag/mxfp4_layout.rs` landed at 41.03% line coverage, below the 90%
floor, and dragged TOTAL to 95.54% against a 96% minimum. The helpers
around it were tested; `race` — the ~200-line driver that is the whole
point of the module — was not run at all.

Adds a smoke test at minimum params, matching the `profile_grouped_experts`
pattern and skipping without a Metal device. It asserts the arms produce
finite positive rates, and that every *checked* arm still agrees with
arm A — `first_bad` is None. Ceiling probes are excluded from the
agreement check because they deliberately compute something other than
the matvec.

Shape is chosen so the row-remainder path runs: N=260 is not a multiple
of the tile.

This is the test that would catch an arm silently drifting out of
agreement. Until now nothing exercised the arms against each other
except running the bench example by hand.
…-point oracle

R7 comes out of the K2 tournament. At K3's expert shape Q6_K reaches eta 0.89
and MXFP4 arm D only 0.724, yet D is 1.31x faster — a denser container is
easier to make bandwidth-bound, so it reports a flattering eta for the same
wall-clock work. Ceiling probes decomposed arm D as skeleton 76% / fp4 decode
22% / X gather 2%, with the skeleton streaming at 0.947-0.965, so the low eta
was the price of the container and there was no kernel defect to chase. Kept
separate from R6 deliberately: R6 is logical-vs-physical reuse, R7 is that the
denominator's representation changed. Sharing an identifier would let one
retire the other.

R8 generalises the slot-0 miss. A cache-aliasing bug made every tournament arm
read slots 1-15 from the wrong offset table, and the oracle passed because it
only checked slot 0 — where every offset convention yields zero. The same shape
has now appeared four times: CN-10's rank-1 first token, shared-vs-per-slot
input diverging only from slot 1, and a square A_log fixture where per-head and
per-channel coincide on the diagonal. The rule is to choose the probe point
after enumerating the plausible wrong implementations, not before.
…not support

Support and mass are different questions and the ladder has conflated them
once already. Support asks which symbols a session touches; mass asks how
often. A session can touch the whole bank and still take most of its events
from a handful of symbols, and only the second quantity prices a cache.

`selection_trace` keeps "symbol" deliberately unnamed: an MoE expert id at
DEC-8.4, an FFN feature-row id at DEC-8.1. One instrument therefore grades the
resident-expert bet and the compact-subexpert bet without learning which, and
without knowing anything about K3. Sessions stay separable rather than pooled
at construction, because the adaptive estimators condition on them.

`freqmass` scores every arm on the same held-out events, varying only the key
used to rank symbols for residency:
`key = lambda*pooled_prior(leave-one-out) + (1-lambda)*session_fit`. lambda=1
is the static slice, lambda=0 the causal adaptive cache, the interior a
shrinkage cache, plus an unrealisable oracle bound.

The null arm — eval half redrawn from the pooled marginal, session identity
destroyed — is load-bearing rather than decoration. A short fit window
concentrates by counting noise alone, so the locality signal is oracle minus
null; oracle alone overstates it. Scoring adaptation without a null is exactly
how a finite-sample artifact becomes a 3x lever.

Exposed as `larql k3-ledger freq-mass`, dispatched before geometry fetch since
it reads a local capture pool and must keep working on non-K3 traces.
`has_metal_kernel` was too coarse and produced a claim that was simply wrong.
MXFP4 *does* have Metal kernels — `mxfp4_matvec` (K1) and the four
`mxfp4_grouped_experts` tournament arms (K2) — but it is absent from
`QuantMatVec`'s format dispatch, so nothing can route a forward pass through
them. "A kernel exists" and "this container can serve" are different facts and
conflating them let the second be asserted from the first.

`KernelMaturity::{None, Standalone, Grouped, Dispatched, Production}` is
ordered so `is_servable()` is `>= Dispatched`, which is what the filter in
"cheapest exact container" actually needs. MXFP4 is Grouped, Q4_K and Q6_K are
Production, Q8_0 Dispatched, Q5_K None. The corrected statement — Q6_K is the
cheapest exact container that can SERVE today — is now a test rather than
prose, and a second test pins that MXFP4 has kernels, is cheaper, and still
cannot serve.

This also sharpens why Q5_K is dominated: not "it needs a kernel too", but it
has no kernel at all while MXFP4 already has two rungs built, so Q5_K costs
strictly more work AND ships more bytes.

The maturity level is what keeps the 4.15 tok/s routed-MXFP4 projection honest
when it travels — it is a kernel measurement, not a serving result.
Re-banks the per-class etas against three genuine cold-rotating repeats and,
more importantly, stops banking them as scalars.

The cold-rotate bug removed a false precision as well as a bias. Once the
rotation genuinely rotated, the small shapes moved 10-12% across repeats while
attention moved 2%. The composed total is stable at 3.61-3.68 because attention
is both the largest term and the steady one — but every PER-CLASS decision
reads the noisy inputs directly. Which class to compress, the frontier's target
row, the serving-format choice: a scalar constant lets a 10% measurement swing
on a small class silently select a bit-width. Aggregate robustness does not
license per-class precision.

So `MeasuredEfficiency` carries central, observed low/high, repeat count and
regime, and `compose_at` propagates the ends so `ceilings` prints a band beside
the point. Classes whose range exceeds 5% print [NOT DECISION-GRADE], which is
currently the down class (0.73-0.82) and the ungrouped expert shape
(0.63-0.70).

Low/high are the observed extremes, deliberately NOT summarised as +/- x. Three
repeats cannot distinguish gaussian noise from two modes — page alignment,
thermal state, allocator luck — and a symmetric interval would claim they can.
If it turns out bimodal the honest bank is the pessimistic mode, not the mean.

The correction is not uniformly downward: the ungrouped expert shape came back
0.63-0.70 against a banked 0.64, so the old numbers were not simply flattering.
Down 0.87 -> 0.78 and gate/up 0.87 -> 0.83 are the real movers.
…thholds

Three presentation-level R0 hazards, all of which survive correct arithmetic.

Scenario rows quoted the BASELINE's composition band beside their own central
value — a band that did not even contain the number it sat next to. Each
scenario now composes its own low/high through `compose_at`.

`ceilings` at a bit width other than the one the etas were measured under
(Q6_K, 6.5625 bpw) is reusing eta across a container change, which R7 forbids
as a performance claim. That row is the biggest on the board and therefore the
likeliest to escape into a document unlabelled, so it now prints DENSITY-ONLY
UPPER BOUND and names the measured penalty — MXFP4 arm D at 0.724 against
Q6_K's 0.89 on the routed class — that has to be applied before quoting it as
reachable.

`[NOT DECISION-GRADE]` now refuses rather than warns. The R4 lever ordering is
what selects the programme, so when a contributing class carries more than 5%
observed spread the ranking is withheld entirely and the offending classes are
named with the command that would settle them. A warning beside a plausible
float gets read past; a missing ranking does not. Precedent is the 8.8 bench
exiting rather than feeding the frontier a number it could not stand behind.

`Regime::Definitional` exempts the unquantised f32 row: it is a modelling
constant with no variance, so it cannot swing and cannot select a bit-width.
Without it the refusal fired on a class that has nothing to be uncertain about.

`diag_eta_repeats` is the promotion harness — it calls `profile_shape_census`
verbatim so samples are drawn from the identical protocol the bank came from,
and reports mean, standard error, sorted samples, a histogram and a largest-gap
ratio. It deliberately does not conclude modality: three points cannot separate
gaussian noise from two modes, and if bimodal the honest bank is the pessimistic
mode rather than a mean that describes neither.
@metavacua

Copy link
Copy Markdown
Contributor

You might want to put concurrency controls on the CI so that it isn't stuck running on the previous commits to the PR.

…ated campaign

Two corrections, one of them to the criterion I added a commit ago.

The decision-grade gate used observed spread, `(max-min)/central <= 5%`. That is
backwards: `max - min` is non-decreasing in sample count, so the gate rewards
collecting less data. Measured directly — attention showed 2% spread over 3
repeats and 5.7% over 7 while its standard error FELL. The gate is now
`n >= 5 && relative SE <= 1%`, which tightens with evidence. Observed extremes
are still displayed, because they are the honest thing to show; they are just
not the thing to decide on.

The 16-run promotion campaign then found something worse than noise. Nine runs
were unusable: the machine degraded under back-to-back load and the attention
control fell 0.89 -> 0.06, a factor of fifteen. Every cell moved together, which
is the signature of run-level state rather than per-shape variance. Averaging
all sixteen would have banked attention at 0.52 and the down class at 0.53 —
wrong by a factor, while presenting as more data. So runs are not exchangeable
draws on this hardware, the control gate is the load-bearing part of the harness
and the repeat count is not.

Banked from the 7 control-passing runs:

  attention   0.876 +/-0.0075 (0.9% rel) [0.85-0.90]  DECISION-GRADE
  down        0.784 +/-0.0057 (0.7% rel) [0.76-0.80]  DECISION-GRADE  <- promoted
  gate/up     0.799 +/-0.0177 (2.2% rel) [0.70-0.84]  not decision-grade
  routed exp  0.614 +/-0.0145 (2.4% rel) [0.54-0.67]  not decision-grade

So one of the two target classes promoted and one did not, and gate/up demoted
on better evidence. The composed exact-Q6_K baseline moves 3.18 -> 3.02
(2.75-3.21). The R4 ordering stays withheld until gate/up and the routed expert
shape are settled — on a cooler machine, not a longer campaign.
The previous commit edited this file's header and dropped the `//!` on one
line, so the crate's examples stopped compiling.

It reached a push because the build check was chained with `;` rather than
`&&`, so its failure did not block the commit that followed. A verification
step has to gate the commit, not merely precede it.
Records what `larql k3-ledger freq-mass` measured on the banked DEC-0
routed pool (registry: dec8-10-frequency-mass-coverage), and documents
the k3-ledger verb, which had no CLI reference at all.

Two standing rules, both earned by errors this measurement caught:

R9 — a null is not a prediction. The uniform-routing union law was used
as a forecast and is wrong in both directions: it overstates per-session
support 3.4x (38.1 of 128 measured against 128.0 predicted) and
understates top-C coverage ~6.8x. DEC-8.6's "~1 of 16 hit/layer under
uniform routing" inherited the second. The sign of that error travels to
K3 even though R2 forbids the magnitude.

R10 — a caveat has a sign, and the sign is per-statistic. The same
64-prompt domain mixture is anti-conservative for the coverage curve and
conservative for the unobserved count, so stating it once per capture
applies it backwards to half the outputs. Includes the support-size
confound: corr(unobserved, top-8) reads +0.861 but the shape-only forms
read +0.543 / -0.501 / +0.659.

Findings, all 8-of-128 / 6.25%-activation (R2 blocks transfer to K3):
a 6.25% resident set covers 42% of routing events and 90% needs 35% of
the bank; session-adaptive population buys 1.03x on the miss stream at
that operating point against a 1.41x oracle bound, so the arm is closed
pending a domain-clustered K3 trace; 16.1% of layer-expert pairs went
unobserved with per-layer spread 3-68 of 128. Static per-layer structure
varies enormously while per-layer adaptation does not — different axes,
recorded so "no layer class wins" is not quoted against the first.

README gains the three programme docs (dec-funnel, k3-funnel, quant-obs),
which the documentation index had never listed.
The control-gated campaign measured `[6144, 7168]` directly under Q6_K, but the
provenance string still cited `q4k_ffn_gate_up_8sg 10240x2560, Q4K, Gemma
shape` and `is_provisional` still flagged it as borrowed. Both described the old
figure while the number beside them came from somewhere else — the exact R0
failure the provenance field exists to prevent, and worse than no provenance
because it reads as verified.

Nothing is provisional now. gate/up carries its own measurement and fails on
precision instead (2.2% relative SE), which `is_decision_grade` already reports,
so the two epistemic states are no longer conflated: "measured somewhere else"
and "measured here but too noisy" are different problems with different fixes.

`Regime::Borrowed` goes with it — no class borrows any more, and an enum variant
that describes nothing in the bank is a lie waiting to be reused.

Also displays the observed spread beside the SE, so the report shows both the
quantity that decides (SE) and the quantity that is honest about the tail
(extremes), and routes central composition through `Bound::Central` with a
debug assertion that it agrees with `compose`.
…f-selection

The band was a component-extrema envelope: every class's independent minimum
combined into one low bound, every maximum into one high. That describes a run
that never happened, and because the campaign's degradation was common-mode it
is 1.18x wider than anything observed — 2.75-3.21 against a real 2.79-3.18.

`compose_observed` now composes the whole ledger once per accepted run with the
pairing preserved, and reports min/max over those seven compositions. Scenario
rows hold their `eta_override` across all runs, since a scenario asserts an
efficiency rather than having measured one.

Attention's 0.876 carries a bias note it did not have. It is also the control
the campaign gates on, so its own low tail is truncated and the estimate is
biased upward; the other three classes are gated on a variable external to them
and carry no such bias.

The obvious fix — a sentinel outside the composed ledger — was tested and is
worse. `gemma down` (21 MB) stayed at 0.74-0.77 through two runs where
attention (72 MB) had already collapsed to 0.33 and 0.26, so it would have
admitted them. Degradation here is size-dependent, which means a sentinel must
have a working set at least as large as the class it protects. The K2
weights-only probe at 89.5 MB is the candidate; `gemma down` is not.

`scripts/precommit_check.sh` makes the verification a gate rather than a step
that merely precedes the commit. `set -euo pipefail`, per-package fmt so an
unrelated package's in-flight work cannot make the gate unusable, build over
--all-targets so examples are covered, tests, and `git diff --check`.
…-01)

Records two closed rungs and one corrected measurement, with seven follow-ups.
Registry: dec8-11 (K2 layout tournament), dec8-12 (efficiency re-bank).

The exact-format search is finished. K3's experts are MXFP4, so a group
reconstructs at most 15 distinct values and 4 payload bits is the floor by
counting rather than search — MXFP4 already spends exactly it. The doubled
alphabet is not an arithmetic progression, so the smallest affine grid holding
it needs 25 levels: Q4_K can never be exact, Q5_K can but is dominated, and
Q6_K is the cheapest exact container that can actually serve.

MXFP4's low kernel efficiency turned out to be the container rather than a
defect: ceiling probes put the skeleton at 0.95 of attainable bandwidth, so
there is nothing to transplant, and the expert-side kernel line closes at
single-token width.

The numbers moved down twice, both times because a measurement got honest and
never because anything got slower. The table separates what is measured (3.02
controlled healthy-regime) from what is projected (3.65 grouped, 4.15
routed-MXFP4 at maturity Grouped), from what is an upper bound R7 forbids
quoting as reachable (5.49 density-only), from what has never been measured at
all (sustained laptop throughput). That last row is why the follow-ups lead
with the sustained end-to-end run rather than more kernel work.

Item 6 files a flaky prefill test the new commit gate surfaced — all-NaN
output at roughly 3-5%. It is recorded rather than resolved because the bisect
was underpowered and said so: 3 failures in 88 runs, landing on non-adjacent
commits with clean 16-run windows between them, where P(0 in 16) at an 8% rate
is 0.26. Reporting a commit as clean on that evidence would have been the
weaker claim.
…ge loop

clippy's needless_range_loop flags `for i in 0..TARGETS.len() { samples[i]
.push(...) }` under `-D warnings`, which is exactly what larql-compute-metal's
CI clippy gate runs — blocking the workflow on every push regardless of what
else changed. `scripts/precommit_check.sh` checks fmt/build/test but not
clippy, so this slipped past the gate that exists for this file's history of
breaking CI.
… gate

Drift alone (mean of the last quarter of repeats against the first) is
porous: quartile means can agree while a single repeat spikes 4x — exactly
the cell that must not be believed. `sentinel_spread` adds IQR/median
dispersion alongside drift; a cell now reads CELL DISTURBED if either drift
or spread crosses its limit, not drift alone.

Captures two AC=true paired validation runs under bench/aim-validation: the
pre-change baseline (no spread column) and the post-change run showing the
new gate.
…, not the row population

R4 refuted sparsifying the FFN row population: with routing supplied free,
the sparse kernel still captured only 23-40% of its row-count reduction.
This probes the other axis of the factorisation — the shared `expert_input`
vector every routed expert reads. Masking hidden channel `j` removes column
`j` from every active expert's gate AND up matrix in one decision, versus one
row bought in one expert, which is the leverage difference R4 didn't have.

Applied after routing on purpose: `router_in` is computed from the unmasked
vector, so the trained router still selects the same experts with the same
weights — only what they're fed shrinks. Zeroing (not mean-ablation) is the
faithful semantics, since masking a channel is exactly not reading that
weight column.

Ceiling computed before believing any quality number: input-side masking
scales two-thirds of expert bytes (gate+up) and leaves `down` fixed, capping
end-to-end speedup at 1.448x even at zero retention; `both_sides` also masks
the aggregated expert output so `down` shrinks too, reaching 1.30x at r=0.5.

Opt-in via `LARQL_MOE_LATENT_RETENTION`; unset or >=1 is a no-op and output
stays byte-identical. Wired into both `cpu_moe_forward` and
`ExpertWeightFfn`. Ships channel-block grouping + permutation support (for
testing whether an offline weight permutation could make masking realisable
in an output-major layout) and a per-layer channel-survival dump feeding an
offline co-activation-ordering follow-up. `larql-cli` flushes that dump at
exit.
Two diagnoses for the sparse-FFN thesis, and the rule they share.

R4 zero-out (dec8-13). Supplying perfect routes for FREE on the production
kernel, the sparse path still runs at 0.837x dense at the accuracy-viable
K=4096 and wins only at K=2048, which fails top-1. No overlap between the
speed-viable and accuracy-viable regions. The binding term is the kernel:
with routing free it captures only 23-40% of its row-count reduction, and
the capture fraction RISES as fewer rows are dropped (23/34/40% against
ceilings 5.00/2.50/1.67x) - fixed per-row overhead, not row choice. Tying
dense needs ~18.6% execution improvement with routing already free, so a
cheaper approximation of gate-top-K was never the missing piece.

Scope kept narrow on purpose: this refutes a COMPUTE-side claim and does
not close dec8-1, which is read-side with a different denominator and a
milder retention target. What transfers is the mechanism kill.

Latent axis (dec8-14). Masking the shared expert input instead of the row
population, where one channel decision removes a column from every active
expert's gate and up. Signal is real - magnitude vs random separates
9.7% vs 555% bits/token at 50% retention - but the headline was an
artifact of larql's known-divergent OLMoE forward, which called r=0.5 free
where the HF reference says +9.66%. At the <=0.5% shannon gate the viable
retention is r~0.875, ideal ceiling 1.040x. Static channel sets refuted
per-layer; blocked latent sparsity refuted per-layer against exact induced
loss and a random-permutation control that turned an apparent 40% win into
a null. Surviving: dynamic per-channel via an input-major packed kernel.

R11: a structural reduction is a claim about a KERNEL, not about a matrix -
price it on the kernel that will actually run it. Two instances a day apart
(dec8-8's zero weight reuse, R4's 23-40% capture). Corollary: realisability
is a property of layout, not of the mask, since a per-channel mask saves
nothing in output-major storage but is contiguous in input-major, and
channel order is free under an exact permutation.

Numbered 11 because R7-R10 landed since this section was last read.
Both decisive latent-axis results came from these two scripts, which lived
only in a scratchpad. Without them the numbers in
docs/diagnoses/moe-latent-axis-sparsity.md are not reproducible.

moe_latent_reference_olmoe.py - repeats the decisive points through
transformers' own OLMoE forward. This is the gate that caught the headline
artifact: larql's own forward reported 50% retention as free, the reference
says +9.66% bits/token. Patch point mirrors the Rust hook exactly - routing
on the unmasked input, then mask, then experts - so the trained router still
selects the same experts with the same weights.

moe_latent_block_partition.py - per-layer channel partitions optimised
against the exact induced loss L_t = sum_kept(16-s) + sum_dropped(s), which
prices within-block disagreement AND inter-block competition under the fixed
budget; pairwise disagreement only seeds it. Calibration and held-out are
disjoint contiguous spans, partitions frozen on calibration. Carries the
random-permutation control that turned an apparent 40% win into a null, and
the held-out L_t diagnostic separating overfitting from objective mismatch.
Asserts the block budget per token rather than checking after, and persists
masks/partitions/per-layer L_t so further analysis needs no forwards.
@chrishayuk
chrishayuk merged commit e057239 into main Aug 1, 2026
34 of 47 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants