v0.2.5 — the arena gets a compute consumer
The NVMe tier had a storage half and no compute half. This closes it.
arena_experts — arena → kernel
ArenaExpertSource slices a baked arena row by the bake's own recorded segment geometry and returns the fused [E, N, K//2] blocks and [E, N, K//32] e8m0 scales that gemm_mxfp4_grouped already consumes. moe_layer_forward() runs gate → GLU → down straight off it.
Those shapes are not a coincidence: a DeepSeek-V3-lineage MXFP4 release ships each expert as exactly weight_packed [N, K//2] + weight_scale [N, K//32], which is the kernel's input contract. So the bytes travel disk → arena → GEMM with no dequantize round trip and no requantization — the bytes multiplied are the bytes shipped, which is what makes per-tensor hash provenance mean anything at the point of compute.
Routing, attention and the residual stay with the caller. This is the expert block, not a model.
moonshot_gather is name-scheme pluggable (#16)
Released Kimi K3 renamed three parts at once — container mlp.experts → block_sparse_moe.experts, projections gate/up/down_proj → w1/w3/w2, and weight → weight_packed + weight_scale. Under the K2 default a real K3 index discovered zero experts, silently, because a dense layer legitimately reports 0 too. detect_scheme() now identifies the family or returns None rather than gathering nothing.
Validated against the real released checkpoint: the optional gate ran with K3_SNAPSHOT pointed at moonshotai/Kimi-K3's own 497,220-entry index.
Fix: build_group_tiles mutated its sizes argument
enumerate over a tensor yields 0-dim views, so left = m; left -= take subtracted in place and zeroed the caller's array. Every prior caller passed a list or called once, so it never surfaced; the first to run two GEMMs off one sizes tensor — gate/up/down in a single layer — got [0, 0] on the second. Affects the NF4 and MXFP4 grouped paths alike. Host-side tile indices only, so no computed value moves and no confirmatory result is disturbed.
Verification
19/19 green on an RTX A5000, no skips, including both end-to-end gates — arena-fed GEMM bit-identical to memory-fed, and the full MoE layer forward. Every byte-identity gate is paired with a control that must fail (flip one arena byte, and it is detected).