fix(records): #1649 has two index rows, and the one its own spec never owned is the one that goes - #1742
Conversation
…r owned is the one that goes Closes #1731. Closes #1733 as a duplicate of #1731. `main` at `038ff61e5` is RED on `scripts/check-agent-record.py` -- the CI job `agent-record` -- and therefore on every session's preflight and every branch cut from it: ``` ERROR: .agents/issue-index.md: issue #1649 listed twice. Under `merge=union` a duplicate is what two branches appending the same issue look like ``` The checker's message diagnoses its own cause. `:592` was appended by `a7bb3130b` (#1633) under `ENG-HF-MODEL-DOWNLOAD`, the lane that FILED #1649. `:632` was appended by `2f2a70925` (#1701) under `GATE-WINDOWS-PORTABILITY-TARGET-SCOPE`, the lane that FIXED it. Neither lane could see the other's append, and `merge=union` combines two appends silently. ## Which row goes, and on what test Not on which row is older, and not on which reads better. On the rule `AGENTS.md` states under `## Every change starts from an issue`: the issue is linked "in three places that must agree: the index, the row's spec, and the pull request body". Exactly one of the two rows passes that test. * `:632` names `GATE-WINDOWS-PORTABILITY-TARGET-SCOPE`, whose spec `.agents/specs/gate-windows-portability-target-scope.md` reads `Issue: #1649` on its third line, and whose pull request #1701 names #1649 in its title. Three surfaces, all agreeing. * `:592` names `ENG-HF-MODEL-DOWNLOAD`, whose spec `.agents/specs/hf-model-download.md` does not mention #1649 anywhere at all -- not under `## Owed`, not in any other section. `grep -n 1649` over that file returns nothing, against a positive control that the same grep over `gate-windows-portability-target-scope.md` returns eight hits. That row's ownership claim was never corroborated by the spec it pointed at. Two further facts read the same way and are recorded so the choice can be re-checked rather than taken on trust. `:592` asserts in the present tense that "`windows-msvc-cpu` is RED on `main` and on every pull request", which #1701 made false; `:632` is a superset of `:592`'s technical content and adds the fix, a second red `:592` missed, the mutation evidence and the residual. And nothing in the tree anchors a line number into this file: `grep -rn 'issue-index.md#L'` over the repository returns nothing, against a positive control that `.md#L` anchors do exist elsewhere, so no reader and no checker is holding an offset that either deletion would move. MERGING the two rows was rejected. It edits one row and deletes the other, which is a strictly larger violation of the same rule than deleting one, and under `merge=union` an edited line is duplicated rather than merged, so the merged row is the shape most likely to come back as a third `#1649`. ## The dropped row is not lost Its four facts that `:632` does not carry -- the introducing commit `a50c57d69` and the `git log -S` that isolates it, the job that measured the red, the disambiguation from #503, #603 and #965, and how the `/w` was isolated and by which lane -- now live in `.agents/specs/gate-windows-portability-target-scope.md` section 10, which is the spec a reader reaches from the surviving row. The row's verbatim text is this paragraph: | [#1649](#1649) | `ENG-HF-MODEL-DOWNLOAD` | **`windows-msvc-cpu` is RED on `main` and on every pull request, and no compilation is ever attempted.** `scripts/build-windows-release.ps1:31` runs `scripts/check-windows-portability.py` before the build and it exits 1 with `CMakeLists.txt: MSVC /W4 /WX policy is negated on the C/C++ compile by /w`. Measured on `9f13751c3` in job [96949585684](https://github.com/mudler/vllm.cpp/actions/runs/32540549699/job/96949585684): the refusal lands seventeen seconds after `Build files have been written to`, and the log carries no `error C####` and no `error LNK####`. NOT [#503](#503), [#603](#603) or [#965](#965), which name a baseline-reporting hole, a POSIX `setenv` and a C4456 shadow respectively -- none of them a checker refusal. Isolated to ONE token: `CMakeLists.txt:2441` gives the FETCHED BoringSSL targets `target_compile_options(<vendored> PRIVATE $<IF:$<CXX_COMPILER_ID:MSVC>,/w,-w>)`, and dropping only the MSVC arm makes the same checker print `Windows portability contract OK` (probed and restored byte-for-byte against a pre-taken sha256). The `/w` is deliberate and correct -- the comment above it reads "Vendored code is not on this project's -Werror path" -- and what the checker cannot see is SCOPE: it reads `/w` anywhere in `CMakeLists.txt` as a project-wide negation, which is true of a global `add_compile_options(/w)` and false of a `PRIVATE` option on a third-party target. Introduced by [a50c57d](a50c57d69) ([#1505](#1505), row `ENG-HF-MODEL-DOWNLOAD`, [#1280](#1280)), which added the static-BoringSSL transport; `git log -S` on that generator expression returns that commit alone. NOT fixed in flow: which side is wrong is a real decision, and teaching the checker scope is a semantic checker change, which `AGENTS.md` `## Changing the rules or a checker` sends down the spec + red-before + fresh-review path. Found while repairing the fresh-review findings on [#1633](#1633), which reads the red, is not its cause, and does not touch `CMakeLists.txt`. Spec [`hf-model-download.md`](specs/hf-model-download.md) | bug | ## Exception argued, not waived This deletes a base-reachable row from an append-only file, which is exactly what `scripts/check-issue-index-append-only.py` forbids. That gate is RED on this branch and it is not being weakened. There is no waiver registry here, so the argument lives in this message, attached to the diff it excuses, as `ff264cb82` (#1025) argued the same one for the #995 duplicate. The append-only contract cannot repair a duplicate. Appending a third `#1649` row makes `check-agent-record` angrier, and the file only becomes well-formed by removing one. The two gates are in genuine contradiction on this tree, and that contradiction is the defect, not the repair. The rule is preserved in substance, verified mechanically: * 620 rows before and 620 after: one `#1649` row removed, one `#1733` row appended; the other 619 rows byte-identical and in the same relative order, and every non-row line of the file byte-identical * `#1649` appears exactly once, down from twice * no key added beyond `#1733`, and no key lost * the gate is preflight-only and absent from `.github/`, against a positive control that `check-agent-record` is there, so this costs no CI red * once this lands, a later branch diffs a `main` with no duplicate and sees no removal in its range, so the violation is confined to this commit ## The instrument, and a false measurement it produced #1733 reports that "the repair is measured and it is NOT blocked", quoting `agent record OK: ENGINE=170 MODEL=377 ...` and `OK: issue index append-only` from the same tree. Both cannot be true of one committed tree while the duplicate is base-reachable, and they are not: `scripts/check-issue-index-append-only.py:50-51` diffs `merge-base(origin/main, HEAD)..HEAD`, which reads COMMITS. Measured here at base `038ff61e5`: * deleting `:592` in the WORKING TREE only: `OK: issue index append-only` rc 0, and `git diff --numstat 038ff61..HEAD -- .agents/issue-index.md` is EMPTY * committing the byte-identical deletion: the same checker rc 1, with a `removed:` line naming the row An uncommitted mutation therefore looks exactly like a gate that cannot detect it. The `#1733` row appended here records that, because the row cannot be edited afterwards. ## Gates `check-agent-record.py` rc 1 before, rc 0 after, reporting `ENGINE=170 MODEL=377 QUANT=84 KERNEL=57 BACKEND=85 ANCHOR-ROT=37`. `check-conflict-markers.py` 0 findings over 3898 tracked text files, proved discriminating by injecting a marker into a scratch copy of `docs/USAGE.md` (rc 1, one finding) and restoring it against a pre-taken sha256. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
|
CI triage, so the red is not read as this diff's. Kept out of the body deliberately: the body is the landed commit message, and next month's reader does not need this week's runner state.
This diff touches two files, Two other knowns, for completeness. Preflight, before and after, on this exact tree:
|
Brings in #1742, which removed the duplicate `#1649` row from `.agents/issue-index.md`. `check-agent-record.py` is GREEN on this branch as a result, where it was red on both this branch and `main` before the merge. The union driver did the right thing and it was verified rather than trusted: the merged index is `origin/main`'s index plus exactly one appended line, this branch's `#1736` row. `diff` reports that single addition and nothing else, so main's deletion held and no row was resurrected or dropped -- the two failure modes a union merge of an append-only record has produced in this tree before. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
Takes main's `test_runner.cpp:1557` repair (#1602, #1608), which turns `build-test-cpu` and both `sanitize-cpu` arms green on this branch, and main's `#1649` issue-index de-duplication (#1742), which is what `agent-record` was failing on here. Neither is this row's work; both are inherited by merge. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…re, and the ratchet it named as a guard cannot see an owed issue Three findings from the fresh review of #1744. The review PASSED the narrowed key itself, so nothing here re-litigates the design. The row was in breach of the rule it is about. `AGENTS.md` requires the issue in three places that agree, and this row had it in none: the spec named #1731, which #1742 CLOSED; the index carried no row for the open issue; and the pull request body never mentioned it. #1745 is the open issue, filed against this branch by name, and it now stands in all three. #1731 stays named through the spec as the first instance and as the red that no longer stands. A guard was asserted in prose that does not exist for most of the index. §4 of the spec and the docstring of test_a_dashed_row_and_an_owned_row_are_not_a_duplicate both said the opposite order "is already gated: an added dashed row raises the unowned count and reds the UNOWNED_HIGH_WATER ratchet". It does not, whenever any spec lists the issue under `## Owed`, because check_issue_index counts a dashed row only `if row_id is None and number not in owed`. Measured on this tree: 328 owed numbers, and 226 of 621 rows carry both an owner and an owed issue. Probing the real index with a dashed #168 row leaves this branch's checker silent at rc 0 with the unowned count still 33, while the contrast row #618, an issue no spec owes, reds it with `34 rows name no owner`. The index was restored byte-for-byte. The exemption is `AGENTS.md` working as written, so the sentences are corrected and the missing ORDERING rule is #1749, owed in the spec rather than built here: it is a second checker semantic in a change that owes one. Nothing proves the CLI runs check_issue_index. Its sole production call site is unguarded inside main(); replacing it with `pass` leaves the suite at 113 tests OK and makes the command accept a byte-identical duplicate row, the exact #1619 corruption the pristine tree refuses at rc 1. Inherited rather than introduced, since `git log -S` on that call returns one commit and this change touches only the body of the function, so it is filed as #1750 and owed, not repaired here. The docstring repair moved two symbols other records cite, and the record-anchor ratchet caught it in the same run: `stale: 33 > baseline 31`. The two ENG-RECORD-ANCHOR-RATCHET citations advance to where the symbols now are, and the baseline is untouched. The three appended index rows carry no `path:line` citation at all, because the index cannot be edited and a stale anchor written into it can never be repaired; they name symbols, and the line numbers live in the spec. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…h shapes, both MEASURED and both WITHDRAWN (#1727, #1728) (#1712) **Nothing here touches assertion (1c).** `tests/vllm/multimodal/test_ltx2_video.cpp` is byte-identical to `main`, #1572, #1576 and #1536 stay open, and the bound still carries the fourth shape's flat 30 ms constant. What lands is the record of two further shapes that were built, measured and withdrawn, so that the next person does not build them again. That is a reversal of what this pull request set out to do. The reason is in the numbers below. ## Four shapes, and what each one is measured to do | shape | denominator | how it fails | measured | |---|---|---|---| | 4th, on `main` | a flat 30 ms per leaf record | reds an unmutated tree under load, and cannot resolve `artifacts.frames` at all | #1576, 171x on one binary | | 5th | `4 x` the WORST boundary a 1 kHz sampler saw across the WHOLE case | one scheduling event on the sampler thread multiplies the bound by ~100 and silences a real 20 ms un-named phase | a fresh review: the defect reds 9/9 alone, **0/4** with one 200 ms sampler stall, whole case **GREEN 2/4** | | 6th | `4 x` the worst boundary inside the RECORD'S OWN window | too tight -- the honest span slack's tail exceeds it | **5 red in 45** consecutive runs of the unmutated case on that mechanism, loadavg 21.8-61.5 (10 in total; see below) | | next | red only when the SAME leaf exceeds it in BOTH renders | untried | recorded, not attempted | ## The fifth shape: right derivation, wrong estimator A fresh review confirmed the derivation operation-by-operation against `render_phase_log.cpp`: `Open` takes the mutex, stamps, samples and flushes a line; `Close` takes the same mutex, samples, then stamps and flushes. Head plus tail really is two boundary-sized operations and `2 x ceiling` really is what the structure gives. **The estimator is what fails.** Read over the whole case, that maximum is drawn from **3,764 to 27,975 draws** while the numerator it bounds is a maximum over **TWO**, and maxima do not compare across sample sizes. Over 24 unmutated runs the sampler's MEDIAN operation measured 18.4-22.9 us -- stable to 1.24x across loadavg 25 to 82 -- and its MAXIMUM 47x to 785x that median. | arm | what it stages | (1c) red | case verdict | |---|---|---|---| | `C20` | a 20 ms un-named phase in `decode.audio`'s head | 9 of 9 | red 9 of 9 | | `C20D2` | the SAME defect + ONE 200 ms descheduling of the sampler thread | **0 of 4** | **GREEN 2 of 4**, bound 0.800 s | A real un-named phase, no assertion fires, and the only thing that made it pass is a scheduling event on a sleeping background thread. The same review found the skip path was a **permanent report-only state**, which `AGENTS.md` `## Gates` refuses in as many words: `span_checked` was computed, printed and asserted nowhere, and CI runs `ctest --output-on-failure`, so on a green run nobody ever sees it. Four `CheckCarryingPhase` calls reported `1 of 1`, `0 of 2`, `0 of 1`, `0 of 1` while the case exited `Status: SUCCESS!` with the 20 ms defect present. ## The sixth shape: better detection, and it reds an unmutated tree Each draw stamped with `PhaseLog::Elapsed()`, and a record's bound reading only the draws inside that record. It is a strict tightening, and it detects better than anything before it: | mutation | 30 ms constant | 5th shape | 6th shape | |---|---|---|---| | 2 ms un-named head in `decode.audio` | 0 of 4 | 1 of 4 | **3 of 3** | | 15 ms un-named head in `decode.video` | 0 of 4 | 5 of 5 | **3 of 3** | | 20 ms + a 200 ms sampler stall | -- | 0 of 4 | **3 of 3** | `artifacts.frames` -- the leaf #1470 is about, 0.9 to 61 ms, which no previous shape could resolve at all -- came out bounded at **71.9 us**. **And then it was validated the way this cluster's own rule demands.** 45 consecutive runs of the containment case, one pinned binary (sha256 `242c7925...`), an unmutated tree, loadavg **21.78 to 61.52**, every run's counts recorded: ``` 45 runs 35 green 10 RED ``` **The headline 10 is not the red rate of the shape's final form, and the honest figure is 5.** Two mechanisms produced those ten, and the second was identified and repaired while the population was still running -- so its nine reds were measured on a binary that predates its own repair, and that repair was never re-validated. **11%, five reds in 45, is the defensible number**, against a measured 1 red in 120 (0.83%) for what is on `main`. It is 13x, it is decided by the same mechanism at any multiplier, and it is what the withdrawal rests on. The nine are reported rather than dropped, because a population that reports only its surviving mechanism is an argument and not a measurement. | assertion | reds | what happened | |---|---:|---| | `span_slack <= span_bound` | 5 | the honest head-and-tail exceeded `4 x` the record's own local worst boundary: `decode.audio` 13.115 ms vs 8.889 ms, `decode.video` 0.488 ms vs 0.374 ms, `artifacts.frames` 70.6 us vs 60.6 us | | `longest_checked` | 9 | a leaf's longest record was not resolvable, most often `artifacts.frames` at ~1 ms with no draw of a 1 kHz sampler inside it | The second was repaired mid-flight -- a record shorter than the sampler's cadence takes the worst of the draws bracketing it. **The first is not repairable by choosing a different denominator**, and it is the same mechanism `.agents/specs/ltx25-phase-residue.md` `## Design` 3 records for the withdrawn residue bound: the part of a boundary the instrument cannot measure -- the `lock_guard` release, the `Close` return, the `Scope` destructor and constructor, the call into `Open` up to its clock read -- dilates faster under contention than the part it can. It sits in the numerator and in no denominator a sampler can build. Raising the multiplier would be repairing a red gate by widening it, which `AGENTS.md` `## Changing the rules or a checker` refuses in as many words. (#1668 is not cited for that and does not say it; what it says, quoting this file's own comment, is that naming the un-named time would settle it properly -- the repair is an ANCHOR, not a threshold, which points the same way by a different route.) So both shapes are withdrawn. ## The next traceable hypothesis, which this row does NOT take A defect is REPRODUCIBLE within a run and a scheduling event is not. The fixture renders the same three carrying leaves TWICE. A bound that reds only when the SAME leaf exceeds it in BOTH renders would keep every detection measured above -- the injected sleeps run on every pass -- while dropping every red measured here: in all five span-slack reds the failing leaf exceeded the bound in exactly one render. **Its cost is named before anybody builds it:** requiring both renders silences any real defect present in only ONE of them -- a first-render lazy allocation, a cold-cache path, anything the second pass warms. On this fixture the three carrying leaves run the same code on both passes, which is why the staged sleeps red 3 of 3, but that is a property of the fixture and not of the assertion. That is a different assertion with a different failure mode. It owes its own red-first evidence, its own mutation set, its own 45-run validation, and a mutation present in render 1 alone. Attempting a fourth shape in the same session that refuted the second and third is how this cluster got four constants in the first place. ## Issues **Closes nothing.** #1572, #1576, #1536, #1470 and #1439 all stay open, and #1536's thesis is not refuted here either -- that claim depended on the fifth shape landing. Filed by the fresh review and left open as **constraints on any re-proposal**, each owned by this row's spec `## Owed`: * **#1727** -- two of the boundary sampler's three terms are unfalsifiable by a gate (deleting the flushed write leaves the estimator inside its own noise, green 3 of 3), and the estimator is unmeasured under the sanitizers and in the FULL 102-case binary, which is where #1576 measured the 171x swing that started this. * **#1728** -- a HYPOTHESIS, unmeasured: on a ROCm runner a 1 kHz boundary sampler would drive `hipMemGetInfo` under the process-wide phase mutex. `CudaBackend` does not override `DeviceMemoryInfo` (#1126); `src/vt/rocm/rocm_backend.hip:373` does. ## Baseline **Inheritance is read by FAILURE TEXT, not by job name, and the inherited set MOVED while this branch was open.** At `019f66c1a` `main` was red on `build-test-cpu`, both `sanitize-cpu` arms and both `windows-msvc-*`. Three of those five are GREEN here now: `main` landed the `tests/vllm/v1/worker/test_runner.cpp:1557` repair (#1602/#1608) and this branch takes it by merge, not by any edit of its own. **`agent-record` was red here and is not this row's.** Its failure text was `ERROR: .agents/issue-index.md: issue #1649 listed twice` — a `merge=union` duplicate this branch filed as #1733 rather than repairing, because `AGENTS.md` says never edit a row and never delete one. `main` repaired it in `6354755ba` (#1742), #1733 is CLOSED, and this branch inherits the repair by merge. That one was worth reading rather than waving through: a records gate failing on a records-only branch is exactly where a real failure would hide behind an inherited one. The `record anchors ... -> rot 37` line beside it is the rot budget being MET — `check-agent-record.py` prints `ANCHOR-ROT=37` and exits 0 on `main` and here alike. What is left is two jobs, and BOTH are inherited, filed and owned. Each is named by FAILURE TEXT and cited to the issue that MATCHES that text, because "the standing Windows red" is not one stable thing and a records gate turning red on a records-heavy branch is exactly where a real failure would hide behind an inherited one. **`agent-record` -- an ABSENT INSTRUMENT reading as a code verdict.** The text is `ERROR: test_rendered_benchmark_index_links_resolve_to_emitted_pages ... FileNotFoundError: [Errno 2] No such file or directory: 'hugo'` / `FAILED (errors=1)`. `tests/scripts/test_check_site.py` invokes `hugo` through `subprocess.run` with no `shutil.which`, no `skipTest` and no `FileNotFoundError` handler, and the job never installs it. That is [#1722](#1722), open since 2026-08-22, with the repair in flight as [PR #1726](#1726). [#1754](#1754) is a later DUPLICATE of the same defect -- same file, same missing guard -- filed six hours after #1722 and citing neither it nor #1726; the original is cited here on purpose, because a squash body is the landed commit message and a pointer to a duplicate cannot be repaired afterwards. It is a LAYERED failure, which is why it looks new: until `6354755ba` the same job exited earlier on `.agents/issue-index.md: issue #1649 listed twice`, so nothing downstream of that line had been reached. The record checkers themselves are FINE -- `check-agent-record.py` prints `ANCHOR-ROT=37` and exits 0 on `main` and on this branch alike, the rot budget MET rather than exceeded, and `audit-live-rows.py` passes -- verified locally on the merged tree rather than inferred from a job colour. **`windows-msvc-cpu` / `windows-msvc-vulkan` -- [#584](#584), and NOT [#1649](#1649 #1649 is the pre-compilation refusal `CMakeLists.txt: MSVC /W4 /WX policy is negated on the C/C++ compile by /w`, and [PR #1701](#1701) rescoped that gate on `main` at `2f2a70925`. The lanes now get PAST `Windows portability contract OK` and die twenty-eight minutes later at `test_openai_api_server.exe exited with status -1073740791` -- `0xC0000409`, `STATUS_STACK_BUFFER_OVERRUN` -- immediately after a `decoder prompt (length 40) is longer than the maximum model length of 32` refusal. That is #584, already open, and its history repeats: it was filed when an earlier fix first let that binary run on Windows, #1649 then re-masked it, and #1701 has unmasked it again. **A post-#1701 Windows red citing #1649 is a false attribution**, so this body does not make one. Nothing in this change touches `src/vllm/entrypoints/`, and the length guard in that log is [#1675](#1675), already present in this branch's merge base. Two branches sharing no diff reproduce the crash byte-identically, so it is attributable to no current row. `windows-msvc-*` is also SKIPPED on `main` ([#503](#503) -- the jobs are pull-request-only), so inheritance for it cannot be read off a main baseline at all and is established from failure text plus the owning issue instead. `tests/scripts/test_cpu_x86_llamacpp_floor.py` refuses locally under box load with `NO_QUIET_WINDOW after 30s (busy=105% builders=0 load=60.83)` and passes once the box is quiet (`Ran 10 tests in 186.719s / OK` at loadavg 35). That is the harness's own quiet-window precondition, not a verdict. ## This is records-only, and the invariants say so `git diff --stat <merge-base>..HEAD -- src/ include/ tests/ docs/ scripts/` is **EMPTY**, and the net diff is exactly three `.agents/` files. The comparison is against the MERGE BASE rather than against `origin/main` deliberately: `main` moved to `b508cbce6` while this branch was open, and a diff against the moved tip reports THAT commit's `docs/FEATURES.md`, `docs/models/qwen3-8-27b.md` and `tests/vt/test_ops_matmul_fp8_block_cuda.cpp` changes INVERTED, as though this branch were reverting them. It is not; it is one commit behind, and `b508cbce6` touches neither ltx25 spec nor the issue index. `.agents/issue-index.md` is `+2` with **zero removals**: `#1727` and `#1728` each appear exactly once, they are the last two rows, and no pre-existing row is modified -- checked row-by-row against `origin/main`, not by reading the diffstat. **It composes with [#1711](#1711), and that was PROVED rather than assumed.** Both pull requests edit `.agents/specs/ltx25-phase-residue.md` and `git merge-tree` reports CONFLICT -- but the conflict is POSITIONAL, not semantic: #1711 edits rows `#1569` and `#1571`, this one edits `#1572` and `#1470`, and they collide only because `#1571` and `#1572` are adjacent lines. That table is a KEYED RECORD, so `AGENTS.md` `## Records` forbids resolving it by an automatic three-way merge. The resolution takes the complete target-branch file and re-applies this branch's two row edits by exact unique anchor: all four edits present, and the six untouched rows byte-for-byte identical. 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>
…t decides whether the CUDA arm is WORSE or only different (#1736) (#1738) W0g excluded three causes of the CPU-against-CUDA token divergence on `Qwen3.8-2.4T-A95B UD-Q1_0` and named none. It left the question G0-CORRECT actually turns on unanswered: **is the CUDA arm WORSE, or only different?** This lands the pre-registration for the experiment that answers it. It adds no product code, takes no lease, and measures nothing. Issue [#1736](#1736). Wave **W0h** of `ENG-EXPERT-STREAM-DEVICE` ([#1124](#1124)). Spec `.agents/specs/cuda-arm-degradation-experiment.md`. ## Why the question is open **The CUDA continuation degenerates.** The two arms agree for 8 tokens, then the CUDA text falls into a mechanical recursion in which each sentence re-uses the previous object. A coin flip between two equally good tokens does not produce that. One continuation is one sample, which is why the design uses a corpus. **Every comparison so far is arm-against-arm with no oracle.** Neither arm is ground truth, so "they differ" structurally cannot say which arm is wrong. `AGENTS.md` makes vLLM the reference and not our CPU arm, and vLLM cannot run this checkpoint. **The growth-rate argument does not survive a proper fit, and this spec records the correction rather than the claim.** The reading offered was: 0.56 % to 13.4 % is 23.9x over 91 blocks, uncorrelated rounding accumulates as `sqrt(91) = 9.54`, so 23.9x sits above the noise prediction. A two-point reading is not the test. A least-squares fit of `log(divergence)` on `log(block + 1)` over all eight recorded points gives an exponent of **0.651**, standard error **0.066**, 95 % interval **[0.489, 0.813]** — which **includes** the 0.5 a random walk predicts and **excludes** the 1.0 a systematic error predicts. Two further limits travel with that table: its points come from one prompt and one prefill, and its statistic is a mean of per-element ratios, which `.agents/benchmark-record.md` already warns is not comparable with the ratio of means it reports twelve lines above. The ramp is therefore carried as a pre-registered test to re-run with more data, not as evidence. ## The design **Teacher forcing, through a seam that already exists.** Both arms are fed the identical token sequence instead of each generating its own, and the harness records the negative log likelihood each arm assigns to the corpus's next token plus the cross-arm logit delta. That removes the confound every earlier comparison carried: the arms currently walk different trajectories after the divergent token, so "they diverge" mixes per-step numerical error with trajectory drift. The instrument is the ABI logits processor (`include/vllm.h` v8), applied at vLLM's non-argmax-invariant stage, whose single call site is `src/vllm/v1/sample/sampler.cpp:441`. The harness is therefore an ABI client with no internal header, and the engine is driven through `vllm_complete_tokens`, so nothing re-tokenizes at run time. **That seam is currently defective on the arm under test, and the spec says so rather than claiming the opposite** — see the review-repair section below. **NLL needs no oracle.** Two equally faithful arms assign nearly the same likelihood to the same held-out text, so a materially higher CUDA NLL means worse and not different. The forced continuation is natural text and never either arm's own generation, because forcing an arm onto its own greedy output hands that arm the maximum-probability token at every position by construction. **Corpus: 16 prompts plus 1 labelled anchor, 20 forced positions each**, with the id arrays pinned verbatim in `benchmarks/w0h_corpus.json`. The anchor is the W0g prompt and is excluded from the primary statistic, because it was chosen for its outcome. ## The pre-registered decision rule Written before any measurement, and the spec is the binding copy. * **R1, primary and binding.** `delta = mean(NLL_cuda - NLL_cpu)`, paired bootstrap over PROMPTS. **DEGRADED** when the interval excludes 0, `delta > 0` and CUDA is higher on `C >= 12` of the 16 prompts. That clause is a one-sided sign test at `2517 / 65536 = 3.84 %`, chosen because `C >= 11` gives 10.5 % and `C >= 13` gives 1.1 %. **INCONSISTENT** is a reported outcome and is never rounded to either side. **NOT-DISTINGUISHED needs the interval INSIDE an equivalence margin `M`, not merely containing 0**, and an interval that contains 0 and is wider than the margin reads **UNDERPOWERED**. * **R2.** Binomial sign test on the per-position logit deltas over the CPU arm's top-64. **SYSTEMATIC** when more than 5 % of positions reject at `alpha = 0.01`. **UNDETERMINED** when every scored position was fully tied, so `R` has no denominator. * **R3.** The depth exponent above. **SYSTEMATIC** when the interval excludes 0.5 **and lies above it**, **SUB-RANDOM-WALK** when it excludes 0.5 and lies below it, **ACCUMULATION** when it includes 0.5 and excludes 1.0. * **R4, materiality.** Measurable only against the oracle arm, whose `S = |NLL_cpu - NLL_llamacpp|` is the measured scale of "two faithful implementations disagree by this much". **Without the oracle no anchor exists and none is invented**: R4 reads PENDING on #933, `M = S` is unavailable, and R1 stands alone as a directional result while NOT-DISTINGUISHED is unreachable. * **R5.** Composition into DEGRADED, NOT-DISTINGUISHED or UNDETERMINED, with each verdict's consequence for the row fixed in advance. **Only R1 and R2 compose**; R3 and R4's verdicts are reported-only, and both rows say so. ## Oracles The oracle arm is `llama-cpp-unsloth` at `36fe8e1cc`, the only tree that defines the `IQ1_XXXS` encoding this checkpoint stores about 97 % of its parameters in. Its record reads `gateable = no`, owed by [#933](#933), and its own file says running it wants 450 GB of RAM against a 119.631 GiB box. A bounded probe (P2) decides whether it runs, and the spec states plainly what its absence costs: R1 still returns a directional verdict, R4 has no anchor, and a defect **shared** by both arms stays invisible. **`llama-cpp` flipped to `gateable = yes` on 2026-08-23 (#1740) and that does not make this arm gateable.** That demonstration ran `Qwen3.8-27B-Q4_K_M.gguf`, a 17 GB Q4_K_M artifact, and the pin's highest ggml type is `GGML_TYPE_Q2_0 = 42`, so it cannot read the type 66 this checkpoint stores its experts in. Gateability is recorded per oracle, and the fork's own pin block still reads `gateable = no` / `evidence = #933`. The spec carries this explicitly, because without it the next reader sees "llama.cpp is gateable now" and treats R4 as live. **The in-tree oracle switch is excluded on arithmetic re-derived here rather than copied.** `VT_CPU_REF=1` forces every tensor to `kExpandBf16` (`gguf_keep_quant.cpp:157`). Whole file at the expert bpw: `369.97 GiB x 16/1.1875 = 4,984.9 GiB = 4.87 TiB`, 41.7x the box. By parameter: `2.4T x 0.97` experts at bf16 plus the rest is `4,470.3 GiB = 4.37 TiB`, 37.4x. Both land in the same place, and the first slightly overstates because the roughly 48.1 GiB outside the expert towers is not stored at 1.1875 bpw. ## Constraints the spec carries Clock state is sampled and recorded per arm through `tools/bench/gpu_clock_state.py`, which W0g did not use. Pinning is unavailable to this row: `nvidia-smi -lgc` returns `LGC_RC=4` in a lease (#1354), and the host path's authority is recorded against another campaign in an untracked preferences file this checkout does not have, so that gate stays PENDING rather than being read out of a document. A clock excursion cannot bias an NLL result, and the record is still taken. The lease is `rc run` and not `rc hold`, so the release lives in the tool rather than in a plan. Weights come from local NVMe. The build happens inside `vllmcpp-build:gb10` at `-j 4`, and no script calls `/usr/bin/time`, which that image does not have. **The per-token slice figures are corrected, and the correction is re-derived from the W0g dump rather than taken on report.** The engine-matrix row says "2790 slices per token, 6.95 GB", which counts 93 MoE blocks. The dump is self-verifying: `8 + 184*44 + sum(T)*17488 = 9,661,480` gives `sum(T) = 552`, `552 = 92*5 + 92*1` fixes **92** blocks over one 5-token prefill and one decode step, and `17488` fixes the top-10 of 512 at `H = 8192`. So the count is `92 x 10 x 3 = 2760`, the demand is **6.873 GB**, and at the measured **43.4 %** decode hit rate the bytes actually **read** are **3.89 GB** — 56 % of the demand, and the figure that sizes the storage path. **Two premises are named as deliberately unused**, so their absence is not read as an oversight. The lane is not assumed I/O bound: on the repository's own QD1 curve at 2.76 GB/s single-threaded the read term is about 1.41 s of a 9.055 s CPU step, near 15.6 %, so read-issue order can move at most about 0.64 s. And nothing rests on routing skew or static prefill-chosen expert pinning, which FreeToken's trace replay (arXiv 2608.16157, Fig. 4b: 59 % miss for static prefill-chosen pinning against 39 % for demand-driven LRU at equal capacity) and a 20.48 GiB pin cost both argue against. Neither enters any result here. The I/O sentence is stated as the whole read term and not tighter: at 2.76 GB/s the term is `3.890 / 2.76 = 1.409 s` of a 9.055 s CPU step, `15.6 %`, and read-issue order can move at most that, because the best a perfect ordering can do is remove it entirely. An earlier draft claimed a `0.64 s` bound; nothing stated yields that number, so it is withdrawn rather than re-justified. **No speed claim. G0-SPEED stays VOID under every outcome.** ## Why a wave and not a new row `ENG-EXPERT-STREAM-DEVICE` `## Owed` already owns this question in two entries, and G0-CORRECT is that row's gate. A second row would give one obligation two owners, which is the #777 and #1731 shape, and would store a verdict about one row inside another. The design lives in its own spec file so the pre-registered rule is auditable by `git log --follow` on one path; the tree carries three precedents for a spec with no matrix row of its own. ## What is NOT in this change The harness, the corpus file and their tests, which are a separate dispatch against this spec, as `AGENTS.md` §`Spec before code` requires. Localizing the first differing operation inside block 0, which the row's `## Owed` already owns. Ratifying a distributional gate, which `AGENTS.md` reserves for the operator. Generalizing the method to `A2-Q1`, which has the same shape and would be speculation before this runs once. ## The fresh review returned FAIL, and this is the repair The arithmetic audit found **all fifteen re-derivable numbers correct** and the decision rule's cell structure exhaustive, so **no threshold moves**. What moved is the set of ways this design could reach a favourable verdict without earning one, plus one guarantee the spec asserted that is false on the target hardware. Nothing has run, so nothing here is a threshold set against a number; the spec's `## Now` records the repair and keeps the audit test where it was, at the run date. **The instrument the whole design rests on is broken on the arm under test, and the spec claimed the opposite.** It said the seam is "host-addressable by construction". The chain, read rather than assumed: `apply_logits_processors` gates on `b.UnifiedMemory()` (`builtin.cpp:93`) and then casts the device pointer to a host pointer (`:98`); `CudaBackend` is constructed with `caps.pageable_memory_access && caps.integrated` (`cuda_backend.cu:363`), so that predicate is TRUE on GB10; `CudaBackend::Alloc` is nevertheless a plain `cudaMalloc` (`:80-82`), and CUDA never overrides `DeviceMemoryIsHostAddressable()`, leaving the base `false` (`backend.h:77`). `op_provider.cpp:866-873` documents this exact class beside the narrow predicate and records that asking the wide one "COST TWO CRASHES (#844, #1435)", plus \#960. The product repair is issue [#1746](#1746) and a separate implementer owns it; **this wave lands no product code**. The spec now states the real position, adds **P6** to refuse the run until that repair is an ancestor of the recorded source SHA, and names #1746 as a **prerequisite** rather than a follow-up. The W0e SIGSEGV in `## Owed` is no longer "still unexplained": #1746 is **a candidate cause of the same class**, and it is stated as a candidate because nobody has re-run W0e against it and the completion callback is a different seam. **A precondition was credited with a check it cannot perform.** The corpus-contamination risk read "probed by P3", and P3 asserts that the emitted ids equal the corpus ids — which is exactly what forcing guarantees, so P3 passes **by construction** on a corpus built from an arm's own generation. That is the dangerous shape: it fails toward a confident DEGRADED rather than toward a void run, and the recorded provenance is no substitute, because a sha256 pins the file against itself and not against its origin. **P7** now probes origin: fetch the pinned source at its pinned revision, detokenize every item, and require a contiguous, in-order, non-overlapping span of that source. A failure voids the run. **Four sampler stages upstream of the instrument were never pinned.** NLL is read from "the unmodified logits row", but `sampler.cpp:425-440` runs `allowed_token_ids`, `bad_words`, `min_tokens` and `logit_bias` **before** `apply_logits_processors` at `:441`; only the penalties, which run after, were pinned. Two of the four early-return on empty inputs (`builtin.cpp:18`, `:46`) and the other two are guarded at the call site, so the defaults are genuine no-ops — which is precisely why they are pinned in `## Constraints` and asserted in P1 rather than changed. A default is not a probe. **Three verdict cells could be reached the easy way.** * **R1** read NOT-DISTINGUISHED on any interval containing 0, so an underpowered run at `P = 16` mapped straight onto the verdict that opens the distributional-gate door. It now needs the interval INSIDE an equivalence margin, TOST-style, and the margin is R4's **measured** `S` rather than a number picked from nothing. The consequence is stated plainly instead of discovered later: **while R4 is PENDING on #933 there is no margin, so NOT-DISTINGUISHED is unreachable** and an interval containing 0 reads UNDERPOWERED, which composes to UNDETERMINED. The minimum detectable effect is pre-registered as `t(0.975, P-1) x sd / sqrt(P)` — `2.13145 / 4 = 0.533 x sd` at `P = 16`, and `2.20099 / sqrt(12) = 0.635 x sd` at the `P = 12` floor, so the floor costs 19 % of the resolution. * **R2's** 0.05 bar is 7.19 standard errors above the 0.01 null **only if the 320 positions are independent**: `sqrt(0.01 x 0.99 / 320) = 0.00556`, `0.04 / 0.00556 = 7.19`. At the other end, one effective position per prompt gives `N_eff = 16`, `SE = 0.02487` and `0.04 / 0.02487 = 1.61`. The bar is somewhere in between, that is an assumption, and it is now labelled one and made falsifiable: the run must report `rho`, `deff = 1 + 19 x rho`, `N_eff = 320 / deff` and the implied distance beside `R`. R2 also gains the `n = 0` cell it left undefined, which is live rather than theoretical because W0g measured an exact bf16 tie in this system. * **R3's** SYSTEMATIC cell excluded 0.5 in **either** direction, so an interval entirely BELOW 0.5 — errors cancelling — would have read SYSTEMATIC and inverted the prose beneath the table. It now needs to lie above, and the below case gets its own **SUB-RANDOM-WALK** cell. Applying R3's own cells to the eight recorded points gives **ACCUMULATION**, not the UNDETERMINED the draft asserted — a misapplication of the rule to its own data, corrected here. * **R3 and R4 are now stated as reported-only in both R5 rows**, not only the first, and R4's measurement is distinguished from R4's verdict, because `S` does reach R5 through R1's margin. **One number is withdrawn rather than re-justified.** "Read-issue order can move at most about 0.64 s" was the only figure the review could not reproduce, and nothing this spec states yields it. **The `#1736` index row moves to the true tail, and measurement showed that is possible.** At the reviewed head the union driver had placed it at `:638` with `#1733` at `:639`, so `AGENTS.md`'s "append a row at the end" was wrong in the word while the merge was right in substance. Against the merge base the move is a **pure append**: `git diff --numstat 6354755..HEAD -- .agents/issue-index.md` reads `1 0`, with zero removed lines, `scripts/check-issue-index-append-only.py` rc 0 and `scripts/check-agent-record.py` rc 0. No other row is touched: 621 rows, no duplicate row key anywhere in the file, and `#1649` still reads exactly 1. `scripts/agent-preflight.sh` on this head is **rc 0, 0 gates failed**. `test_cpu_x86_llamacpp_floor` — the load-dependent case [#618](#618) names — passed on this run without needing the serial re-run. `check-agent-record` is now **green**. It was red for the duplicate `#1649` index rows on this branch and on `main` alike until [#1742](#1742) removed the extra row; this branch merges that fix, so the record gate went from red-inherited-from-main to green rather than merely not-worse. **CI's one red, `agent-record`, is inherited from `main` and is an absent instrument rather than a code verdict.** The failing step is "The docs site's content invariants hold". `scripts/check-site.py` itself printed `site OK: 12 published docs, nav in bijection`; what failed is `tests/scripts/test_check_site.py::test_rendered_benchmark_index_links_resolve_to_emitted_pages`, which shells out to `hugo` with no availability guard and raises `FileNotFoundError: [Errno 2] No such file or directory: 'hugo'` on a runner that has no hugo. That is [#1722](#1722), the same class as #961 and #1661, and the repair is already in flight on [PR #1726](#1726) — so this branch does not touch it. Three things make that a reading rather than a plea. `main`'s own CI fails the identical step (run `32608320394`, job `agent-record`). This branch changes three files, all under `.agents/`, and none is read by that test. And the test passes locally, where `hugo` is on `PATH` — which is the positive control that the difference is the binary and not the tree. **The union merge of `.agents/issue-index.md` was verified rather than trusted.** `merge=union` has twice in this tree produced a clean merge that was wrong -- it can resurrect a line the other side deleted, and it can drop a tail row. Here main deleted a row and this branch appended one, which is exactly that shape. The check: the merged index is `origin/main`'s index **plus exactly one line**, this branch's `#1736` row, with `diff` reporting that single addition and nothing else. `#1649` reads 1 and `#1736` reads 1. **The merge commit's own body called that line "appended" and at that commit it was not the last line**, because the union driver ordered ours before theirs; the review caught the word, and the review-repair commit moves the row to the true tail so the word and the file agree. 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>
…l contract without which nothing could fill it (#1124) (#1735) `--device cuda` serves `Qwen3.8-2.4T-A95B UD-Q1_0` today only where the platform's kernels can dereference host storage, because the only production `ExpertSlotStore` keeps its slots in a `std::vector<uint8_t>`. That is one part, a GB10. A discrete device cannot read that arena at all, so for it the slice has to live in device memory. `DeviceExpertSlotStore` is that store: one contiguous arena through `vt::Backend::Alloc`, plus one pinned host staging slot. ## The fill contract is not a separate wave, and could not have been The class could not be filled by ANY caller before this change. `ExpertStreamer::EnsureFile` hands `SlotForWrite()`'s pointer straight to `::pread`, and `vt::Backend::DeviceMemoryIsHostAddressable()` is false for CUDA, so a device slot pointer is not a legal `pread` destination. Scheduling the fix as a wave after W1 would have scheduled a wave that deadlocks its predecessor. The spec says so under "Verdict on issue #1124's piece 3" and folds it in here. So `ExpertSlotStore` gains `CommitSlot(int32_t, size_t)`. `SlotForWrite` returns a host-writable destination, `pread` fills it exactly as it does today, and `CommitSlot` publishes it. On the host store that is a bounds-checked no-op over a `SlotForWrite` that still returns the slot itself, so the host path is byte-identical, which is this wave's stop condition. On the device store `SlotForWrite` returns the single pinned staging buffer and `CommitSlot` performs one contiguous H2D followed by a queue synchronize. Three decisions inside that, recorded because the diff does not carry a reason. **`CommitSlot` is PURE, not a defaulted no-op.** A default is correct for exactly one implementation, the host one, and silently wrong for every store whose slots the host cannot write — the entire population it was added for. Its failure presents as zeros in a slot rather than as a compile error, and that failure is literally the RED this change was gated on. The cost is two overrides: the host store's no-op and `test_expert_streamer`'s `RecordingStore`, which now counts the calls. **The publish call sits INSIDE `EnsureFile`'s existing `try`.** Publishing is the last step of the fill and it can fail for the same class of reason the read can. A device copy that throws would otherwise leave the slot holding whatever it held before, under a cache entry that already claims the key is resident — the same silent, plausible and wrong outcome the loop is wrapped for. It takes the same `Invalidate` undo. **ONE staging slot, not one per slot.** The filler is synchronous by design (`expert_streamer.h`: no async I/O, no prefetch, no read-ahead; overlap is `ENG-EXPERT-STREAM` W6 and is conditional on a measurement), so exactly one fill is ever in flight. A buffer per slot would double the arena's host cost — 18.55 GiB on the target checkpoint — to buffer a concurrency that does not exist. The single buffer is also what makes the write/commit pairing a contract: `CommitSlot` refuses a slot that is not the one `SlotForWrite` last handed out, because with one buffer that mismatch files one expert's weights under another expert's key and the cache then reports a HIT over the wrong bytes. ## Why a staging bounce and not `cuFile` A zero-copy filler (GPUDirect Storage / `cuFile`, or `O_DIRECT` DMA into a device BAR mapping) moves fewer bytes and needs a driver capability probe, a mount-level check, an aligned-I/O path and a fallback for each of those. The bounce costs one extra host-to-device copy of one slice per MISS on top of a disk read of the same size, lands in one wave, and keeps the zero-copy filler genuinely optional rather than load-bearing. It is chosen for that reason and not because it is faster. The measurement that would justify replacing it — a device-arm decode where the H2D leg is a measurable fraction of fill time — does not exist until W1 has run somewhere, and it is recorded under the spec's `## Owed`. ## G1, red-first `DeviceExpertSlotStore` driven through `ExpertStreamer::EnsureFile` produces byte-identical slot contents to `HostExpertSlotStore` on the same input, on a CPU `vt::Backend`. No GPU was leased; the gate does not need one and no discrete NVIDIA GPU is reachable from this project, which is why G-DISCRETE stays owed. There is no upstream: pinned vLLM `555967922` has inference-time expert paging nowhere, so the host store is the oracle and the spec already says so. **RED**, with everything present except the streamer's publish call — which is exactly #1124's third piece: `test_device_expert_slot_store` exit status **1**, `Status: FAILURE!`, compile status **0**, `grep -c 'No space left on device'` **0**. All four slices failed both halves of the comparison, against the host store AND against the file, because the bytes sat in staging and never reached the device slot. **Re-measured at the head of the second review's repair, where the suite is 10 cases / 112 assertions rather than the 9 / 97 the first offering had: 10 cases / 2 failed, 112 assertions / 18 failed.** **GREEN**, with the call: exit status **0**, **10 cases / 10 passed**, **112 assertions / 0 failed**. `test_host_expert_slot_store` 9 cases / 203 assertions / 0 failed, unchanged in count from before the contract change. `test_expert_streamer` 12 cases / 269 assertions / 0 failed. `test_expert_stream_wiring` 4 cases / 882 assertions / 0 failed. The comparison asserts equality with the FILE as well as with the host store, and **its stated reason has now been wrong twice**. The first draft said two empty arms would pass a bare `memcmp`; the first review caught that (F2) — the host arm is filled by its own streamer and is non-zero, so the host-versus-device comparison reds on its own, measured rather than conceded: with both file `CHECK`s deleted AND the H2D copy deleted the suite is still **RED at 10 cases / 3 failed, 104 assertions / 10 failed, rc 1** (re-measured at this head). The replacement reason was "it makes the red DETERMINISTIC", and the second review showed that one wrong too (F5): the arena prefill described under **F4** below now does that job, and determinism was never the stronger ground. **The ground that holds is the shared helper.** Host-arm against device-arm is a comparison between two runs of the SAME `ExpertStreamer`, over the same descriptor, at the same `file_offset`. A streamer that read the wrong offset, read short, or read one slice twice makes both arms identically wrong and passes it. The bytes on disk are the only input neither arm computed, so the file `CHECK` is the only assertion in the case that can see that class of defect. It is kept, with the reason replaced rather than the check deleted. The two structural claims a CPU tier could otherwise fake — that `SlotForWrite` hands out staging rather than the slot, and that `SlotForRead` hands out the slot rather than staging — are asserted as pointer relationships, because on a unified allocator both spellings produce the right bytes. **"Byte-identical" is over the filled prefix** (F3). The host arena is zero-filled at construction and the device arena is not, so the two stores differ past the bytes a fill wrote. It does not reach G1, where every fill writes a whole slot, and no reader may look there — the streamer never hands out a slot it has not filled. Zeroing the arena would cost a full write of the whole budget at load, 18.55 GiB on the target checkpoint, to define bytes nobody reads. **The qualification reached the header, the gate case and the spec's `## Now`, and not the two lines that DEFINE the gate** six hundred lines away, which the second review found (F3 again). `## Gates` G1 and the `## Tests to port` row are what a W2 or a G-DISCRETE implementer reads to learn what PASS means, and both still made the unqualified claim; a gate written against that sentence would be gating a property the class does not have. Both now read "over the bytes a fill wrote", and the G1 entry says why in one sentence rather than pointing six hundred lines back. ## Mutations Seventeen, each applied alone in the tree, built, run, then restored and verified byte-identical by sha256 (`RESTORE_BAD=[]`, four files, all `True`). Every row carries its compile status, its `git diff --stat`, and a non-zero case count, because a mutation that fails to build and a mutation that never applied both read as a passing test. M1 was rejected and rewritten for exactly that reason: deleting the copy left `dst` unused, `-Werror=unused-variable` failed the build at compile status **1**, and the stale binary printed `SUCCESS!`. The row below is the rewritten one. **Every row is re-measured at the repaired head, not only the two the review named.** The device suite is now 112 assertions where it was 97 and the streamer suite is 269 where it was 187, so every recorded denominator was stale and a stale count is a claim the tree does not support. No mutation was carried over from the previous table. | # | Mutation | compile | diff --stat | Suite | Result | |---|---|---|---|---|---| | M1 | delete `CommitSlot`'s H2D copy | 0 | 1 file, 1+/1- | device | **RED** 10 cases/3 failed, 112 assertions/18 failed, rc 1 | | M2 | `SlotForWrite` returns the slot instead of staging | 0 | 1 file, 1+/1- | device | **RED** 10/2 failed, 112/17 failed, rc 1 | | M3 | `SlotForRead` returns staging instead of the slot | 0 | 1 file, 2+/1- | device | **RED** 10/3 failed, 112/15 failed, rc 1 | | M4 | delete `store_.CommitSlot(...)` in `EnsureFile` | 0 | 1 file, 0+/1- | device + streamer + wiring | **RED** device 10/2 failed, 112/18 failed, rc 1; streamer 12/2 failed, 255/16 failed, rc 1; `test_expert_stream_wiring` **GREEN** 4/882/0, rc 0 — the reachability control, see below | | M5 | delete `CommitSlot`'s `Synchronize` | 0 | 1 file, 0+/1- | device | **RED** 10/1 failed, 112/2 failed, rc 1 | | M6 | delete the staged-slot identity refusal | 0 | 1 file, 0+/10- | device | **RED** 10/1 failed, 112/6 failed, rc 1 | | M7 | widen the arena overflow guard so it cannot fire | 0 | 1 file, 1+/2- | device | **RED** 10/1 failed, 112/3 failed, rc 1 | | M8 | move `CreateQueue` above the budget refusals | 0 | 1 file, 1+/1- | device | **RED** 10/1 failed, 112/1 failed, rc 1 | | M9 | publish on the HIT path too | 0 | 1 file, 1+/0- | device + streamer | **RED** device 10/1 failed, rc 1 — and note the assertions line reads `108 / 0 failed`, because the case THREW; streamer 12/1 failed, 272/5 failed, rc 1 | | M10 | make the host `CommitSlot` skip its bounds check | 0 | 1 file, 2+/9- | device + host | **RED** device 10/1 failed, 112/3 failed, rc 1; host suite green, correctly — the assertion lives beside the device oracle | | M11 | move `store_.CommitSlot(...)` out of `EnsureFile`'s `try` | 0 | 1 file, 1+/1- | device + streamer | **RED** streamer 12/1 failed, 264/6 failed, rc 1; device suite green, correctly — no store in THAT suite can fail a publish | | **M12** | **delete `EnsureSpan`'s `try`/`catch` around `WriteSlot`** | 0 | 1 file, 1+/7- | streamer + device | **RED** streamer 12/1 failed, **235/8 failed**, rc 1; device green, correctly — no store there fails a write | | **M13** | **delete `Ensure`'s `try`/`catch` around `WriteSlot`** | 0 | 1 file, 1+/7- | streamer + device | **RED** streamer 12/1 failed, **266/8 failed**, rc 1; device green, same reason | | **M14** | **drop the arena release from the constructor's `catch`** | 0 | 1 file, 0+/1- | device | **RED** 10/1 failed, **112/2 failed**, rc 1 | | **M15** | **drop the constructor's whole `catch` body** | 0 | 1 file, 1+/5- | device | **RED** 10/1 failed, **112/4 failed**, rc 1 | | **M16** | **make `EnsureFile`'s `pread` ignore `file_offset`** (the shared-helper defect, F5) | 0 | 1 file, 1+/1- | device | **RED** 10/2 failed, **112/12 failed**, rc 1 — and the host-against-device assertion at `:359` stays GREEN through it | | F2C | delete both file `CHECK`s AND the H2D copy | 0 | 2 files, 2+/3- | device | **RED** 10/3 failed, 104/10 failed, rc 1 | **M12 and M13 are separate rows on purpose.** They are what proves the two new wraps are independent: a `try` on one entry point leaves the other exactly as exposed as it was, so one mutation covering both would have measured a single guard twice. M9's device row is the reason `Status:` is read and not only `assertions:`. `0 failed` on the assertions line with a failing case is a thrown case, which is what a `grep assertions:` gate would have called a pass. **M11 was added by the fresh review's FAIL finding, and the finding was right.** The first ten mutations did not include it. `store_.CommitSlot(...)` sits inside `EnsureFile`'s `try` so a failed publish takes the same `cache_.Invalidate` a failed read takes; that placement was argued in the body, the commit, the spec and the source comment, and attacked nowhere. Moving the call to just after the `catch` left all three suites GREEN, because no store in the tree could fail a publish. The scenario is real and arrives with W2: `DeviceExpertSlotStore::CommitSlot` calls `vt::Backend::Copy` and `Synchronize`, and a real CUDA backend throws out of both. Outside the `try` the throw escapes with the cache still claiming the key resident over a slot holding the previous expert's bytes; the next request is an ordinary HIT, no read is issued, and the GEMM multiplies the wrong expert with no symptom. Repaired red-first. `RecordingStore` takes a `throw_on_commit` flag and `test_expert_streamer` gains "a PUBLISH that throws leaves nothing resident either", mirroring the existing case that covers the `pread` arm only. RED with the call moved out: **10 cases / 1 failed, 182 assertions / 6 failed, rc 1**, compile status 0, no ENOSPC. The red IS the corruption rather than a proxy for it — `cache.IsResident(key)` stays TRUE and the retry comes back `hit` with `filled` false, which is the predicted "ordinary HIT over a slot nobody published", in the assertion output. GREEN with the call restored: **10 cases / 187 assertions / 0 failed, rc 0**, `expert_streamer.cpp` byte-identical to `git show HEAD:` by sha256. ## The second fresh review, and what it changed ### F1 (blocking) — two more entry points had the identical corruption window `EnsureFile` was wrapped and **`EnsureSpan` and `Ensure` were not**. Their `store_.WriteSlot(...)` calls are the same window one step earlier: `Acquire` has to run first because the write needs a slot, so by the time the write throws the cache already says the key is resident, over a slot still holding the expert the acquisition just evicted. The throw escapes, the next request for that key is an ordinary HIT, no bytes move because a hit moves none, and the GEMM multiplies the evicted expert. **This PR is what makes `WriteSlot` throwable.** `DeviceExpertSlotStore::WriteSlot` calls `vt::Backend::Copy` and `Synchronize`, which route through `Check()` in the CUDA backend and raise `std::runtime_error`. Before this wave every store's `WriteSlot` was a `memcpy`. **`EnsureSpan` is a PRODUCTION call site**, reached from `Qwen3_5Model::Forward` through `Qwen35ExpertStream::Slice`, and this PR's own device suite drives the device store through it. So this half is not the wait-for-W2 shape the publish arm has; it is in this wave's scope. Both now take the same `try` / `catch (...) { cache_.Invalidate(key); throw; }` as `EnsureFile`. Gated red-first: `RecordingStore` takes a `throw_on_write` flag mirroring `throw_on_commit`, with one case per entry point, and each asserts CONSISTENCY rather than emptiness — the key is not resident, `SlotOf` is empty, `resident()` is 0, `fills()` and `bytes_filled()` are unmoved, the slot still holds the EVICTED expert's bytes, and the retry is a real MISS that refills. **RED** with neither `try` present: **12 cases / 2 failed, 232 assertions / 16 failed**, rc 1, compile status **0**, no ENOSPC. The red is the corruption itself rather than a proxy — `retry.hit` is `true` and `store.slot(retry.slot)[0]` reads `4` where the test asked for expert `6`. **GREEN** with both: **12 cases / 269 assertions / 0 failed**, rc 0. Independence proven by M12 and M13 above. ### F2 — the constructor leaked on the failure that happens and guarded one that cannot It tested `Alloc` and `AllocPinned` for `nullptr`. **No backend in this tree returns one:** `CpuBackend::Alloc` refuses with `VT_CHECK`, `CudaBackend::Alloc` and `AllocPinned` refuse through `Check(...)`, and the base `Backend::AllocPinned` forwards to `Alloc`. They all throw. So both guarded branches were unreachable, while a throw from `Alloc` stranded the CUDA stream and a throw from `AllocPinned` stranded the stream AND the whole device arena — 18.55 GiB on the target checkpoint — at the one moment the device has no memory left to lose. Out of memory is this class's headline failure; #1123 is literally `vt cuda: cudaMalloc: out of memory`. The acquisitions now sit inside a `try` whose `catch` runs the destructor's body and rethrows unchanged, so the caller still sees the backend's own message. **The `nullptr` branches are KEPT, deliberately**: `vt::Backend` is an interface, and a nullptr-returning implementation would otherwise hand out slot pointers off a null arena instead of being refused. They now cost one branch and no cleanup code, because the catch owns the release. The header's claim that a failed allocation raises this constructor's own `std::runtime_error` is corrected to describe what actually happens, and the `.cpp` comment that said nothing is acquired above a possible throw now says which part of that is still true. Gated by `throw_on_alloc` and `throw_on_pinned_alloc` on the suite's `CountingBackend`, with `frees`, `pinned_frees` and `last_freed` counters so the assertion is "the arena came back", not "no crash". **RED** at **10 cases / 1 failed, 112 assertions / 4 failed**, rc 1, compile 0; **GREEN** at 112 / 0. Mutations M14 and M15 above. ### F4 — two recorded mutation counts were allocator-dependent The "each slot holds a DIFFERENT slice" check compared two device slots that a publish-suppressing mutation leaves UNWRITTEN, and `vt::Backend::Alloc` does not initialise them, so whether that assertion red was decided by `std::aligned_alloc` garbage. The reviewer measured M4 device at **112/18** against the recorded 17 and the F2 combination at **104/10** against the recorded 9; both deltas were that single assertion. That is the very allocator non-determinism the F2 correction invokes as its justification, appearing inside the gate's own assertions. The gate now writes every device slot to a known byte before the fills, so an unmutated fill is the only thing that can make two slots differ, and the assertion measures the store. **Proven, not asserted:** under M4 the assertion at `test_device_expert_slot_store.cpp:374` fails on **25 consecutive runs** and the suite reads **112 assertions / 18 failed on all 25**. The whole table above is re-measured at this head for the same reason. ### F5 — the file `CHECK`'s reason, restated rather than deleted, and then measured Covered under G1 above: the check is load-bearing on the shared-helper ground, not on determinism, and it stays. Restating a reason in prose is the same shape as the reason it replaced, so **M16 measures it**. Making `EnsureFile`'s `pread` ignore `file_offset` fills every key in BOTH arms from offset 0 — one shared helper, one defect, two identically wrong arms. The host-against-device assertion at `test_device_expert_slot_store.cpp:359` **stays GREEN** through exactly that, as the argument predicts, and the only assertions that red are the two file `CHECK`s at `:370` and `:371` and the different-slice check at `:375`: 10 cases / 2 failed, 112 assertions / 12 failed, rc 1, compile 0, tree restored byte-identical by sha256. That is the whole case for keeping a check whose stated reason was wrong twice: it is the only assertion in the case that can see a class of defect the rest of the case is structurally blind to. ## Nothing lands dead: this lands UNREACHED, and here is what that means exactly **What is not reached:** `DeviceExpertSlotStore`. No loader, no model, no example and no registered command constructs one. `Qwen35ExpertStream::store_` is still a `std::unique_ptr<HostExpertSlotStore>` and `Qwen35ExpertStream::Slice` reads the concrete `HostExpertSlotStore::Slot`, so there is no seam a device store could be handed to yet. **The owning row:** `ENG-EXPERT-STREAM-DEVICE`, wave W2 — the virtual `SlotForRead`, `store_` becoming a `std::unique_ptr<ExpertSlotStore>`, and selecting the store from the platform. **The tracking issue:** #1124, which stays OPEN. This is one wave of three. The spec lists it under `## Owed`, the landing commit body names it, and the class carries the statement in its own header comment. The spec's own recommendation is still to land W1 and W2 as one pull request; the split was a dispatch decision and this declaration is the cost it names. Measured rather than asserted, and the earlier draft of this line overstated it. `git grep -n DeviceExpertSlotStore -- src include examples benchmarks tests`, excluding the class's own files and its own suite, returns **five matches, every one of them inside a `//` comment**: `expert_streamer.h:18`, `expert_streamer.cpp:159`, and `test_expert_streamer.cpp:50`, `:84` and `:396`. Filtering the comment lines out leaves **rc 1, no output** — no construction, no declaration, no type use anywhere outside the class. Two of those five predate this review; the third source one is a comment F1's repair added. The positive control that makes this an absence rather than a wrong pattern is the same grep for `HostExpertSlotStore`, which returns `qwen3_5.cpp:5912` and `:5941` — a real production construction and a real production member. **The narrower half, stated so nobody reads it generously.** The `CommitSlot` CALL is not in the same position as the class. It sits in `ExpertStreamer::EnsureFile`, which IS a production call site: `qwen3_5.cpp`'s `Qwen35ExpertStream::Slice` reaches it from `Qwen3_5Model::Forward`, and the line executes on every real streamed fill today. What it does there is nothing, because the store production selects is the host one and its `CommitSlot` is a no-op. The reachability mutation says so in numbers: deleting that call reds the new suite (M4 above) and leaves `test_expert_stream_wiring` — the suite that enters through `Qwen3_5Model::Forward` — at **4 cases / 882 assertions / 0 failed, rc 0**, compile status 0, unchanged from its baseline and re-measured at this head. A call count is not a capability. Both halves wait on W2. **One thing the second review's F1 does change here, and it is worth saying precisely.** The `EnsureSpan` and `Ensure` wraps are not in the unreached half. Those entry points are production paths today — `Qwen35ExpertStream::Slice` calls `EnsureSpan` from `Qwen3_5Model::Forward` — so that repair is reached at its own merge commit, on the host store, on every streamed fill. What stays unreached is the CLASS, exactly as stated above. ## Records `.agents/specs/expert-stream-device-slots.md`: `## Now` records the wave and the G1 result, the W1 work-breakdown section records what landed and the pure-vs-defaulted decision, `## Owed` converts the conditional "W1 may land unreached" entry into the factual one, and the `## Tests to port` table is corrected to the file that exists. `.agents/issue-index.md` is NOT touched: #1124 already has a row, and the index is append-only, so a second row would be the duplicate `check-agent-record.py` refuses. The gate file is `tests/vllm/model_executor/test_device_expert_slot_store.cpp`, not the `test_expert_slot_store.cpp` the spec's table named when it was written. Named rather than done quietly: the header it gates is `device_expert_slot_store.h` and the suite sits beside `test_host_expert_slot_store.cpp`, which is its ORACLE, so a name that does not say which store it is about is the one thing a reader has to disambiguate every time. The spec table is corrected to match the tree. No public document is owed. This edits `src/`, `include/`, `tests/` and one spec; no row changes lifecycle state, no benchmark ID changes disposition, and no user-visible surface moves. ## Not mine `check-agent-record` and `test_agent_record` were RED on this branch and on `origin/main` because `.agents/issue-index.md` listed **#1649 twice**. That was repaired on `main` by #1742 while this branch was in review, and merging it in cleared the gate here. This branch never touched that file. At the repair head `scripts/agent-preflight.sh` is **rc 0** with no FAIL and no SKIP, trailer and commit-style gates included, and `test_cpu_x86_llamacpp_floor` (the #618 flake) passed on the first run with no discrimination needed. `check-commit-style.py --range origin/main..HEAD` and `check-commit-trailers.py --range origin/main..HEAD` are both rc 0. **CI at `7e0cf84ac` (run `32612431657`): 14 of 17 jobs green, and the three reds are each attributed by reading the log rather than assumed.** Green includes `build-test-cpu`, `build-test-cpu-arm64`, `build-test-vulkan`, `build-newest-gcc`, `cuda-fat-build`, `cuda-arch-features`, both `sanitize-cpu` lanes, `device-leakage`, `pr-size`, `commit-protocol-tag`, `documentation-checkpoint`, `last-gated-commit` and `vulkan-spirv-freshness`. * **`windows-msvc-cpu` and `windows-msvc-vulkan`: [#584](#584), not this branch.** Both fail at `tests\Release\test_openai_api_server.exe exited with status -1073740791` (`0xC0000409`, the `__fastfail` status). Attributed by MATCHED ARM rather than by argument: the run on this PR's own previous head `a3c89a609` (run `32608548556`) fails both lanes with the byte-identical signature, before any of this repair existed. #584 already carries the diagnosis, owned by `ENG-RELEASE-WINDOWS`, so no new issue is filed. * **`agent-record`: [#1722](#1722), not this branch.** It errors in `SiteGuardTests.test_rendered_benchmark_index_links_resolve_to_emitted_pages` with `FileNotFoundError: [Errno 2] No such file or directory: 'hugo'` — the runner has no Hugo. Attributed by baseline: `agent-record` is `failure` on `main`'s own runs at `6354755ba` (runs `32611673963` and `32608320394`) and at `b508cbce6` (run `32613454280`). This branch edits no docs-site input. 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>
Closes #1731. Closes #1733 as a duplicate of #1731.
mainat038ff61e5is RED onscripts/check-agent-record.py-- the CI jobagent-record-- and therefore on every session's preflight and every branchcut from it:
The checker's message diagnoses its own cause.
:592was appended bya7bb3130b(#1633) underENG-HF-MODEL-DOWNLOAD, the lane that FILED #1649.:632was appended by2f2a70925(#1701) underGATE-WINDOWS-PORTABILITY-TARGET-SCOPE, the lane that FIXED it. Neither lanecould see the other's append, and
merge=unioncombines two appends silently.Which row goes, and on what test
Not on which row is older, and not on which reads better. On the rule
AGENTS.mdstates under## Every change starts from an issue: the issue islinked "in three places that must agree: the index, the row's spec, and the
pull request body". Exactly one of the two rows passes that test.
:632namesGATE-WINDOWS-PORTABILITY-TARGET-SCOPE, whose spec.agents/specs/gate-windows-portability-target-scope.mdreadsIssue: #1649on its third line, and whose pull request fix(GATE-WINDOWS-PORTABILITY-TARGET-SCOPE): assert the MSVC warning policy by TARGET, so a vendored PRIVATE /w stops reading as a project-wide negation (#1649) #1701 names windows-msvc-cpu is red on main and every PR: check-windows-portability reads the vendored BoringSSL /w as a project-wide negation of /W4 /WX #1649 inits title. Three surfaces, all agreeing.
:592namesENG-HF-MODEL-DOWNLOAD, whose spec.agents/specs/hf-model-download.mddoes not mention windows-msvc-cpu is red on main and every PR: check-windows-portability reads the vendored BoringSSL /w as a project-wide negation of /W4 /WX #1649 anywhere at all-- not under
## Owed, not in any other section.grep -n 1649over thatfile returns nothing, against a positive control that the same grep over
gate-windows-portability-target-scope.mdreturns eight hits. That row'sownership claim was never corroborated by the spec it pointed at.
Two further facts read the same way and are recorded so the choice can be
re-checked rather than taken on trust.
:592asserts in the present tense that"
windows-msvc-cpuis RED onmainand on every pull request", which #1701made false;
:632is a superset of:592's technical content and adds thefix, a second red
:592missed, the mutation evidence and the residual. Andnothing in the tree anchors a line number into this file:
grep -rn 'issue-index.md#L'over the repository returns nothing, against a positivecontrol that
.md#Lanchors do exist elsewhere, so no reader and no checkeris holding an offset that either deletion would move.
MERGING the two rows was rejected. It edits one row and deletes the other,
which is a strictly larger violation of the same rule than deleting one, and
under
merge=unionan edited line is duplicated rather than merged, so themerged row is the shape most likely to come back as a third
#1649.The dropped row is not lost
Its four facts that
:632does not carry -- the introducing commita50c57d69and thegit log -Sthat isolates it, the job that measured thered, the disambiguation from #503, #603 and #965, and how the
/wwasisolated and by which lane -- now live in
.agents/specs/gate-windows-portability-target-scope.mdsection 10, which isthe spec a reader reaches from the surviving row. The row's verbatim text is
this paragraph:
| #1649 |
ENG-HF-MODEL-DOWNLOAD|windows-msvc-cpuis RED onmainand on every pull request, and no compilation is ever attempted.scripts/build-windows-release.ps1:31runsscripts/check-windows-portability.pybefore the build and it exits 1 withCMakeLists.txt: MSVC /W4 /WX policy is negated on the C/C++ compile by /w. Measured on9f13751c3in job 96949585684: the refusal lands seventeen seconds afterBuild files have been written to, and the log carries noerror C####and noerror LNK####. NOT #503, #603 or #965, which name a baseline-reporting hole, a POSIXsetenvand a C4456 shadow respectively -- none of them a checker refusal. Isolated to ONE token:CMakeLists.txt:2441gives the FETCHED BoringSSL targetstarget_compile_options(<vendored> PRIVATE $<IF:$<CXX_COMPILER_ID:MSVC>,/w,-w>), and dropping only the MSVC arm makes the same checker printWindows portability contract OK(probed and restored byte-for-byte against a pre-taken sha256). The/wis deliberate and correct -- the comment above it reads "Vendored code is not on this project's -Werror path" -- and what the checker cannot see is SCOPE: it reads/wanywhere inCMakeLists.txtas a project-wide negation, which is true of a globaladd_compile_options(/w)and false of aPRIVATEoption on a third-party target. Introduced by a50c57d69 (#1505, rowENG-HF-MODEL-DOWNLOAD, #1280), which added the static-BoringSSL transport;git log -Son that generator expression returns that commit alone. NOT fixed in flow: which side is wrong is a real decision, and teaching the checker scope is a semantic checker change, whichAGENTS.md## Changing the rules or a checkersends down the spec + red-before + fresh-review path. Found while repairing the fresh-review findings on #1633, which reads the red, is not its cause, and does not touchCMakeLists.txt. Spechf-model-download.md| bug |Exception argued, not waived
This deletes a base-reachable row from an append-only file, which is exactly
what
scripts/check-issue-index-append-only.pyforbids. That gate is RED onthis branch and it is not being weakened. There is no waiver registry here, so
the argument lives in this message, attached to the diff it excuses, as
ff264cb82(#1025) argued the same one for the #995 duplicate.The append-only contract cannot repair a duplicate. Appending a third
#1649row makes
check-agent-recordangrier, and the file only becomes well-formedby removing one. The two gates are in genuine contradiction on this tree, and
that contradiction is the defect, not the repair.
The rule is preserved in substance, verified mechanically:
#1649row removed, one#1733rowappended; the other 619 rows byte-identical and in the same relative order,
and every non-row line of the file byte-identical
#1649appears exactly once, down from twice#1733, and no key lost.github/, against a positivecontrol that
check-agent-recordis there, so this costs no CI redmainwith no duplicate and sees noremoval in its range, so the violation is confined to this commit
The instrument, and a false measurement it produced
#1733 reports that "the repair is measured and it is NOT blocked", quoting
agent record OK: ENGINE=170 MODEL=377 ...andOK: issue index append-onlyfrom the same tree. Both cannot be true of one committed tree while the
duplicate is base-reachable, and they are not:
scripts/check-issue-index-append-only.py:50-51diffsmerge-base(origin/main, HEAD)..HEAD, which reads COMMITS. Measured here atbase
038ff61e5::592in the WORKING TREE only:OK: issue index append-onlyrc 0,and
git diff --numstat 038ff61e5..HEAD -- .agents/issue-index.mdis EMPTYremoved:line naming the rowAn uncommitted mutation therefore looks exactly like a gate that cannot detect
it. The
#1733row appended here records that, because the row cannot beedited afterwards.
Gates
check-agent-record.pyrc 1 before, rc 0 after, reportingENGINE=170 MODEL=377 QUANT=84 KERNEL=57 BACKEND=85 ANCHOR-ROT=37.check-conflict-markers.py0 findings over 3898 tracked text files, proveddiscriminating by injecting a marker into a scratch copy of
docs/USAGE.md(rc 1, one finding) and restoring it against a pre-taken sha256.
FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]