fix(#1549): the LTX-2.5 DiT self-attention was on the correctness-grade kernel, and one forward cost 47.84 s - #1557
Merged
Merged
Conversation
…ttention op, and paid 47.84 s a forward (#1549) One LTX-2.5 DiT forward at 768x448/49f measures 47.84 s on GB10 (n=119, median 47.91 s, spread 5.8%), and first-order arithmetic says it should take well under a second. The cause is that `ltx2_device.cpp:421` calls `vt::Attention`, which on CUDA is the kernel whose own header at `cuda_ops.cu:1456-1459` calls itself "Correctness-grade (M0.9)": one 256-thread block per (query, head), a 256-wide shared-memory tree reduction per key, and no K/V tiling. The attribution is arithmetic rather than assertion. `multimodal-speed.md:24-26` measures that same kernel on that same box at 5.70 ns per block-key iteration, and LTX's 1.77e8 block-key iterations per call over 48 layers come to 48.4 s against the measured 47.84 s. The reusable half is why nobody saw it. `kAttention` is deliberately frozen on the naive kernel so text decode stays byte-identical, and the fast kernels are separate ops each caller must name. There is no shape routing and no fallback notice, so a model that never opts in gets correct output at roughly 500x the cost with no warning anywhere: the goldens pass, no refusal fires, and the provider stats count the naive selection as the success it is. This commit is the spec only, so the implementation that follows it can be read against a statement of intent that predates it. It also files two things found while writing it rather than folding them in: FA-2 refuses head_dim 128 (#1551), and the same defect shape reaches every other `vt::Attention` caller (#1552). FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
… op, and give the tile the shared memory it asks for The DiT self-attention called `vt::Attention`, which on CUDA resolves to the kernel whose own header calls itself "Correctness-grade (M0.9)": one 256-thread block per (query, head), a 256-wide shared-memory tree reduction per key, and no K/V tiling. At 768x448/49f that is 75,264 blocks each looping 2352 keys, over 48 layers, and one DiT forward measured 47.84 s on GB10. Three changes, and the third is a prerequisite rather than a bonus. `ltx2_device.cpp` calls `vt::AttentionDenseFlash` in the self-attention branch. The dispatch RULE is unchanged: the branch is still chosen by `context == nullptr && a.bias == nullptr`, upstream's own self-attention marker, and never by what the numbers happen to be. Only the op it calls moves. `VLLM_LTX2_DIT_FLASH_ATTN=0` restores the old op so the A/B runs on one binary, the shape `VT_FA2_DENSE` already takes. `cuda_ops.cu` opts the flash K/V tile into more than the 48 KiB of dynamic shared memory every CUDA architecture guarantees without asking. The op advertises head_dim up to 256; at f32 that tile is 64 KiB at head_dim 128 and 128 KiB at 256, so those two shapes had never launched at all, and the symptom was a bare `invalid argument` from a kernel that had not run. The ceiling is QUERIED through the same cached seam `cuda_paged_attn.cu` uses, not assumed, and a tile that cannot be opted into falls back to `AttentionDenseFast` rather than refusing -- the flash kernel's own contract is that the two are bit-identical, so the fallback costs the tiling and nothing else. LTX's f32 arm is a supported arm at head_dim 128, so without this the swap would have turned slow-but-correct into a refusal. The host arm at `ltx2.cpp` is deliberately NOT moved. It computes into `std::vector<float>` and is CPU-only by construction, and on CPU both ops are the same registered function, so the swap would be a byte-identical no-op that moves the L2 parity reference off the reference op. NUMERICS, stated rather than glossed. On CPU this is byte-identical and the goldens are unmoved: `test_ltx2_device` 22/22 and 652/652, `test_ltx2` 43/43 and 4581/4581. On CUDA it is NOT bit-identical -- the warp kernel groups the head_dim partial sums across 32 lanes instead of a 256-thread block, so the same f32 online softmax associates differently. The binding gate is the existing host-vs-device parity case, at its committed 2e-5 f32 and 5e-3 bf16, and that case is not widened by this change. REACHABILITY. A new case drives the production entry point `Ltx2DitForwardDevice` and asserts the dispatch two-sidedly through `GetOpProviderStats`: `kAttentionDenseFlash` selected exactly 8 times (two self-attentions x two blocks x two batch rows) and `kAttention` selected zero. Mutation M1 restored `vt::Attention` at the call site (+1/-1, compile rc 0) and both halves went red -- `CHECK( 0 == 8 )` and `CHECK( 8 == 0 )`, exit 1 -- while every golden case in the same binary stayed green, which is the whole point: no numerical gate in this tree can see a 500x slower kernel that computes the right answer. The two things found while doing this are filed and NOT folded in: FA-2 still refuses head_dim 128 (#1551), and the same opt-in-by-name defect reaches every other `vt::Attention` caller (#1552). Both are listed under `## Owed` in the spec. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…is already documented one file over (#1549) `src/vt/cuda/cuda_attention_cross.cu:88-96` states the same defect, with the same arithmetic, for the same model: at f32 and head_dim 128 a 64-column K/V tile asks for 64 KiB of dynamic shared memory, over the 48 KiB a launch gets without opting in, "so the kernel would fail to launch on exactly the real geometry while every reduced-dimension gate (head_dim 8 and 4) passed". That kernel is itself a port of `AttentionDenseFlashKernel`; the problem was solved in the copy and never carried back to the original. This turns section 4.3 from a reading of the code into a corroborated finding, and it is why the new `test_ops_attention` case runs at the real head_dims rather than the fixture's. It also tightens section 2's attribution rather than loosening it: because `vt::AttentionCross` on CUDA is already flash-tiled, the DiT's four cross-attentions per block were never on the naive kernel, and only the two self-attentions were slow. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
… _WIN32 branch The reachability case set `VLLM_LTX2_DIT_FLASH_ATTN` with its own `#ifdef _WIN32 / _putenv_s / setenv` block. That is the exact thing issue #603 exists to stop: `setenv` and `unsetenv` are POSIX, MSVC has neither, and three files had already grown a private copy before `tests/support/test_env.h` landed to hold it once. Nothing would have caught it either, because `check-windows-portability.py` scans no test translation unit at all (#1107), so the first report would have been the `windows-msvc-*` lane going red for a fourth reason. `vllm_test::SetEnv` and `UnsetEnv` also throw rather than return a status, which is the behaviour this case wants: a test whose environment did not take effect is not running the arm it claims, and here the arm IS the assertion. Focused gate unchanged and green: 22/22 cases, 652/652 assertions, with the reachability case still reading `kAttentionDenseFlash selections = 8 (want 8), kAttention selections = 0`. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
… is 7.680 s, but the worker died before the naive arm (#1549) Lease `6c724dfd` on `dgx:gpu0`, source `30dce3a1d`, one binary built in-lease with cutlass-nvfp4, cutlass-fp8 and FA-2 all enabled for `[121a]`. CORRECTNESS FIRST, and it passed before any speed number was read. `test_ops_attention` 10/10 with **88,439** assertions against 23 on a CPU box, so the new f32 head_dim 64/128/256 case really ran and the shared-memory launch failure is repaired against a device rather than against a reading of the code. `test_ltx2_device` 22/22 with 749 assertions. The numerics claim is now a number: the swap is NOT bit-identical on CUDA and the measured deviation is **8.94e-08 video / 4.47e-08 audio**, f32 round-off scale, 224x inside the 2e-5 the case was already held to. The gate was not widened. REACHABILITY AT FULL SCALE, from the render's own log rather than a fixture. With `VT_OP_PROVIDER_STATS=1` the 21.00B render at 768x448/49f announces `op=21 device=1` (`kAttentionDenseFlash` on CUDA) and announces `op=18 device=1` (`kAttention` on CUDA) **zero** times. Same two-sided claim the unit case makes, taken through the production entry point on the real model. THE FLASH ARM IS MEASURED: n=19, median **7.680 s**, mean 7.633, 7.109 to 8.196, spread 14.2%, reduced from the engine's own `last=` lines. Against the recorded 47.84 s that is 6.23x, inside the predicted range. THE NAIVE ARM DID NOT RUN, so there is no same-binary A/B. At forward 20 the `rc` worker was lost and `dgx:gpu0` read `unhealthy (no contact)`. GB10 shares host RAM with the GPU and an unconstrained job has OOM-rebooted this box before, and `ab.sh` as first written carried no memory guard and no sample cap. That is a defect in this row's harness, not a finding about the change, and the A/B gate therefore reads PENDING rather than satisfied: 6.23x is a cross-run comparison against a number a different binary produced in a different lease, which is the weaker form the same-binary rule exists to replace. The harness now caps each arm at 13 samples, holds a 12 GiB `MemAvailable` floor, traces memory per arm, caches the build keyed on the source SHA so a resumed run does not re-spend 18 minutes compiling, and runs the NAIVE arm first. The previous order took the cheap arm first and lost the box before the expensive one, which is how a two-arm measurement became a one-arm one. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…one, on the public pages this claim reaches (#1549) The GB10 run produced one arm and no pair, so the record has to say both things: 7.680 s per DiT forward at `768x448/49f` (n=19, from the engine's own `last=` lines) is measured, and the A/B against the 47.84 s naive denominator is `PENDING` because the worker was lost at forward 20 and the naive arm never ran. The cause of that loss is UNPROVEN and this record refuses to name one: no memory trace was taken and the box did not return to be asked. `docs/STATUS.md` and `docs/BENCHMARKS.md` are written in the same commit as `.agents/benchmark-record.md`. A new measurement is a claim about the project, and `scripts/check-doc-checkpoint.py` holds every commit that records one to the surfaces that claim reaches a reader through. The two record commits this one replaces wrote the record without the surfaces, which is what put `documentation-checkpoint` red on this branch. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…hape that does not fit is refused by name Review finding on this branch: the first version of this fix answered a tile that does not fit by falling back to `AttentionDenseFastKernelCuda`, on the argument that the flash kernel's own header makes the two bit-identical by construction, so the fallback is numerically free. Both halves of that are true and it is still wrong. A silent degradation to the untiled rung is the DEFECT THIS ROW EXISTS TO REMOVE -- #1549 is entirely about a caller sitting on a 500x slower kernel with nothing anywhere saying so -- and because the two kernels agree bit-for-bit, no numeric gate anywhere can tell a fallback from a launch. That is not hypothetical: `test_ops_attention` reported 10/10 and 88,439 assertions on GB10 while flash never launched at f32 head_dim 256 once. So the launcher refuses instead, naming the device and the shortfall. The BOUND is derived at runtime and is written down nowhere. Against GB10's queried 101,376 B, with `kFlashBc = 64` and a tile of `2 * 64 * d * sizeof(T)`: no opt-in (49,152 B) opt-in, GB10 (101,376 B) bf16 d <= 192 d <= 256 f32 d <= 96 d <= 192 Neither column belongs in the code. 192/96 caps GB10 at two thirds of the head_dim it can serve; 256 assumes an opt-in ceiling GB10 does not have, and f32 at 256 wants 131,072 B. `cuda_device_caps.h:46` already says the rule -- "other architectures differ and MUST be asked, not assumed" -- so the tile is asked about per device. The `d <= 256` VT_CHECK stays: that one is the kernel's own `kMaxPerLane = 8` register bound and is a property of the code, not of any device. `SetDynamicSmemOptIn` is not a new function and not a second copy. It was cuda_paged_attn.cu's file-local helper; it moves to the shared seam it always belonged to -- declared in `cuda_device_caps.h`, defined beside `DynamicSmemFits` in `cuda_arch_tactics.cu` -- because a second copy is the parallel path AGENTS.md "Shared seams" forbids, and the two would have diverged immediately, the second one having been written to fall back silently. Its six existing call sites are unchanged and GB10 takes the same branch as before. The test is retitled to what it exercises. f32 head_dim 128 (65,536 B) is the shape that was red and is repaired; f32 head_dim 256 is NOT repaired and cannot be on this device. Its arm now asserts the device-derived outcome -- launch and match where the ceiling reaches 131,072 B, refuse and name BOTH numbers where it does not -- rather than a numeric match that a bit-identical fallback satisfies either way. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…d the reachability case restores its own instrument Three review findings, all in the same two files plus their test. TWO ANCHORS WERE WRONG, and one of them is load-bearing. The reachability case's ENTRY POINT paragraph and the spec's §6 both cited `src/vllm/multimodal/ltx2_video.cpp:4055-4059` as the production call to `Ltx2DitForwardDevice`. At HEAD and at this row's base that region is prose about the res_2s step counter; the call is at `:4246`. That is the citation the whole reachability argument rests on. The second, `cpu_ops.cpp:3551-3562`, is `FusedLoad`/`FusedStore`; the registrations it meant are at `:3750-3761`. The claim there was true and only the anchor was wrong. THE CASE LEAKED PROCESS STATE ON ANY UNWIND. It enables `vt::EnableOpProviderCallStats` and sets `VLLM_LTX2_DIT_FLASH_ATTN`, both per-process, and it contains `REQUIRE`s. A failed `REQUIRE` or a throw from either forward left the counting instrument enabled and the knob set for the other 21 cases in the binary, so an unrelated case's result would depend on which case failed first. Two scope guards now hold both. Measured rather than asserted: with a scratch `REQUIRE(false)` after the `SetEnv` and an observer case appended after it, the observer without the guards reads the knob still set and counts 8 leaked `kAttention` selections; with them it reads neither. Tree restored byte-for-byte and re-gated 22/22, 652/652. BOTH NAIVE `vt::Attention` CALL SITES NOW RECORD THEIR REASON IN THE FILE, in the `// VT-ATTN-NAIVE:` form #1578's allowlist defines for a deliberate one. This change ADDS one (the A/B knob's off arm in `ltx2_device.cpp`) and leaves `ltx2.cpp`'s host arm in place, while that allowlist exempts both stems on the ground that "another row is removing" their naive call. This row does not remove either, so without the markers both files would be permanently and silently exempt from the checker that row lands. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…n invocation The flash arm's 7.680 s is not reproducible from its own evidence. `arm-flash.log` opens at `[render] + load` with no command line, `wd-flash/` is empty and no `phase-log.json` was written, so the geometry, prompt, seed and sample cap behind that number cannot be read off the run. The only description of it was `/mnt/nas_share/rc/ltx25-attnflash/job/ab.sh`, a mutable path on a share, whose mtime is 25 minutes AFTER the run finished and which is not committed anywhere. The 47.84 s denominator has its full command line as line 1 of its own `render.log`; that asymmetry is why this branch's two measurement confounds had to be established from a `conditioning.tower` duration instead of being read off a recipe. Two repairs. The harness is committed here, so a revision of it is immutable and citable. And each arm now writes its own invocation to line 1 of its own log -- harness sha256, binary sha256, source SHA, geometry, seed, prompt and the resolved command line -- with a `harness_sha256` line into `PROVENANCE` beside the binary's. `set -x` is deliberately not used: it would interleave the trace with the engine's progress lines, which are the samples being reduced. Committed as-is otherwise. The file already carries the sample cap, the `MemAvailable` floor, the per-arm memory trace, the SHA-keyed build cache and the naive-arm-first ordering that the lost-worker run lacked, and rewriting it in the same change would leave nothing to compare the next run against. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…x carried two confounds, and neither was disclosed (#1549) Review findings against the records, all of them narrowing a claim. f32 HEAD_DIM 256 WAS NEVER REPAIRED, and three surfaces said it was. That tile is `2 * 64 * 256 * 4` = 131,072 B against GB10's queried 101,376 B, so it never fitted; the launcher fell back to `AttentionDenseFast`, which is BIT-IDENTICAL to the flash kernel by contract, and all 20,480 of that arm's assertions passed with flash never launching at 256 once. A numeric comparison cannot separate the two -- that IS the contract -- so the case measured a fallback and reported it as a launch. head_dim 128 (65,536 B) is genuinely repaired and that claim stands. THE RATIO IS ~6.0x, NOT 6.23x, and the correction is named rather than quietly applied. Two confounds, both inflating it, neither disclosed: * The denominator ran under `runguard.py --stack-period 12`, which `eu-stack`s the process and so ptrace-stops every thread. Its own `stacks.txt` prices that: 523 samples, median inter-sample delta 12.40 s against a 12.0 s period, so ~0.40 s median and 1.50 s max of stopped process per sample. ~3.9 samples land inside each 47.84 s forward, ~1.54 s, ~3.2%. Correcting only the denominator: 46.3 s / 7.680 s = 6.03x. The flash arm had no sampler. * The two arms used different prompts. The denominator's `render.log:1` has a ~70-word prompt and the harness has one short sentence, and `ltx2_video.cpp:2253` sets `context_tokens = encoded.seq` UNPADDED, so the DiT's cross-attentions see a different number of keys. Corroborated: `conditioning.tower` 45.013 s against 28.426 s. Same sign, not quantified. The defensible statement is the range 6.03-6.23x with the sampler correction named and the prompt confound uncorrected and pushing the same way. `~6.0x` is what the pages now quote. WHAT ELSE IS RECORDED: the runtime-derived bound and why neither #1578's 192/96 nor this branch's earlier 256 is it, with the note that #1578 must reconcile onto it and that this row does not edit that branch; the two corrected anchors; the scope-guard mutation and its counter-proof; the flash arm's missing invocation and the committed harness that replaces it; and the `documentation-checkpoint` red, which was this branch's own and not inherited, together with the side effect that its `set -eu` step stopped `check-now-current.py` and `check-role-discipline.py` from running in CI at all. NEWLY OWED, filed as [#1612](#1612): there is no numeric or pixel comparison at production geometry. The only numeric gate is the reduced-dimension host-vs-device case, which bounds the arithmetic change and not the change at head_dim 128 with 2352 keys over 48 layers; a diffusion render has no token gate; and the flash arm was interrupted before writing any frames, so no pixel A/B exists even against the completed 49-frame baseline render already on the NAS. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…ranch The branch is rebuilt on e2a9e03 and main has moved again since. Merging rather than rebasing keeps the repair commits reviewable against the head the fresh review read. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
localai-bot
force-pushed
the
row/LTX25-DIT-ATTN-FLASH
branch
from
August 21, 2026 16:03
2f39a94 to
b97910e
Compare
…of a function that moved `SetDynamicSmemOptIn` left this translation unit for the shared seam in the preceding commit, so `cuda_paged_attn.cu:944`'s "enforced by SetDynamicSmemOptIn above" points at nothing. The symbol name is what survives the move, and the note at :96 says where it went. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
localai-bot
pushed a commit
that referenced
this pull request
Aug 21, 2026
… measurement, not a convenience (#1544) D2 rejected `cudaFuncSetAttribute` partly because "it cannot be verified without a device, and this row has no lease". That reads as a preference. The number is now available from #1557's review: GB10's queried opt-in ceiling is 101,376 bytes, and head_dim 256 in f32 wants 131,072. The raise therefore cannot make the advertised 256 true for f32 on the part this project gates on, which is the exact width that motivated it, so narrowing beats raising on measured grounds. #1573 stays owed and says why the new number does not discharge it: 101,376 is a device value that bounds what an opt-in could buy, and it proves nothing about whether the launcher's refusal executes. That still needs a lease. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…ranch `.agents/benchmark-record.md` was the only conflict and both sides are pure appends: main's `### The Qwen3.5-4B 1.0283x row ran against an UNFUSED denominator` subsection and this row's `## LTX25-DIT-ATTN-FLASH` top-level section. Resolved by taking main's version byte-for-byte first -- it is a `###` under the section above it and would be orphaned by a `##` inserted before it -- then this row's section. Neither side lost a line. `.agents/issue-index.md` union-merged clean at `4 0`: four appended rows, no deletion, no duplicated id. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
… needed LTX-2.5's DiT renders at the stream dtype, and in production that is bf16 (`ltx2_device.cpp:1166`). At head_dim 128 the flash op's K/V tile is `2 * kFlashBc(64) * 128 * sizeof(bf16)` = 32,768 B, and the audio stream's head_dim 64 asks 16,384 B. Both are inside the 49,152 B of dynamic shared memory every CUDA architecture gives a launch WITHOUT an opt-in, so the routing this row exists for never needed `cudaFuncSetAttribute` at all. What the raise was actually serving was f32: head_dim 128 at 65,536 B, and head_dim 256 at 131,072 B against GB10's queried ceiling of 101,376 B (`cuda_device_caps.h:46`) -- which does not fit even WITH the opt-in, and was being answered by the bit-identical `AttentionDenseFast` while the case reported a launch. So the raise bought one shape this row does not run, and paid for it by moving a shared helper across two files and colliding with #1578 on the same lines of `cuda_ops.cu` and `test_ops_attention.cpp`. #1578 is the correct treatment and it merges first: instead of raising the cap it narrows the ADVERTISED head_dim domain to what the code can launch and refuses above it, which is a property of the code rather than of whichever device is under it. After it, bf16 head_dim 128 is inside the declared bound and this row's swap is untouched. Reverted in full. `src/vt/cuda/cuda_ops.cu`, `cuda_device_caps.h`, `cuda_arch_tactics.cu`, `cuda_paged_attn.cu` and `tests/vt/test_ops_attention.cpp` are byte-identical to `main`, so there is nothing left to conflict. The measurement survives the revert, and that is arithmetic rather than assertion: the binary that produced 7.680 s carried the opt-in call, but the helper returns immediately below 49,152 B, so it was a no-op on every launch those numbers came from. The withdrawn evidence is the `test_ops_attention` run, which measured code no longer in the tree; it is marked WITHDRAWN in `.agents/benchmark-record.md` rather than deleted. One consequence is disclosed rather than left to be found: the f32 L2 parity arm at production geometry now refuses instead of running slowly. It fails loud in both worlds -- a `cudaGetLastError` throw at `cuda_ops.cu:3352` today, a `VT_CHECK` naming the head_dim once #1578 lands -- and nothing gated reaches it, since production is bf16 and the f32 arm is exercised at the fixture's reduced dimensions. Filed under `## Owed` against #1612. Also in this commit, because they are the same edit to the same records: - The corrected ratio reads **~6.0x** everywhere. 6.23x survives only as the uncorrected upper end of the 6.03-6.23x range, never on its own. - Two anchors were wrong at this row's declared base `6b48edb2c` as well as at HEAD. `ltx2_video.cpp:4055-4059` pointed at prose about the res_2s step counter -- and it is the citation the whole reachability argument rests on; the production call site is `:4246`. `cpu_ops.cpp:3551-3562` pointed at `FusedStore`; the three CPU attention registrations are at `:3750-3761`. Every other anchor in the spec was re-read against the base rather than carried forward, and eight more were corrected. - The committed A/B harness had a third precondition that grepped for `FlashTileSmemOptIn`, a spelling no revision of this change ever used, so it counted 0 and would have `exit 42`-ed on a correct tree. It is removed with the cap-raise it guarded, and the comment says why a precondition that cannot pass is not a stricter one. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…ranch Clean automatic merge. `.agents/issue-index.md` union-merged at `4 0` -- four appended rows, no deletion, no duplicated id -- and this branch's diff against the merged main is confined to the ten files the row owns. In particular nothing under `src/vt/cuda/` or `tests/vt/` appears in it, which is the check that the reverted shared-memory cap-raise left no residue behind to conflict with #1578. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
Adding the bf16-tile arithmetic comment to `AttentionDev` shifted the call sites below it, and the stream-dtype `VT_CHECK` with them. The spec now gives both numbers where they differ -- the base `6b48edb2c` line and the post-change line -- rather than one that is right at neither revision, which is the failure the two corrected anchors in the previous commit already were. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
`check-public-doc-tables.py` reds a cell over 220 chars and a row over 600. The previous commit's addition to the caveat column took that row to 408 and 680, so the forensics move to `.agents/benchmark-record.md` -- which is where the checker says they belong, and where the withdrawn evidence is already written out in full. The row still names all four things a reader of the ratio needs: the `PENDING` A/B, the four ways the arms differ, the missing pixel gate, and that the cap-raise is reverted with its head_dim evidence withdrawn. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…worlds The comment said "refuses, loudly and by name". Only half of that is true today: `Check(cudaGetLastError(), "attention-dense-flash launch")` at `cuda_ops.cu:3352` names the OP but not the head_dim, and it is #1578's `VT_CHECK` that will name the head_dim too. Both are loud and neither is silent, which is the property that makes this a disclosure rather than a blocker -- so the comment now states the two cases separately instead of claiming the stronger one for both. Anchors resynced to the lines this edit moved. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
localai-bot
added a commit
that referenced
this pull request
Aug 22, 2026
…ow says why, and AttentionDenseFlash advertises the head_dim it can launch (#1544) (#1578) Two additive changes from #1544's `## Owed`. Neither moves a single existing caller's numerics, and that constraint shaped the whole design. ## The naive rung stops being a silent default `vt::Attention` resolves `OpId::kAttention` straight to the correctness-grade kernel, and nothing in the tree ever routes it up: the rung is whichever C++ function name the author typed. That is deliberate for six of the nine call sites, and invisible to everyone else, which is how one LTX-2.5 DiT forward came to cost 47.84 s. A token gate cannot see the difference by construction — every rung is bit-identical or inside the bf16 envelope, so the goldens pass either way. A selector that auto-routes was rejected, and not on taste. Three of the six sites are reference arms a gate compares against (`nemotron_h.cpp`, `nemotron_h_device.cpp`, `qwen3_5.cpp`), two are the `VT_*_EAGER` rungs of a same-binary A/B (`whisper_audio.cpp`, `qwen3_vl_vision.cpp`), and one is a measured-negative device path behind `VT_KIMI_DEVICE_MLA`. Rerouting any of them changes what the reference computes, which deletes the comparison the gate performs rather than fixing anything — the "widen the assertion until the gate passes" failure AGENTS.md names. `kAttention` and `vt::Attention` are untouched here. `scripts/check-attention-rung-consistency.py` requires the CHOICE to be recorded instead: a `// VT-ATTN-NAIVE:` reason on the call line or within 20 lines above it. The six deliberate sites now carry one, and an author who never heard of the fast rungs gets a red instead of a silent 500x. The scan runs over `checker_text.normalize_source`, so a commented-out, `#if 0`-ed or `if (false)`-ed call is a deletion to it exactly as it is to nvcc, and the reported `file:line` still describes the original file. The record is per-site and in-file, so an ordinary change writes no shared record at all. `scripts/attention-rung-allowlist.txt` holds only the three stems whose naive call another row is currently deleting — `muse_glimmer_vision` (#1545) and the two LTX-2.5 files — because editing the very lines those changes replace would conflict for no gain. A stale entry there is reported and is not fatal, so the removing row owes this file nothing. ## `AttentionDenseFlash` advertises the head_dim it can launch It claimed `head_dim <= 256` while asking the driver for `2*kFlashBc*d*sizeof(Tin)` bytes of dynamic shared memory, with no `cudaFuncSetAttribute` anywhere in `src/vt/cuda/`. The default 48 KiB cap made the real ceiling 192 in bf16 and 96 in f32, so Kimi at 192 f32 or Qwen3.5 at 256 would have received a bare launch error from the `cudaGetLastError` at the bottom of the launcher, naming nothing they could do instead. The bound now lives in `include/vt/ops.h` as `AttentionDenseFlashSmemBytes` and `AttentionDenseFlashMaxHeadDim` — pure host arithmetic, so a box with no GPU can execute it — tied to the kernel by two `static_assert`s on the tile width and the register blocking. The launcher refuses above it naming `vt::AttentionDenseFast`, which uses no shared memory and does serve those widths. Narrowing beats opting in to a larger cap here, and that is now a measurement rather than a preference. **GB10's queried opt-in ceiling is 101,376 bytes** (measured during #1557's review), while head_dim 256 in f32 wants 131,072. So `cudaFuncSetAttribute` cannot make the widest advertised width true on the part this project gates on — the raise buys nothing at the width that motivated it, and a caller there would have gone on falling back silently without ever launching. The bound is INCLUSIVE, which matters in one direction: head_dim 192 in bf16 lands exactly on 49152 and launches today, so an exclusive bound would refuse work that currently runs. Opting in stays available later as a widening for bf16 above 192, owned by nobody today. This mirrors vLLM's own polarity rather than inventing one: `vllm/model_executor/models/vision.py:99` selects an encoder backend by shape, and `vllm/v1/attention/backend.py:155-163` consults `supports_head_size` BEFORE dispatch instead of discovering the domain by launching. Both read at the pinned oracle `555967922`. ## Evidence RED first, on the unmodified tree: the checker reported all six deliberate sites at the exact lines #1544 names (`kimi_linear_device.cpp:598`, `nemotron_h.cpp:671`, `nemotron_h_device.cpp:330`, `qwen3_5.cpp:5279`, `qwen3_vl_vision.cpp:527`, `whisper_audio.cpp:324`) and correctly excluded the three allowlisted ones. GREEN after the markers: 9 sites, 6 marked, 3 allowlisted. `tests/scripts/test_check_attention_rung_consistency.py` 34/34, including six mutations that must go RED — a new unmarked model, a new unmarked call in a HEADER, a deleted marker, a second unmarked call inside an already-marked file, a stub reason, and a widened regex that would swallow the fast rungs. It also pins that the scanned population is not empty, which is the guard against the way a structural checker usually goes green: by matching nothing at all, and that every allowlisted stem names a model source that exists, which is what catches a typo. `tests/vt/test_ops_attention.cpp` gains the head_dim contract cases: the tile arithmetic at both element sizes, both honest bounds, that 256 is outside both, and the inclusive edge in both directions. 11 cases / 39 assertions, SUCCESS. MUTATED, because a green suite over new arithmetic proves only that the arithmetic agrees with itself. Making `AttentionDenseFlashMaxHeadDim` return `kAttentionDenseMaxHeadDim` — the exact contract this change repairs — turns the new case RED on 6 of its assertions, each printing the wrong value it now carries (`256 == 192`, `256 == 96`, `65536 <= 49152`, `131072 <= 49152`), so the mutation demonstrably applied and demonstrably compiled. `include/vt/ops.h` restored and verified by sha256 against its pre-mutation snapshot; rebuilt; 39/39 green again. Proof that no caller's numerics moved: the checker executes no model code; the head_dim guard fires only where the launch already failed; no marker changes a statement; and `git diff` touches no kernel arithmetic, no dtype and no default. ## What the fresh review changed Six findings, repaired here. None of them moves a kernel's arithmetic either. `pr-size` was RED, and this branch caused it. A checker created inside the range has no BASE version for the red-before half of the evidence run, so it has to register the disabled stub its own suite must reject; about twenty checkers do, and this one did not, so the gate could not classify the change at all. Measured rather than asserted: under the stub every case goes red — re-measured after the repairs below, `FAILED (errors=34)` — because the suite loads the checker as a module and every case calls into it. `agent-preflight.sh` does not run `check-pr-size`, which is why a local green said nothing about it. The second `static_assert` beside the kernel was a tautology. It read `8 * 32 == kAttentionDenseMaxHeadDim` while the real `kMaxPerLane` was a function-local `constexpr` inside the kernel body, invisible at file scope, so setting that local to 4 — precisely the drift the message claims to catch — left the assert reading `256 == 256`. The register blocking is now `kFlashMaxPerLane` at file scope; the kernel's register arrays and unrolled loops read it, and so does the assert. The same mutation now reads `128 == 256` and fails to compile. There is no nvcc on this box, so the tie was measured by extracting that constant block from `cuda_ops.cu` VERBATIM and compiling it against the shipped `include/vt/ops.h` under `g++ -fsyntax-only`: clean before, `static assertion failed` after, `cuda_ops.cu` restored and verified by sha256. The first assert (`kFlashBc == kAttentionDenseFlashTileCols`) was already a real tie and is untouched. Two comments claimed more than the code delivers. The launcher said its guard and its shared-memory request came from "the SAME function … cannot disagree"; they are two functions, and `AttentionDenseFlashMaxHeadDim` re-derives the division instead of inverting `AttentionDenseFlashSmemBytes`. The guarantee holds and is tested: mutating the `2 *` in `SmemBytes` to `3 *` reds 9 assertions of the shipped contract case, both inclusive-edge checks among them, while `MaxHeadDim(2) == 192` stays green — which is the re-derivation made visible. The comment now describes that. The `AttentionDenseFa2` fall-through comment promised "the best available kernel for their shape rather than a hard refusal", which stopped being true for an over-cap head_dim the moment this branch added the refusal; it now names the domain and records that every caller today is far inside it (max head_dim 80). The checker claimed "the population is what makes a green meaningful" and named no limits. Four spellings reach the same kernel undetected — a `using` declaration, a namespace alias, a `#define`, and a call through `&vt::Attention` — each verified during review to leave the checker green with a live unmarked call. None exists in this tree, and widening the regex would make every fast rung a site, which is D1's rejected failure mode again; closing it needs a compiler-side population, not a longer pattern. The docstring and spec D6 state the bound, so a green reads as "no unmarked `vt::Attention(` call" and never as "no model is naive". The OK line reported total and marked sites but never the number a reader needs: sites carrying no reason that pass only because their stem is allowlisted. It is not `sites - marked`, since a marked call inside an allowlisted file counts in `marked`. Two cases now pin the line; dropping the count from it reds them. Two records were wrong. `scripts/attention-rung-allowlist.txt` told a removing row to delete its stem without saying that `test_allowlist_holds_only_the_in_flight_stems` pins the set in another file and reds on the deletion; the allowlist header, the checker docstring and spec D7 now say so. The kernel-matrix cell stored this suite's case count — a measurement of one file inside another, which AGENTS.md names as a drift lock — so the count is gone rather than corrected. ## Two drift locks in the new suite, both repaired here Found while landing this change against #1579, by checking the interaction instead of assuming the two pull requests were independent. Each was green on its own; `main` went red only once both landed, which is why nothing on either branch caught it. **The first was the population floor.** `test_the_population_is_not_empty` asserted the scanned population was `>= 9` against a tree of EXACTLY 9 sites. Its own name says "is not empty" and its assertion pinned a count: the name was right. A raw total is a measurement of the model tree stored in a test file, which AGENTS.md `## Records` forbids, and it reds on any row that legitimately REMOVES a naive call — every stem on `scripts/attention-rung-allowlist.txt`, which is to say the rows that allowlist exists to unblock. It runs in the required `agent-record` job, so #1545 alone would have turned `main` red. **The second was `assertGreater(excused, 0)`** in `test_the_ok_line_reports_the_excused_sites`. Same shape, one case down: `excused` counts unmarked calls in allowlisted files, so it reaches 0 when the LAST stem is cleaned up, redding the case while the checker is green at rc=0. It does not fire for any of the three rows individually, so it would have sat latent until the LTX-2.5 reroute tripped it. Both are repaired here rather than deferred, because none of this has landed: `scripts/check-attention-rung-consistency.py`, its allowlist and its suite are all CREATED by this pull request, so correcting a defective assertion in them is repairing the change, not amending a gate that `## Changing the rules or a checker` governs. The floor is now genuine non-emptiness (`>= 1`). One new case asserts every allowlisted stem NAMES AN EXISTING model source — keyed on file existence and deliberately never on scan membership, because a stem stops having a call site the moment its removing row lands, which is the state the allowlist is built to survive and which `stale_allowlist_entries` already promises in its own docstring ("Reported, never fatal"); asserting scan membership would have rebuilt the identical lock one line over. The `excused > 0` floor is gone, its shipped-tree half kept because it RE-DERIVES the count instead of pinning it and so holds at 0 as well as at 3, and the coverage it was standing in for moved onto two cases driven over a constructed scan and a temporary allowlist, which the model tree cannot switch off. RED first, each mutation proven applied and restored by sha256. Stubbing `scan_models` to `{}` reds the floor at `0 not greater than or equal to 1`, and independently so does renaming the checker's regex. A typo'd `muse_glimmer_vison` entry reds the new case naming that stem while the CHECKER stays green — which is the point, since a bogus stem is reported only as STALE and never fails. For the second lock the control is sharper: with the checker's excused count broken AND the tree in its end state, the retained shipped-tree case goes GREEN over the defective checker and only the constructed case catches it, so the replacement is real coverage rather than a deletion in disguise. Composed green, measured on this head with #1579's `muse_glimmer_vision.cpp` copied in: with the allowlist stem left in place the suite is `Ran 34 tests ... OK` and the checker prints `STALE (not a failure)` at rc=0, where before the repair it read `8 not greater than or equal to 9`. With the stem also deleted, only `test_allowlist_holds_only_the_in_flight_stems` reds, which is the by-design pin on the set, and updating that set in the same change returns it to green — so both removal routes now have one. A fresh review of the repair returned one finding, repaired here, and repairing it turned up a second of the same kind. The new case's comment claimed it also caught a checker printing `sites - marked`. It does not, and no constructed scan could make it: `main()` reaches the OK line only when `drift_sites` is empty, and then every unmarked site is excused, so the two quantities coincide identically — 1000 reachable green states enumerated, 0 where they differ. Substituting `sites - marked` into the checker leaves all 34 cases green. Nearby, `test_the_excused_count_is_not_sites_minus_marked` claimed that dropping the allowlisted file's marked site made the two diverge; it does not, because that lowers `sites` and `marked` together. Both comments now state what is actually pinned and why the rest cannot be, which is the same "a comment claimed more than the code delivers" class this branch already repaired twice. The fix is comment-only, and that is proven rather than asserted: `ast.dump` is byte-identical across the change, so no assertion, fixture or docstring moved. Five rounds of comment repair were needed, because each of the first three removed a false claim by writing a NEW causal explanation that the next fresh review then measured false. From the third round on the rule was to DELETE rather than re-explain, and to run every clause left standing. Comment lines go down, not up. `ast.dump` is byte-identical across rounds three and four, so no assertion or fixture moved; round five changes exactly one `assertTrue` MESSAGE string, with the assertion condition's own `ast.dump` hash shown identical either side. Two findings from those reviews were REFUTED by measurement rather than applied. `test_widening_the_regex_to_the_fast_rungs_is_visible` was reported dead because it survives a `drift_sites` break and a `\b` removal — but neither is the widening it names, and mutating `_NAIVE_CALL` to `\bvt::Attention\w*\s*\(` reds it along with five others. The spec's "six mutations that must go RED" was reported as five; there are six, and each reds under the mutation it names. Applying either would have renamed a working test and made an accurate record false. **Knowingly shipped, and recorded rather than hidden.** Three comments in `scripts/check-attention-rung-consistency.py` (`:58-61`, `:93-96`, `:252-255` — anchors measured, not estimated) are MEASURED FALSE and are NOT repaired here. All three are now enumerated in the test file beside the case that pins the real behaviour, so a reader of the suite can find every one. They claim the `\b` in `r"\bvt::Attention\s*\("` is what stops the pattern matching the fast rungs. It is not: the trailing `\(` does that, `vt::AttentionDenseFlash(` matches with neither, and the two patterns differ on exactly the 63 identifier characters — `xyvt::Attention(` alone. With the `\b` removed the suite stays green at `Ran 34 tests ... OK`, so the companion claim that the widening "is caught in this suite" is false too. The equivalent claims in the TEST file ARE repaired, because `tests/scripts/test_*.py` is not a governance checker. **The tree therefore contradicts itself between the test and the checker beside it, and that is deliberate**: `scripts/check-pr-size.py:170` classifies every `scripts/check-*.py` as a `governance_checker` and demands executable red-before evidence, which a comment-only diff cannot produce. Attempting the repair returns `ERROR: BASE checker stayed green ... changed test is not semantic evidence` at rc=1. The prepared patch was deliberately not committed rather than land a red `pr-size`. #1629 records both drift locks. #1631 records the comment freeze, and it is not one file: the pattern covers all 43 `scripts/check-*.py` checkers plus the `.sh` ones, so a false comment in any checker in this repository cannot be corrected on its own today. Both are linked in the three places AGENTS.md requires rather than in this body alone: `.agents/issue-index.md` gains one appended row each, and `## Owed` in `.agents/specs/attention-rung-visibility.md` records #1629 as DISCHARGED HERE and #1631 as owed with the reason it cannot be. No gate would have caught their absence, because `check-agent-record.py` counts index rows that name no owner and there was no row at all. ## Owed, and named rather than skipped Nothing on a CPU-only box executes `LaunchAttentionDenseFlash`, so the pure-arithmetic cases stay green over a launcher that lost the bound. The on-device refusal case emits a loud PENDING message and returns, and #1573 owns running it plus the reachability mutation that proves the case reaches the guard. No lease was taken: `dgx:gpu0` was unavailable for the whole branch. The 101,376-byte GB10 ceiling above does NOT discharge #1573 — it bounds what an opt-in could buy and says nothing about whether the launcher's refusal executes. Owed and filed rather than left to be discovered: **#1629**. The new `test_the_population_is_not_empty` case asserts the scanned `vt::Attention` population is `>= 9`, and the shipped tree has exactly 9 sites, so the floor has ZERO headroom and any row that deletes a naive call reds it. That is every stem on `scripts/attention-rung-allowlist.txt` — `muse_glimmer_vision` (#1545) and the two LTX-2.5 files — which is to say the rows the allowlist exists to unblock. Measured while landing this change, with #1579's `muse_glimmer_vision.cpp` copied onto this head: leaving the stem reds that case at `8 not greater than or equal to 9`, and deleting the stem reds it AND `test_allowlist_holds_only_the_in_flight_stems`, so a removing row has no green path. The tree was restored byte-for-byte after each and the suite returns to `31 tests ... OK`. This is the same drift-lock shape the kernel-matrix cell above was corrected for, retained one file away, and it is NOT repaired here because changing the floor changes what the gate accepts and AGENTS.md routes that to its own row, spec and red-before evidence. #1629 carries the evidence and two candidate directions, and #1579 is held on it. Inherited red, not introduced here: `test_cpu_x86_llamacpp_floor` fails in `agent-preflight.sh` on this box. It is the known load-dependent case of #618 — at high loadavg the harness exits `NO_QUIET_WINDOW (4)` where the case expects `GIVING_UP (2)` — and both the case and `scripts/cpu-x86-llamacpp-floor.sh` are byte-identical to `origin/main` on this branch, which is how it was established as inherited rather than assumed to be. FIVE CI jobs are red on the head, every one of them inherited from main with a named owner, and each was verified per-job against a main baseline rather than asserted. `windows-msvc-cpu` and `windows-msvc-vulkan` are the standing PR-only red (#584, #965). `build-test-cpu` and both `sanitize-cpu` arms fail on ONE shared doctest case, `test_runner.cpp:1557`; that case landed on main in `e2a9e035d` (#1273) and is owned by #1608 and #1602. Inheritance was established by comparing the failing ASSERTION and not the job name: the scheduled main baseline at `e2a9e035d` fails the identical `CHECK_THROWS_WITH_AS( make_runner(), "Block size must be a multiple of 16", std::invalid_argument )` at the same `test_runner.cpp:1557`, with the same "No valid attention backend for device type 0" text, in all three jobs, and with zero sanitizer findings in either sanitized arm. `test_runner.cpp` is not touched by this branch. `build-newest-gcc` was the sixth red when this body was first written and is GREEN here. #1581 and #1618 landed the `::getpid` repair on main, and this branch was re-merged onto `2e7f3bee7` to pick it up, so the job now compiles and reports on this change. Every job that can see this change is green, including `pr-size` and `agent-record`, which is the job that runs this row's new checker. Closes #1544. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code] --------- Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
…this row's record sections at the file's TRUE end Two record files collided, and both are the shape AGENTS.md `## Records` names as a lock: a shared surface that every pull request writes. Neither collision is a disagreement about a fact, so neither is resolved by choosing a side. `.agents/benchmark-record.md` is a tail-append collision. This row appended its `LTX25-DIT-ATTN-FLASH` section and `origin/main` appended `SPEC-DFLASH2 W6` at the same place. Both are kept: main's section stays where main put it and this row's 173 lines are re-appended after it, at the file's true end. Against `origin/main` the file reads `173 0` in `git diff --numstat` with zero deleted lines, so nothing of main's was traded away to make room. `docs/STATUS.md` is a keyed record and was resolved as one, by taking the complete target-branch row and applying this row's scoped edit again rather than accepting either side whole. Two rows conflicted. `Speculative decoding` is main's alone -- this branch never touched it, verified byte-for-byte against the merge base `c020347a7` -- so main's text is taken unchanged. `Image, video, audio, speech, music, and diffusion models` was edited by BOTH: main rewrote the MiniMax-Music3 clause (595.9 s to 449.969 s, the DiT falling from 62.4 % to 50.2 %, citation gaining #1555) while this row appends the LTX-2.5 DiT sentence after it. Composing main's row with this row's addition leaves `docs/STATUS.md` reading `1 1` against `origin/main`: exactly the one row this change owns, with every other row byte-for-byte main's. The #1578 interaction was re-measured here rather than assumed, because this row removes two more allowlisted `vt::Attention` sites. On the merged content `tests/scripts/test_check_attention_rung_consistency.py` runs 34 of 34 green and `scripts/check-attention-rung-consistency.py` exits 0, reporting `ltx2` and `ltx2_device` as `STALE (not a failure)` -- the outcome `scripts/attention-rung-allowlist.txt` documents for a removing row, since deleting the stems here would red `test_allowlist_holds_only_the_in_flight_stems` unless its expected set moved in the same change. `.agents/issue-index.md` unioned cleanly: `4 0` against `origin/main`, no issue id twice in the file. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
localai-bot
added a commit
that referenced
this pull request
Aug 22, 2026
…he file it named (#1665) `47a918d8f` (#1579, issue #1545) and `90e8c3c85` (#1557, issue #1549) landed the routing that `scripts/attention-rung-allowlist.txt` was parked for, and left their three stems behind. That is by design: `scripts/check-attention-rung-consistency.py` reports a discharged stem as `STALE (not a failure)` and exits 0, so the removing row never has to edit this file and the deletion falls to whoever runs preflight next. This is that preflight. Closes #1663. ## The window is not free, and that is the finding `## Risks/decisions` D4 of the spec says the deferral is safe because a stale entry is reported and never fatal. True of the exit code, and not the whole story: an allowlisted stem excuses its ENTIRE translation unit, never only the call that earned the entry. Every `VT-ATTN-NAIVE:` marker in a covered file is therefore decorative for as long as the stem sits there. Measured on `db648fb88` by deleting the live marker at `src/vllm/model_executor/models/ltx2.cpp:959`: | allowlist | checker | report | |---|---|---| | the three stems, as `main` has them | **rc=0** | `7 carry a recorded reason, 1 unmarked and excused` | | the stems removed, as here | **rc=1** | `src/vllm/model_executor/models/ltx2.cpp:966` | Both arms restored byte-for-byte against a sha256 taken before the mutation. So the entries were a live hole in the guard #1544 exists to be, open from `90e8c3c85` until here — not untidiness. The deferral D4 designs for is still right, because it genuinely keeps the removing row off a shared file; what is added to the spec is what it costs, so the next row that parks a stem reads it with the cost attached. ## The three earned their green separately, and asymmetrically - `muse_glimmer_vision` names `vt::Attention` **nowhere**: `47a918d8f` routed the perception encoder's sole path to `vt::AttentionDenseFlash`. - `ltx2` and `ltx2_device` still **name** it, at calls that now record their own reason — the host arm, CPU-only by construction, where `kAttention` and `kAttentionDenseFlash` resolve to the same registered function (`src/vt/cpu/cpu_ops.cpp:3750-3761`); and the `VLLM_LTX2_DIT_FLASH_ATTN=0` arm of a same-binary A/B, which exists so both halves of the 47.84 s / 7.680 s measurement run from one build. One assertion covering all three would be false of one of them in either direction, so the new test case asserts them apart. ## The expected set moves in the same change The allowlist's own header requires it: the checker does not fail on a stem set that has drifted, and `tests/scripts/test_check_attention_rung_consistency.py::ShippedTreeTests::test_allowlist_holds_only_the_in_flight_stems` does, on an addition or a deletion alike. An empty expected set is not a weaker assertion. `drift_sites` now excuses nothing, so `test_shipped_tree_is_green` measures the shipped tree on its markers alone, and a silent append still reds this case exactly as before — verified by appending `some_new_tower` and measuring 3 failures, restored after. The new `test_the_formerly_allowlisted_stems_pass_on_their_own_merit` re-states positively what the allowlist used to assert by omission. The header keeps the reason each stem left, so an empty parking lot does not read as an abandoned one. ## Evidence ``` python3 scripts/check-attention-rung-consistency.py OK (attention rung): 8 vt::Attention call site(s) in 8 model source file(s); 8 carry a recorded reason, 0 unmarked and excused by 0 allowlisted in-flight stem(s). rc=0 python3 -m unittest tests.scripts.test_check_attention_rung_consistency Ran 35 tests in 5.297s -- OK scripts/agent-preflight.sh exit=0, All gates green. scripts/agent-preflight.sh --staged exit=0, All gates green. ``` `.agents/specs/attention-rung-visibility.md` `## Now` records the discharge and the measured cost of the window. #1629 and #1631 are separate defects in the same checker and are untouched. ## Fresh review, and the four findings repaired in `f64effeca` The review confirmed the headline mutation independently and found four things. **F1 was the one that mattered**, and it was a regression this branch introduced. The `#1631` owed entry was edited to say the checker comment at `:252-255` "denies an equality that held ... and no longer does" — wrong in the direction that misleads, because if the equality no longer held then the comment denying it would be RIGHT. It still holds, and after this row it holds for a stronger reason: with an empty allowlist any green tree has `sites == marked` and `excused == 0` by construction, so it cannot fail. The same sentence credited the 8/8/0 triple to #1663, when `db648fb88` already read 8 sites and 8 marked — `47a918d8f` and `90e8c3c85` moved that; #1663 moves only the excused count. **F2** — the marker is at `ltx2.cpp:959`, not `:958`, which is `a.causal = false;`. Corrected here and in the index row, which is append-only and could not have been corrected after the merge. **F3** — the `KERNEL-ATTN-DENSE-FLASH` evidence cell read `checker green (9 sites / 6 marked / 3 unmarked and excused by the 3 in-flight allowlisted stems)`. Every number was already false on `db648fb88` and all three would be false again after this row, which is the point: a count of one file stored in another reds on every row that legitimately adds or removes a call. The cell stops quoting the triple rather than restating it, because restating it rebuilds the drift lock AGENTS.md `## Records` forbids — and that is the same defect #1629 names in this checker's own test. **F4** — `test_every_allowlisted_stem_names_a_real_model_source` now iterates over an empty set. Dormant, not dead, and the file now says so: it guards a file that is currently empty and fires on the first thing added to it, measured rather than asserted, since appending one bogus stem reds it together with the pinning case. `test_cpu_x86_llamacpp_floor` red twice during this work, at loadavg 31 with `NO_QUIET_WINDOW` and `busy=161%`. That is #618, and it is not reachable from a diff of three Markdown files and one Python comment: the same suite passed on a clean tree and on this tree at lower load. ## Why this merged without a complete rollup on its exact SHA `main` moved six times while this branch was in review, the last move being this session's own #1670 landing ten rows into `.agents/issue-index.md`. GitHub does not honour the `merge=union` driver on that file, so each move marked this PR `CONFLICTING` and forced a merge commit, restarting a ~75-minute CI cycle against a branch that merges ~10 commits/hour. That loop has no fixed point. **A complete rollup exists, and it covers every byte this branch contributes.** At `22ee35d7e` the rollup settled with **zero pending**. Against that head, the four files this branch owns are byte-identical to what is being merged now: ``` git diff --stat 22ee35d b974837 -- \ scripts/attention-rung-allowlist.txt \ tests/scripts/test_check_attention_rung_consistency.py \ .agents/specs/attention-rung-visibility.md \ .agents/kernel-matrix.md [empty] ``` So the `+41/-1` in the Python test — the one real test change here — was **present and gated** at the head that produced the complete rollup. Everything added to the range since is `main`'s own work arriving through three merges, each commit of which carries `main`'s own verdict. This is not "the delta is small enough to ignore"; it is that the delta contains none of this change. Both SHAs are pinned deliberately. `origin/main` is a moving ref, and diffing it instead of a pinned commit made a sibling branch appear to delete 768 lines of `vocoder1d.cpp`, `cpu_conv1d_*` and three test files, when the deletions were another commit landing between two commands. **Every failure at that gated head was verified inherited by failure text**, not by job name, against `main`'s baseline `90e8c3c85`: | job | evidence | |---|---| | `build-test-cpu` | 1 of 590, test #282, **byte-identical 335-byte** assertion at `test_runner.cpp:1557` | | `sanitize-cpu (address,undefined)` | same assertion, **0** ASan/UBSan diagnostics both sides | | `sanitize-cpu (thread)` | same assertion, **0** TSan diagnostics both sides | | `windows-msvc-cpu` / `-vulkan` | `/W4 /WX ... negated by /w` refusal (#1649), fires before compilation, **0** `error C####`, **0** `error LNK####` | | `build-newest-gcc` | **green** — the one job that can never be called inherited, since `main-baseline.py`'s newest row predates #1581 | The `test_runner.cpp:1557` failure is #1602/#1608: the attention-backend selector refuses a non-multiple-of-16 block size before `CheckKvCacheShape` can, so the case asserts a message only a ROCm/FLASH_ATTN build produces. Nothing outside that characterised set appeared on any run of this branch. Records verified on the merged result: 598 rows, 598 unique issue ids, zero duplicates, `1 0` for this branch's single #1663 row. Local preflight green at the pushed head, no gate skipped. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code] --------- Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One LTX-2.5 DiT forward at
768x448/49fmeasured 47.84 s on GB10 -- n=119, median 47.91 s, spread 5.8%, from the engine's ownlast=samples -- and first-order arithmetic says it should take well under a second.The DiT self-attention called
vt::Attention, which on CUDA resolves to the kernel whose own header calls itself "Correctness-grade (M0.9)": one 256-thread block per (query, head), a 256-wide shared-memory tree reduction per key, and no K/V tiling, so K and V are re-read from global once per (query, head). At that geometry the video stream is 2352 tokens x 32 heads = 75,264 blocks each looping 2352 keys, over 48 layers.The attribution is arithmetic rather than assertion.
.agents/specs/multimodal-speed.mdmeasures that same kernel on that same box at 5.70 ns per block-key iteration, and 1.77e8 x 5.70 ns x 48 = 48.4 s against the measured 47.84 s. A 1% match, which leaves the other 1% for 48 blocks of GEMMs, norms, RoPE, gating and six attentions each.After this change one forward is 7.680 s (n=19, median,
768x448/49f, GB10). The ratio is ~6.0x, and it is not an A/B. Both qualifications are load-bearing and both are under "The A/B" below.Why nobody saw it, which is the part worth keeping
kAttentionis deliberately frozen on the naive kernel so that text decode stays byte-identical. That decision is correct and this change does not touch it.The consequence is the defect. The fast kernels are separate ops that each caller must opt into by name --
kAttentionDenseFast,kAttentionDenseFlash,kAttentionDenseFa2. There is no automatic selection, no shape routing, and no fallback notice. A model that never opts in gets correct output at roughly 500x the cost, with no warning anywhere.Nothing in this tree can detect that. The output is right, so every golden passes. The op is registered, so no refusal fires.
GetOpProviderStatscounts the naive selection and reports it as a success, because it is one. The only symptom is a wall clock, and a diffusion render has no reference wall clock to be held to. That is why the gate for this change is a dispatch observation and not a number.What changed
ltx2_device.cppcallsvt::AttentionDenseFlashin the self-attention branch. That is the whole of the product change. The dispatch RULE is unchanged: the branch is still chosen bycontext == nullptr && a.bias == nullptr, upstream's own self-attention marker, never by what the numbers happen to be. Only the op it calls moves. Its square-problem contract holds by construction, because that branch is entered only whens == tq.VLLM_LTX2_DIT_FLASH_ATTN=0restores the old op, so both arms of the measurement run from one binary. Same shape asVT_FA2_DENSE, documented indocs/ENVIRONMENT.mdas a measurement lane and never a configuration. Both remaining naivevt::Attentioncall sites -- that one, andltx2.cpp's CPU-only host arm -- carry a// VT-ATTN-NAIVE:line saying why, the form #1578's checker defines for a deliberate site.The host arm at
ltx2.cppis deliberately NOT moved. It computes intostd::vector<float>and is CPU-only by construction, and on CPU both ops are the same registered function (src/vt/cpu/cpu_ops.cpp:3750-3761), so the swap would be a byte-identical no-op that moves the L2 parity reference off the reference op.The shared-memory cap-raise is REVERTED, and that is the change since the review
An earlier version of this branch also raised
LaunchAttentionDenseFlash's dynamic shared-memory cap throughcudaFuncSetAttribute, movedSetDynamicSmemOptInonto a shared seam, and added head_dim contract cases totests/vt/test_ops_attention.cpp. All of it is gone.src/vt/cuda/cuda_ops.cu,cuda_device_caps.h,cuda_arch_tactics.cu,cuda_paged_attn.cuandtests/vt/test_ops_attention.cppare byte-identical tomain.The swap never needed it. LTX renders at the stream dtype, and in production that is bf16 (
ltx2_device.cpp:1183). The flash op's K/V tile is2 * kFlashBc(64) * head_dim * sizeof(Tin), so the video stream's head_dim 128 asks 32,768 B and the audio stream's head_dim 64 asks 16,384 B. Both are inside the 49,152 B every CUDA architecture gives a launch without any opt-in at all.What the raise was actually serving does not fit. The shapes over 48 KiB here are f32: head_dim 128 at 65,536 B, and head_dim 256 at 131,072 B against GB10's queried ceiling of 101,376 B (
cuda_device_caps.h:46). The 256 shape therefore does not fit even with the opt-in -- it was falling back to the bit-identicalAttentionDenseFastwhile the case reported a launch. So the raise bought this row one shape it does not run, and paid for it by moving a shared helper across two files and colliding with #1578 on the same lines.#1578 owns the bound and takes the opposite, better approach. Rather than raising the cap it makes the ADVERTISED domain honest:
AttentionDenseFlashdeclareshead_dim <= 256while it can only launch bf16 192 / f32 96, and #1578 narrows the declaration to what the code can do and refuses above it. That is a property of the code rather than of whichever device is underneath, and it is thesupports_head_size()polarity vLLM already has. #1578 merges first, and after it bf16 head_dim 128 is inside the declared bound, so this change is unaffected. There is now nothing left to conflict.One consequence is disclosed rather than left to be found. With the raise gone, the f32 L2 parity arm at production geometry (head_dim 128, 65,536 B) reaches
AttentionDenseFlashand cannot launch: acudaGetLastErrorthrow atcuda_ops.cu:3352today, aVT_CHECKnaming the head_dim once #1578 lands. It fails loud in both worlds and never silently, and nothing gated reaches it -- production is bf16, and the f32 arm is a parity reference exercised at the fixture's reduced dimensions. Filed under## Owed.Numerics, measured rather than asserted
On CPU: byte-identical.
kAttentionandkAttentionDenseFlashare the same registered function pointer, and the goldens are unmoved --test_ltx2_device22/22, 652/652;test_ltx243/43, 4581/4581;test_ltx2_video102/102, 4194/4194;test_ops_attention_cross9/9, 32/32.On CUDA: NOT bit-identical, and here is the number. The warp kernel groups the head_dim partial sums across 32 lanes instead of a 256-thread block, so the same f32 online softmax associates differently. On
dgx:gpu0the host-vs-device parity case measures video 8.9407e-08, audio 4.47035e-08 against its committed2e-5, and bf16 CUDA-vs-CPU-backend at 0. That is f32 round-off scale, 224x inside the gate, and the gate was not widened.That is the only numeric evidence there is, and it bounds less than it looks like it bounds. The case runs the fixture's reduced dimensions, so it bounds the ARITHMETIC change -- a length-D sum reassociated -- and not the change at head_dim 128 with 2352 keys over 48 layers. A diffusion render has no token gate to fall back on, and the flash arm was interrupted before writing any frames, so no pixel comparison exists either, not even against the completed 49-frame baseline render already on the NAS. Filed as #1612 and listed under
## Owed.The
test_ops_attentionevidence is WITHDRAWN, not restated. The GB10 lease ran it at 10/10 and 88,439 assertions, and this change claims nothing from that run: it measured the head_dim cases that came with the cap-raise, and those are no longer in the tree. Two of its three arms would not have supported the claim anyway, for the reason above. Thetest_ltx2_devicerows and the render below DO survive the revert, and that is arithmetic rather than assertion -- the measured binary carried the opt-in call, but the helper returns immediately below 49,152 B, so it was a no-op on every launch those numbers came from.Reachability, twice
Unit. A new case drives the production entry point
Ltx2DitForwardDevice-- called from the denoise loop atltx2_video.cpp:4246-- and asserts the dispatch two-sidedly throughGetOpProviderStats:kAttentionDenseFlashselected exactly 8 times (two self-attentions x two blocks x two batch rows) andkAttentionselected 0. The negative half is what makes it a routing proof rather than an addition proof. The case scope-guards its own process state, because it enables a counting instrument and sets an env var and containsREQUIREs; measured with a scratchREQUIRE(false)and an appended observer, without the guards the observer reads the knob still set and 8 leaked selections, and with them it reads neither.That
:4246is itself a repair. This branch and its spec both citedltx2_video.cpp:4055-4059, which points at prose about the res_2s step counter -- at the row's declared base6b48edb2cand at HEAD alike, so re-reading it at either revision would have caught it. It is the citation the whole reachability argument rests on.cpu_ops.cpp:3551-3562was wrong the same way and pointed atFusedStore. Every other anchor in the spec has been re-read against the base rather than carried forward, and eight more were corrected.Mutation M1: restore
vt::Attentionat the call site, +1/-1, compile rc 0. Both halves went red --CHECK( 0 == 8 )andCHECK( 8 == 0 ), exit 1 -- while every golden case in the same binary stayed green. That contrast is the finding: no numerical gate in this tree can see a 500x slower kernel that computes the right answer. Tree restored and re-gated green.Production, on the real model. With
VT_OP_PROVIDER_STATS=1the full 21.00B render at768x448/49fon GB10 announcesop=21 device=1(kAttentionDenseFlashon CUDA) and announcesop=18 device=1(kAttentionon CUDA) zero times. Same two-sided claim, taken through--device cudaat full scale rather than on a fixture.The A/B: one arm measured, the pair still PENDING
Lease
6c724dfdondgx:gpu0, source30dce3a1d, one binary built in-lease with cutlass-nvfp4, cutlass-fp8 and FA-2 allENABLED for [121a]. Correctness cleared before any speed number was read.Flash arm, per DiT forward at
768x448/49f= 2352 tokens, from the engine's ownlast=lines:The naive arm did not run, so this is not an A/B. At forward 20 the
rcworker was lost anddgx:gpu0readunhealthy (no contact). The cause is UNPROVEN and this change does not name one: no memory trace was taken and the box did not return to be asked. What is established is that the harness as first written carried no memory guard and no sample cap, which is a defect in this row's harness rather than a finding about the change.The 47.84 s denominator carries two confounds this arm does not, and both inflate the ratio. Neither was disclosed before:
runguard.py --stack-period 12(render.log:1), whicheu-stacks the process and soptrace-stops every thread. Its ownstacks.txtprices that: 523 samples, median inter-sample delta 12.40 s against a 12.0 s period, so ~0.40 s median and 1.50 s max of stopped process per sample. About 3.9 samples land inside each 47.84 s forward, ~1.54 s, ~3.2%. Correcting only the denominator gives 46.3 s / 7.680 s = 6.03x.render.log:1carries a ~70-word prompt; the harness uses one short sentence, andltx2_video.cpp:2253setscontext_tokens = encoded.sequnpadded, so the DiT's cross-attentions see a different number of keys in each arm. Corroborated rather than inferred:conditioning.toweris 45.013 s against 28.426 s. Same sign, and not quantified.So the defensible statement is the range 6.03x to 6.23x, quoted as ~6.0x, with the sampler correction named and the prompt confound uncorrected and pushing the same way.
6.23xsurvives in the records only as the uncorrected upper end of that range, never on its own. The A/B gate readsPENDINGand this change does not claim otherwise.The flash arm's artifacts do not record what it ran, which is why those confounds had to be established from a phase duration.
arm-flash.logopens at[render] + loadwith no command line,wd-flash/is empty, nophase-log.jsonwas written, and the only description of the run was a mutable NAS path edited 25 minutes after it finished. Both halves are repaired: the harness is committed asscripts/ltx25-dit-attn-flash-ab.sh, and every arm now writes its own invocation -- harness sha256, binary sha256, source SHA, geometry, seed, prompt, resolved command line -- to line 1 of its own log. It also caps each arm at 13 samples, holds a 12 GiBMemAvailablefloor, caches the build on the source SHA, and runs the naive arm first.That harness also had a precondition that could never pass. It grepped
cuda_ops.cuforFlashTileSmemOptIn, a spelling no revision of this change ever used, so it counted 0 and would haveexit 42-ed on a correct tree as readily as on a wrong one. It is removed with the cap-raise it guarded.CI
documentation-checkpointwas RED and it was THIS BRANCH's, not inherited.2aa78c69band2f39a9426each recorded a measurement in.agents/benchmark-record.mdwithout writingdocs/STATUS.md(anddocs/BENCHMARKS.mdfor the second); the control on the main-only range4c193bd55..5d548d003is rc 0. Neither commit is in this branch's history any more, and the checker is re-run at this head rather than trusted to have stayed fixed -- a job that has stopped appearing in a failing set is not the same fact as a job that passes. Local rc 0 over the branch range. A side effect worth recording: that job runsset -euand this checker is the FIRST of three commands, socheck-now-current.pyandcheck-role-discipline.pynever ran in CI on this branch at all. Both are rc 0 locally, so nothing hides behind it.build-newest-gccis now GREEN onmainsince fix(SPEC-DFLASH2): build-newest-gcc has been red since W5, because the GGUF test bypassed the process_id seam (#1575) #1581 landed, and this branch carries that fix through the merge. Earlier runs of this PR predate it. A red here now would be this branch's, not inherited.build-test-cpuand bothsanitize-cpulanes: inherited, and verified from this head's own logs rather than from the issue numbers. All three fail on exactly one case out of 585 --test_runner.cpp:1557,CHECK_THROWS_WITH_AS(make_runner(), "Block size must be a multiple of 16", ...)receivingNo valid attention backend for device type 0instead -- with byte-identical text in all three, andmain's own newest baseline run fails the same case.test_ltx2_devicepasses in all three, so this change's own cases are green on the lanes that run them. From test(BACKEND-ROCM): the block-size contract is enforced at its production call site #1273; owned by test_runner's new block-size case is red on a default CPU build: the SELECTOR refuses before CheckKvCacheShape can, so it asserts a message only a ROCm/FLASH_ATTN build produces #1602 andtest_runner"refuses a non-multiple-of-16 block size" is RED on main ate2a9e035dfor every CPU build: the attention registry refuses first, with another message #1608.windows-msvc-cpu/windows-msvc-vulkan: inherited, baseline-less lane. A markdown-only control PR (record(BACKEND-ROCM): index the decode profile, which refutes the transfer hypothesis #1295) fails the identical step. test_openai_api_server crashes on Windows with STATUS_STACK_BUFFER_OVERRUN (0xC0000409), unmasked by the #512 fix #584/windows-msvc-cpu/vulkan are red on EVERY pull request: C4456 'loaded' shadow in server_main.cpp, and it is not #645 #965 own them.main's newest baseline at503e459005d7.scripts/main-baseline.pywas the instrument, not the push runs, which are all cancelled (main is not verified by its own CI: every run is cancelled, and 5 tests fail under ASan/UBSan #274).Owed, filed and not folded in
vt::AttentionDenseFa2still refuses head_dim 128, so LTX cannot reach tensor cores. Everything here is still a scalar warp-per-query recurrence.vt::Attentioncaller, and nobody will be told there either.All three are listed under
## Owedin.agents/specs/ltx25-dit-attn-flash.md, together with the twoscripts/attention-rung-allowlist.txtstems that #1578's checker will reportSTALEonce the markers here meet it.Closes #1549
FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]