record(VT-REFTIER-HOST-ADDRESSABLE): the test cited as pinning CUDA reads the CPU backend - #1639
Merged
Merged
Conversation
…eads the CPU backend #1620 recorded that `tests/vllm/platforms/test_platform.cpp` pins the CUDA backend's `DeviceMemoryIsHostAddressable()` default. It does not. `FakeUnifiedAddressablePlatform` reports `device_type() == kCUDA` at `:284`, but its `backend()` returns `vt::GetBackend(DeviceType::kCPU)` at `:285`, so the `CHECK_FALSE(gb10.backend().DeviceMemoryIsHostAddressable())` at `:329` asserts against the CPU backend. The fixture's own comment says so. The conclusion survives and the code is unchanged: `CudaBackend` declares no override, so it inherits the base `false` at `include/vt/backend.h:77`. But it holds by ABSENCE of an override, which is a weaker claim than a pin, and the record read as though a test stood behind it. Nothing in the tree pins the real `CudaBackend`. No gate could have caught this. The assertion passes, and it passes for a reason unrelated to what it claims to measure -- the same shape as a mutation that never applied reading as a passing test. The wrong citation landed in three places and one of them is permanent: `docs/ENVIRONMENT.md:210` is corrected here, the commit message stands as what was believed at the time, and `.agents/issue-index.md:552` is append-only and can never be edited. That last one is why this is a new row rather than an edit, and why a record-only change is the right shape: the record IS the work. Pinning the real `CudaBackend` needs a CUDA device and stays owed on the row. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
`origin/main` gained six `.agents/issue-index.md` rows between this branch's base and now, from other sessions landing concurrently. Merging locally is not a formality here: GitHub does NOT honour the `merge=union` driver declared in `.gitattributes`, so a stale branch carrying an append to that file is resolved by the forge without it. Doing the merge here lets the driver union the appends, which is the only reason two concurrent appends to an append-only log are safe. The only shared file is that index, and both sides only append. No source file is touched by both sides. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
…d`, and cite the index by ROW Two review findings on the CUDA unified-memory citation correction. The `#1635` bullet was written into `## Scope`, under "Out of scope, and why", because the authoring script anchored on `s.index("## Owed")` and matched a PROSE MENTION of that string inside `## Scope` — the "Recorded under `## Owed`" cross-reference in the `#1435` bullet above it — rather than the heading. It is now the last bullet of the real `## Owed` section, anchored on a line that begins `## Owed`. The content was miscategorised as well as misplaced: "#1635 is OPEN and owned here, and pinning stays owed" states an obligation, not an exclusion from scope. The row also baked a line number into the append-only index. It cited the wrong claim's home as `.agents/issue-index.md:552`, and that was already false: `origin/main` landed five rows above `#1502` while this branch was open, so the merged tree puts it at `:557`. A permanent line number into a file nobody may edit would have been wrong on the day it shipped — which is the defect this row exists to describe, committed inside the row describing it, and it is `#844`'s own rule. Every reference now names the ROW: "the `#1502` row of this index", and "the `VT_ADOPT_DEVICE_BYTES` row of `docs/ENVIRONMENT.md`". The second was lower risk than the first, because `docs/ENVIRONMENT.md` is editable, but a line number is a line number and the row already had to say where the claim lives; naming it costs nothing and cannot rot. A corrected `:557` was rejected: it rots again on the next append above it. Editing the `#1635` row rather than appending another is not an append-only violation. The row exists only on this branch — `git show origin/main:.agents/issue-index.md` has no `#1635` row — and the rule forbids editing a row already in the log, which this is not. `issue-index append-only` passes. Records only. No product code, no test, and no behaviour changes. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
`origin/main` gained three commits while the review ran, and five of them land index rows ABOVE `#1502`. Merged rather than rebased, because a rebase would need a force-push. The merge is done LOCALLY on purpose: GitHub does not honour the `merge=union` driver on `.agents/issue-index.md`, so resolving the append on the forge is not the same operation as resolving it here. This merge is also the evidence for the second review finding. At the pre-merge head `#1502` sat at `:552`, which is what the `#1635` row used to cite; in this merged tree it sits at `:557`. A line number into an append-only file was therefore already wrong before the row had shipped once, which is why every reference now names the ROW. Index after the merge: 546 rows = 540 at the merge base + 5 from `origin/main` + 1 from this branch. `#1635` appears exactly once, no row present at the merge base is missing or altered, every `origin/main` row survives byte-for-byte, and no issue number is duplicated. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
…use #844's rule is about the file the number is written IN The `#1635` row invoked #844's rule -- "grep the symbol, because this index is append-only and a line number here would be permanent" -- while itself keeping four permanent line numbers: `:284`, `:285` and `:329` into `tests/vllm/platforms/test_platform.cpp`, and `include/vt/backend.h:77`. The distinction the previous wording drew was "editable target, so keep the anchor". That is not #844's distinction. #844's criterion is the file the number is WRITTEN IN, not the file it POINTS AT: this index is append-only and no checker scans it, so a number here can never be repaired once it rots, no matter how editable its target is. A row that argues a rule and then breaks it teaches the wrong criterion to the next reader. The four numbers are replaced by names that cannot rot: the `device_type()` and `backend()` overrides of `FakeUnifiedAddressablePlatform`, the assertion `CHECK_FALSE(gb10.backend().DeviceMemoryIsHostAddressable())`, and `Backend::DeviceMemoryIsHostAddressable`'s `false` default. The file names stay, so the row is still grep-navigable. The row also now states that the landing pull request carries no closing keyword and that #1635 stays OPEN. It carried `Fixes #1635` in its body, which would have closed the issue at the squash and falsified this row's own OWED clause and the `## Owed` bullet in `.agents/specs/vt-reference-tier-host-addressable.md`, both of which say #1635 is open. The second owed item -- pinning the real `CudaBackend`, which needs a CUDA device -- is not done here, so closing it would leave that work tracked by a spec bullet alone. Only the `#1635` row is touched. It exists on this branch only and is not yet in the log, so this is not an append-only violation: `check-issue-index-append-only.py` diffs `merge-base(origin/main, HEAD)..HEAD` and reports ok. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
…r the two review findings `origin/main` advanced to `5453e571d` while the two fresh-review findings were being repaired. Merged rather than rebased, because a rebase would need a force-push and `main` never takes one. Merged LOCALLY, because GitHub does not honour this repository's `merge=union` driver on `.agents/issue-index.md`. Row arithmetic: 549 rows = 548 on `origin/main` at `5453e571d` plus the one `#1635` row this branch appends. The merged file with that single row deleted is byte-identical to `origin/main`'s copy, so the union merge added nothing, removed nothing and edited no existing row. `#1635` appears exactly once and no issue number is duplicated. No conflict markers survive. The `5453e571d` side is the ModelOpt 27B NVFP4 change, which touches no file this branch writes apart from the index, so the other paths merge by fast-forward of their `origin/main` state. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
…ng line number carried a rotting count The `#1635` row and the pull request body both explained the line-number citation by saying `origin/main` had pushed the `#1502` row five lines further down. That was true at the previous merge and false at this head, where the figure is larger, because a third merge landed more rows above it. A count read off whatever `origin/main` happened to be merged last rots by exactly the mechanism the paragraph exists to condemn, and this branch will merge `origin/main` at least once more before it lands, so replacing five with the current figure would only schedule the next instance. Both sentences now carry no count and no line number. What survives is the argument, which no merge can falsify: `origin/main` appended rows above `#1502` while this branch was open, so the line the first draft named stopped identifying that row, which is why every citation names the row instead. The row also says outright that no count is written, so the next reader does not supply one. The `docs/ENVIRONMENT.md` sentence loses its `:329`. That file is editable, so a stale number there is repairable rather than permanent, but the sentence already quotes the assertion verbatim and the number adds nothing except one more thing to go stale -- in the very file this change is rewriting to remove a bad citation. Records only. No `src/`, `include/` or `tests/` path is touched, and #1635 stays open. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
…r the count was removed The fourth merge on this branch, and the first one that cannot falsify anything this branch says. `origin/main` appended five more index rows, which moved the `#1502` row from line 559 to 564 -- so the "five lines" this branch had just finished deleting would have been wrong again, by a different amount, for the third time. The rewritten paragraph names no count and no line, and it reads true at this head for the same reason it read true at the last one. The index merged by union and needed no manual reconciliation. Verified rather than assumed: deleting the single `#1635` row from the merged file reproduces `origin/main`'s `.agents/issue-index.md` byte for byte (sha256 `dbd24385...`), so this branch edits no existing row and deletes none. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
GitHub refused the squash with a conflict on `.agents/issue-index.md`. That file carries `merge=union` in `.gitattributes`, and the forge does NOT honour the driver -- so two branches that each append a row conflict there even though the union resolves cleanly. Doing the merge locally is what applies the driver. `#1622` landed between this branch's last merge and the squash attempt. The only shared file is that append-only index, and both sides only append. No source file is touched by both. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [Claude Code]
localai-bot
added a commit
that referenced
this pull request
Aug 23, 2026
…nswer in the two halves it actually has (#1778) `CudaBackend::DeviceMemoryIsHostAddressable()` gates the portable CPU reference tier (`src/vt/op_provider.cpp`), the weight loader's `VT_ADOPT_DEVICE_BYTES` adoption (`src/vllm/model_executor/models/qwen3_5_weights.cpp`) and the logits-processor bounce (`src/vllm/v1/sample/logits_processor/builtin.cpp`). Being wrong there hands a device pointer to a host memcpy, which is the SIGSEGV that #844 and #1435 measured. Nothing in the tree held that answer. The test cited as the pin reads a different backend: `FakeUnifiedAddressablePlatform` in `tests/vllm/platforms/test_platform.cpp` reports `device_type() == kCUDA` while its `backend()` returns `vt::GetBackend(DeviceType::kCPU)`, so its `CHECK_FALSE` measures the CPU backend. #1639 corrected that fixture's comment; this change supplies the pin the record was still owed. ## A runtime pin was rejected on a CI fact, not a preference No job in `.github/workflows/ci.yml` has a GPU. `cuda-fat-build` is the only job with a CUDA toolchain, it runs the `nvidia/cuda:13.3.0-devel` container on `ubuntu-latest`, it configures `-DVLLM_CPP_BUILD_TESTS=OFF` and it builds the `vllm` target alone. The CUDA registrar returns early when `cudaGetDeviceCount` finds no device, so `vt::GetBackend(kCUDA)` throws on every machine this project's CI owns. A `TEST_CASE` reading the real backend would report a skip on every lane forever, and a skip reads as a pass. That is the shape of evidence #1635 was filed about, so writing one and calling the debt discharged would repeat the defect rather than close it. ## One claim, two halves, each checked where it executes The record's claim decomposes, and neither half alone is sufficient. **`CudaBackend`'s answer IS the base default.** `src/vt/cuda/cuda_backend.cu` gains a `static_assert` beside the class requiring `decltype(&CudaBackend::DeviceMemoryIsHostAddressable)` to be `bool (Backend::*)() const`. Taking the address of an inherited member through a derived class yields a pointer-to-member of the class that DECLARES it, so that type holds exactly while `CudaBackend` declares no override of its own, and becomes `bool (CudaBackend::*)() const` the moment somebody adds one. It fires on ANY override, including one returning `false`, because an override invalidates the reasoning whatever it returns. `cuda-fat-build` compiles this translation unit on every push. **The base default is `false`.** `tests/vt/test_backend.cpp` gains a `Backend` subclass that implements the pure virtuals and deliberately declares no `DeviceMemoryIsHostAddressable`. Every other fake in the tree overrides that method and takes the answer as a constructor argument, so each measures its own override and none reads the default. This one does, and it answers `UnifiedMemory() == true` on purpose so the case cannot pass by the two predicates happening to agree. Every host lane runs it. Together they are a mechanical proof of `CudaBackend` answering `false`, checked on surfaces that execute rather than skip. ## Mutation evidence Half one, against `nvcc` 13.3.33 from the CI container, compiling the real file: | Tree | `nvcc -std=c++20 -Iinclude -Isrc -c src/vt/cuda/cuda_backend.cu` | |---|---| | clean, sha256 `d9d11f96...` | rc=0 | | `+ bool DeviceMemoryIsHostAddressable() const override { return true; }` | rc=2, `static assertion failed` at `cuda_backend.cu(363)` | | same override returning `false` | rc=2, identical message | | restored, sha256 `d9d11f96...` | rc=0 | Half two, `include/vt/backend.h` default flipped to `true` and the target rebuilt: | Tree | `test_backend -tc="Backend::DeviceMemoryIsHostAddressable defaults to false"` | |---|---| | clean, sha256 `80edf388...` | `assertions: 3 \| 3 passed`, rc=0 | | default `false` -> `true` | `assertions: 3 \| 1 passed \| 2 failed`, rc=1 | | restored, sha256 `80edf388...` | `assertions: 3 \| 3 passed`, rc=0 | Both mutated builds compiled, so neither red is a build failure wearing a test failure, and both files were restored byte-for-byte and re-verified by sha256 and by re-running the check. Suites: `test_backend`, `test_backend_cross_device`, `test_backend_cross_device_vt_attn_decode_d128`, `test_cuda_backend`, `test_op_provider`, `test_reference_tier` -- 6/6 pass; `test_backend` alone is 9 cases / 51 assertions. ## What is still not held, stated rather than left to be inferred No CI surface observes a live `CudaBackend` object answering the question, because no CI surface has a device. `tests/vt/test_cuda_backend.cpp` gains that observation using the skip convention every case in that file already follows, and on a CPU host it reports `assertions: 0` and says in its own skip message that nothing was observed and where the answer actually lives. It is the empirical belt to the two structural braces. Citing it as the pin would repeat #1635 exactly, and both the case and the spec say so. `docs/ENVIRONMENT.md` claimed nothing pinned the real `CudaBackend`, which this change makes false, so it now names both halves and warns off the wrong citation. The `#1502` row of `.agents/issue-index.md` keeps the original wrong citation, because that index is append-only and can never be edited; no row is edited here. Fixes #1635 FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude: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.
A post-hoc review of four merged records pull requests found that #1620 cited
tests/vllm/platforms/test_platform.cppas pinning the CUDA backend'sDeviceMemoryIsHostAddressable()default. It does not.FakeUnifiedAddressablePlatform'sdevice_type()override returnsDeviceType::kCUDA, but itsbackend()override returnsvt::GetBackend(DeviceType::kCPU). So the assertion:CHECK_FALSE(gb10.backend().DeviceMemoryIsHostAddressable());reads the CPU backend. The fixture's own comment says so. Nothing in the tree pins the real
CudaBackend.What survives, and what does not
The conclusion is still true and no code changes here:
CudaBackenddeclares no override, so it inheritsBackend::DeviceMemoryIsHostAddressable'sfalsedefault ininclude/vt/backend.h. But it holds by ABSENCE of an override, which is a strictly weaker claim than a pin, and the record read as though a test stood behind it.No gate could have caught this. The assertion passes, and it passes for a reason unrelated to what it claims to measure — the same shape as a mutation that never applied reading as a passing test.
Why a new row rather than an edit
The wrong citation landed in three places:
VT_ADOPT_DEVICE_BYTESrow ofdocs/ENVIRONMENT.md— corrected in this change#1502row of.agents/issue-index.md— append-only, and can never be editedThat last one is the whole reason this is a record-only change with a new row.
CLAUDE.mdmakes the index append-only and gates it; the correction cannot be applied in place. Per policy a record-only pull request is correct when the record IS the work — a newly filed gap — which is this case.Both citations name a ROW, not a line
An earlier revision of this branch cited the wrong claim's home by LINE NUMBER, and that citation was already false.
origin/mainappended rows above#1502while this branch was open, so the line that revision named had stopped identifying that row before the branch had shipped even once — verified in the merged tree here, where the cited line holds a different row entirely. A permanent line number into a file nobody may edit would have shipped wrong on its first day, which is exactly the defect this row exists to describe, committed inside the row describing it. It is also #844's own rule: grep the symbol, because this index is append-only and a line number here would be permanent.Correcting the number to its new value was rejected. A corrected line number is still a line number in an append-only file, and it rots again on the next append above it. So is a count of the rows that pushed it down, which is why neither this body nor the row states one. An earlier draft of both explained the stale line by naming how many lines
origin/mainhad moved#1502, and the next merge falsified that figure exactly as the merge before it had falsified the line — the same defect, one level up, inside the change that exists to condemn it. The argument needs no quantity: rows landed above#1502, so the line stopped naming it. Stated that way it survives the merge this branch still has to do before it lands, and every merge after that. Every reference now names the row instead, and the row's citations intotests/vllm/platforms/test_platform.cppandinclude/vt/backend.hname the two overrides, the assertion, and the virtual rather than four line numbers — #844's criterion is the file the number is WRITTEN IN, which is this append-only index and this permanent message, not the file it points at. Thedocs/ENVIRONMENT.mdanchor got the same treatment even though that file is editable and the risk is lower: the row has to say where the claim lives either way, and naming the row costs nothing and cannot go stale.Owed
Pinning the real
CudaBackendneeds a CUDA device and stays owed. It is the## Owedbullet of.agents/specs/vt-reference-tier-host-addressable.mdopening "Nothing in the tree pins the realCudaBackend's" — grep that, rather than counting down to a bullet position that the next appended bullet would move, for the reason this whole change is about.Tracked by #1635, which stays OPEN. This change corrects the citation and records the gap; the pin itself is the other half of what #1635 owes, it needs a CUDA device, and it is not done here. No closing keyword appears anywhere in this body, for any issue, deliberately.
Verification
agent-preflight.shon the merged head: every gateokand zero SKIPPED, includingissue-index append-only,doc-checkpoint range,now-current range,commit-trailersandcommit-style.check-commit-style.py --rangeandcheck-commit-trailers.py --filledexit 0 over$(git merge-base origin/main HEAD)..HEAD, andcheck-issue-index-append-only.pyexits 0 over the same base. The base is written as that command rather than as a SHA, because the base moves every time this branch mergesorigin/main, and a pasted SHA names whichever merge happened to be last.check-agent-record.pytakes no base — it reads one tree — and exits 0 on the merged tree.origin/main's file plus exactly this branch's one row: deleting the#1635row from it reproducesgit show origin/main:.agents/issue-index.mdBYTE FOR BYTE. So no existing row is edited or removed, no issue number is duplicated,#1635appears once, and no conflict marker survives. That byte identity is the check, and it replaces the row-count arithmetic an earlier draft used — another number that was only true of whicheverorigin/mainwas merged last. The merge was done LOCALLY because GitHub does not honour themerge=uniondriver.test_cpu_x86_llamacpp_floorfails locally under load, with a failure count that varies run to run at load averages of 174 and 41 — the known contention flake, not reachable from three record and document files.FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Claude:claude-opus-5 [Claude Code]