Skip to content

fix(rocm/gemma4): #839 retirement-safe prefill peer GeGLU Launch/Finish - #1047

Merged
localai-bot merged 15 commits into
mudler:mainfrom
bakon11:fix/gemma4-839-prefill-peer
Aug 18, 2026
Merged

fix(rocm/gemma4): #839 retirement-safe prefill peer GeGLU Launch/Finish#1047
localai-bot merged 15 commits into
mudler:mainfrom
bakon11:fix/gemma4-839-prefill-peer

Conversation

@bakon11

@bakon11 bakon11 commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

fix(rocm/gemma4): #839 retirement-safe prefill peer GeGLU Launch/Finish

Fixes #839.

The defect

The Gemma-4 FP8 cross-device prefill helper was one monolithic function. Its
donor unpinned the dequant cache immediately after enqueueing the GEMMs and
before hipEventRecord(ev_e), and the cache is process-wide, so a second worker
could evict a zero-pin slot while the first expert stream was still reading it.
Under batched-MoE prefill that is a use-after-free, and the observed symptom is a
wedge after matched BEGIN/END with the process idle in kfd_wait_on_events.

What changed

RunGemma4Fp8ExpertGeGLUPrefillOnExpertDevice stays the public symbol and
becomes a wrapper over LaunchGemma4Fp8ExpertGeGLUPrefillPeer and
FinishGemma4Fp8ExpertGeGLUPrefillPeer. The cache pin now lives on the
PeerSlot and is released only after the host has observed retirement, never
after a mere enqueue:

  • Launch pins under the same mutex scope as GetLocked and stores
    {cache_pin, cache_dev} on the slot before any GEMM is enqueued. It never
    unpins.
  • Finish host-waits ev_e, then hipStreamSynchronize on the compute stream
    that received the output copy, and only then unpins under the lock.
  • A failed fill or ready-event keeps a fill lease; the lease is retired with
    RetireFillLocked after the producer-stream sync and never under cache.mu.
  • A restore failure after publish retires before it rethrows; a failed retire
    quarantines the pin rather than dropping it.
  • The same-dev arm follows the same rule. "Enqueue then unpin" is forbidden on
    both arms, because a hipStreamWaitEvent on the compute stream is not a
    host-side retirement proof.

Peer-pipe overlap stays default OFF, so Launch and Finish run back-to-back on
slot 0 and the event order is the one shipping today plus slot-scoped scratch.

What the tests prove

tests/vt/test_ops_gemma4_prefill_peer.cpp is 23 cases over the host lifetime
model and the product source. The lifetime cases drive HostLaunch/HostFinish;
the product is bound to them by source-slice gates that extract the real
Finish and RestoreComputeOrThrow bodies out of
src/vt/rocm/rocm_gemma4_experts.hip, compile them, run them, and require each
mutation to red. Replacing the hipSetDevice(compute_dev) throw in the product
RestoreComputeOrThrow with (void)compute_dev; fails three assertions.

CI repairs in this revision

Three checks were red for reasons that had nothing to do with the change under
review, and all three are green on main.

build-test-cpu and both sanitize-cpu legs failed because the test named one
absolute HIP compiler from the contributor's box,
/opt/rocm-7.2.4/core-7.14/bin/hipcc, in four places and CHECKed that it
returned 0 — so any machine without that exact path failed rather than
reporting that the gate had not run. The toolchain is now resolved
(VLLM_CPP_HIPCC, HIPCC, ROCM_PATH/bin/hipcc, hipcc on PATH,
/opt/rocm/bin/hipcc, each probed with --version) the way
tests/vt/test_ops_getblas_product.cpp:15-21 resolves its own precondition, and
its absence prints a loud HIP COMPILE GATE NOT RUN banner on stderr instead of
asserting. Measured on this tree: 23/23 cases and 199 assertions with no
toolchain plus two banners, 23/23 and 205 assertions with VLLM_CPP_HIPCC set,
the six recovered assertions being exactly the HIP legs. Both arms still red on
the RestoreComputeOrThrow mutation, so the repair stopped the gate failing on
absent hardware without widening it.

Absent hipcc does not exit(77) the way test_ops_getblas_product.cpp does.
That file is one HIP gate and nothing else, while this one carries 21
host-lifetime cases that are the only gate CI has on this change, and exiting
would take those with it. The g++ leg of both compile gates still runs, so
neither case can report a zero-assertion pass.

pr-size refused the whole change with could not classify because nothing in
scripts/check-pr-size.py matches .agents/evidence/. The five donor slices
move beside the spec as SPEC_EVIDENCE .log files (.txt is unclassified
too) and the manifest becomes
.agents/specs/rocm-gemma4-prefill-peer-helper-donor.md. Renaming does not touch
bytes and all five SHA256 values in the table verify unchanged.

docs/FEATURES.md returns to main's text. The branch had rewritten that row
and lost the spec link, the VT_GEMMA4_*/VT_ATTN_* pointer and the
test_gemma4_rocm_fp8_seams seam name in order to fit; main's cell is already
219 of the 220 characters check-public-doc-tables allows. Nothing here owes
that page: check-doc-checkpoint has keyed feature_surface off a changed
REGISTER_VLLM_MODEL set since #595, and this change touches no file under
src/vllm/model_executor/models/.

The branch was rebuilt by rebase onto affc2a7fd, so it carries no untrailered
merge commit and all ten original commits keep their authorship. The
docs/USAGE.md conflict against #837's landed GetBlas text was resolved as a
union: both paragraphs survive.

What this does NOT close

Named here and under ## Owed in
.agents/specs/rocm-gemma4-prefill-peer-helper.md. Row
ROCM-GEMMA4-PREFILL-PEER-HELPER owns them and #839 stays open until they are
discharged.

  • No measurement for the new blocking retirement. Finish now host-waits
    hipEventSynchronize(ev_e) and hipStreamSynchronize(cst) on every call, and
    the same-dev arm gains one too, where the path was fully asynchronous before.
    It runs per expert per layer during prefill, so it serialises a pipeline that
    used to overlap. The wait is required for correctness and correctness comes
    first, so it stays; what is missing is the number. Prefill throughput before
    and after on 2x R9700 needs the hardware, and no CI runner here has it.
  • PeerSlot s[2] with only slot 0 reachable from the production wrapper.
  • The host simulator is a second implementation. HostLaunch/HostFinish in
    include/vt/rocm/rocm_gemma4_prefill_dequant_cache.h are hand-written
    analogues of the product Launch/Finish, ~250 of that header's lines, compiled
    into every HIP build. Moving them under tests/ is the fix; it was not done
    here because only a ROCm box compiles the one translation unit that consumes
    the header, so the move cannot be verified where this repair was made.

Known-unrelated CI

windows-msvc-cpu and windows-msvc-vulkan are red on every open PR from a
break predating this branch (#503, #584).

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: ClaudeCode:claude-opus-5 [ClaudeCode]

@localai-bot

Copy link
Copy Markdown
Collaborator

Sending this one back — the lifetime state machine is carefully thought through,
but three things need to change before it can land, and two of them are not
things we can repair for you.

1. The test hardcodes your toolchain path, so it fails everywhere else.

tests/vt/test_ops_gemma4_prefill_peer.cpp:438, :452, :522 and :531 each
pass "/opt/rocm-7.2.4/core-7.14/bin/hipcc -x c++" and then CHECK that the
compile returns 0. On any machine without that exact path it does not skip, it
fails. tests/CMakeLists.txt:1440 registers the suite unconditionally, so
this is the whole of the build-test-cpu, sanitize-cpu (address,undefined)
and sanitize-cpu (thread) red — one suite, 363 - test_ops_gemma4_prefill_peer,
23 cases / 21 passed / 2 failed. All three of those jobs are green on main, so
unlike the windows-msvc-* reds this one is genuinely attributable.

The minimum fix is to resolve hipcc from HIP_PATH/ROCM_PATH/PATH and
skip when absent — tests/vt/test_ops_getblas_product.cpp:20 already does
exactly that with std::exit(77), and your own #1045 uses that pattern.

2. Underneath that is a structural problem the path fix does not solve.

The suite shells out to a compiler at ctest time (std::system at :96, :98,
:158) and greps production source through VLLM_CPP_SOURCE_DIR (:17-27, used
by 8 of 23 cases). Nothing else in this tree tests that way.

More importantly,
include/vt/rocm/rocm_gemma4_prefill_dequant_cache.h:346-620 — roughly 250 of
the header's 624 lines — is a host simulator compiled into every HIP build, and
HostLaunch/HostFinish are a hand-written second implementation of the
production Launch/Finish logic. The tests exercise that copy; the only thing
tying it to the product is the source greps above. So a defect in the real
Launch would not move these tests. AGENTS.md is explicit on both halves
("never write a parallel path by hand", and a unit test that constructs the type
by hand proves the class works, never that anything reaches it). The shared state
machine — PrefillPeerLife, ChoosePrefillRetire, PrefillDequantCacheT,
ComputeDevGuard, RestoreFailed, SameDevLife, OutputCopyGate,
SlotReusable — should stay in the header; the Host* half belongs under
tests/.

3. The perf question the change raises, on a perf issue, is unanswered.

FinishGemma4Fp8ExpertGeGLUPrefillPeer now does a blocking
hipEventSynchronize(tls.ev_e) and a blocking hipStreamSynchronize(cst) on
every call, and the same-dev arm gains a hipStreamSynchronize(cst) too. Before
this change the function was fully asynchronous — record event,
hipStreamWaitEvent, async peer copy, return. It runs per expert per layer
during prefill, so this converts an overlapped pipeline into a host-serialized
one. Nothing in docs/BENCHMARKS.md, the spec, or the PR body records a prefill
throughput or TTFT number either side of it. We have no gfx1201 here, so this
one needs you.

A cross-PR landmine, which decides the order of your stack.

tests/vllm/models/test_gemma4_indexed_max_t.cpp:299-306 (from #1046) slices
rocm_gemma4_experts.hip between "bool RetireGemma4Fp8TopKIndexedPeer" and the
next "RunGemma4Fp8ExpertGeGLUPrefillOnExpertDevice", and asserts return true;
is absent from that gap. This PR inserts ~160 lines into exactly that gap. I
extracted the merged tree and confirmed the segment then contains return true;.
That suite is registered unconditionally and reads the .hip from disk, so it
runs on build-test-cpu — meaning #1047 landing after #1046 turns main
red. Best fixed in #1046 by anchoring that assertion to the function body rather
than to the gap between two symbols.

Smaller things, none blocking on their own

One last note that applies to all three of your open PRs: the Verification
section is identical in #1045, #1046 and #1047 and describes a single combined
#837+#838+#839 smoke on d1b0ea3a. Landing any one alone therefore lands
something never exercised in isolation. #1045 is in good shape and close to
landing; splitting your evidence per PR would let it go first.

localai-bot pushed a commit to bakon11/vllm.cpp that referenced this pull request Aug 17, 2026
…ce invariant that survives mudler#1047

Maintainer repairs applied while landing mudler#1046. No product behavior changes.

Donor evidence moves from `.agents/evidence/` -- a path
`scripts/check-pr-size.py` cannot classify, so the gate refused the change
outright -- to `.agents/specs/rocm-gemma4-indexed-max-t-donor.{md,log}`, which
match `SPEC` and `SPEC_EVIDENCE` at `check-pr-size.py:188-189`. The bytes are
unchanged: both slice sha256s still match the manifest table, which is what
that table exists to check.

`docs/FEATURES.md` is left as `main` has it. The branch had rewritten the
Gemma4 row, dropping the spec link, the `VT_GEMMA4_*`/`VT_ATTN_*` env pointer
and the `test_gemma4_rocm_fp8_seams` seam name. Restoring those and adding the
mudler#838 sentence does not fit -- `check-public-doc-tables` caps a cell at 220
characters and main's is already 219 -- and `check-doc-checkpoint` does not ask
for a FEATURES edit here, because `feature_surface` now keys off a change to
the set of `REGISTER_VLLM_MODEL` registrations (mudler#595, mudler#1086) and this branch
changes none. The mudler#838 detail stays where the branch already put it in full:
`docs/USAGE.md`, `docs/ENVIRONMENT.md` and the spec.

The source-invariant case in `test_gemma4_indexed_max_t.cpp` sliced
`rocm_gemma4_experts.hip` between `RetireGemma4Fp8TopKIndexedPeer` and the next
symbol, then asserted `return true;` was absent from the result. That is a
slice of the GAP between two functions, not of the function, so any unrelated
definition added after the closing brace lands inside it. Open PR mudler#1047 inserts
about 160 lines exactly there and carries its own `return true;`, which would
have turned `main` red on `build-test-cpu` after both landed -- this case is
registered unconditionally and reads the source from disk. It now brace-matches
the function's own body, and gains two assertions that the slice is bounded and
that the function does sync the compute stream, so the guarantee is stated more
completely than before rather than relaxed.

Proved by mutation against the real test binary, built standalone and pointed
at a scratch source root: the repaired case reds on a mutant that discards the
sync status and returns unconditional success (3 assertions fail), and stays
green under the mudler#1047-shaped insertion, where the original case reds. The
worktree sources were never mutated; the scratch copy was restored and its
sha256 compared against the tree.

The remaining review findings need a gfx1201 pair and are recorded under
`## Open on gfx1201 hardware` in the row spec instead of being papered over.
The `rw_idx` pooled buffer on the T=1 path contradicts Scope item 3's
hipGraph-stable invariant, and this branch still records no measurement for a
change that flips a product default across T=2..63.

The branch was rebuilt by rebase rather than merge, so it carries no
untrailered merge commit; all six original commits keep their authorship.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: ClaudeCode:claude-opus-5 [ClaudeCode]
localai-bot pushed a commit to bakon11/vllm.cpp that referenced this pull request Aug 17, 2026
…ce invariant that survives mudler#1047

Maintainer repairs applied while landing mudler#1046. No product behavior changes.

Donor evidence moves from `.agents/evidence/` -- a path
`scripts/check-pr-size.py` cannot classify, so the gate refused the change
outright -- to `.agents/specs/rocm-gemma4-indexed-max-t-donor.{md,log}`, which
match `SPEC` and `SPEC_EVIDENCE` at `check-pr-size.py:188-189`. The bytes are
unchanged: both slice sha256s still match the manifest table, which is what
that table exists to check.

`docs/FEATURES.md` is left as `main` has it. The branch had rewritten the
Gemma4 row, dropping the spec link, the `VT_GEMMA4_*`/`VT_ATTN_*` env pointer
and the `test_gemma4_rocm_fp8_seams` seam name. Restoring those and adding the
mudler#838 sentence does not fit -- `check-public-doc-tables` caps a cell at 220
characters and main's is already 219 -- and `check-doc-checkpoint` does not ask
for a FEATURES edit here, because `feature_surface` now keys off a change to
the set of `REGISTER_VLLM_MODEL` registrations (mudler#595, mudler#1086) and this branch
changes none. The mudler#838 detail stays where the branch already put it in full:
`docs/USAGE.md`, `docs/ENVIRONMENT.md` and the spec.

The source-invariant case in `test_gemma4_indexed_max_t.cpp` sliced
`rocm_gemma4_experts.hip` between `RetireGemma4Fp8TopKIndexedPeer` and the next
symbol, then asserted `return true;` was absent from the result. That is a
slice of the GAP between two functions, not of the function, so any unrelated
definition added after the closing brace lands inside it. A `git merge-tree` of
this branch with open PR mudler#1047 (`3df512a11`) grows that gap from 16 lines to
176 and puts three `return true;` inside it, which turns `main` red on
`build-test-cpu` once both land -- this case is registered unconditionally and
reads the source from disk. It now brace-matches the function's own body, and
gains two assertions that the slice is bounded and that the function does sync
the compute stream, so the guarantee is stated more completely than before
rather than relaxed.

Proved against the real test binary, built standalone and pointed at a scratch
source root. The repaired case reds on a mutant that discards the sync status
and returns unconditional success (3 assertions fail), and stays green on the
actual merged tree, where the original case reds with 1 failed assertion. The
worktree sources were never mutated; the scratch copy was restored and its
sha256 compared against the tree.

The remaining review findings need a gfx1201 pair and are recorded under
`## Open on gfx1201 hardware` in the row spec instead of being papered over.
The `rw_idx` pooled buffer on the T=1 path contradicts Scope item 3's
hipGraph-stable invariant, and this branch still records no measurement for a
change that flips a product default across T=2..63.

The branch was rebuilt by rebase rather than merge, so it carries no
untrailered merge commit; all six original commits keep their authorship.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: ClaudeCode:claude-opus-5 [ClaudeCode]
@localai-bot
localai-bot force-pushed the fix/gemma4-839-prefill-peer branch from 3df512a to cf31e5e Compare August 17, 2026 23:14
@localai-bot

Copy link
Copy Markdown
Collaborator

@bakon11 — I pushed three repairs to fix/gemma4-839-prefill-peer (3df512a11cf31e5e98, rebased onto affc2a7fd, all ten of your commits keep their authorship, no merge commit). Everything I could fix without an AMD box is fixed. Two things are left, and both need your hardware.

The three CI reds were not about your change. All three are green on main.

build-test-cpu and both sanitize-cpu legs: tests/vt/test_ops_gemma4_prefill_peer.cpp named /opt/rocm-7.2.4/core-7.14/bin/hipcc in four places and CHECKed that the compile returned 0, so any machine without that exact path failed rather than reporting that the gate had not run. The toolchain is now resolved — VLLM_CPP_HIPCC, HIPCC, ROCM_PATH/bin/hipcc, hipcc on PATH, /opt/rocm/bin/hipcc, each probed with --version — the way tests/vt/test_ops_getblas_product.cpp:15-21 resolves its own precondition, and its absence prints a loud HIP COMPILE GATE NOT RUN banner on stderr instead of asserting. Measured here: 23/23 cases and 199 assertions with no toolchain plus two banners; 23/23 and 205 with VLLM_CPP_HIPCC set, the six recovered assertions being exactly the HIP legs. Both arms still red on the RestoreComputeOrThrow mutation (3 failed assertions), so the gate did not get weaker — it stopped failing on absent hardware.

I did not exit(77) the way getblas does. That file is one HIP gate and nothing else; yours carries 21 host-lifetime cases that are the only gate CI has on this change, and exiting would take them with it. If you would rather have the HIP leg as its own skippable CTest entry, that is a clean follow-up — it needs the helpers moved into a tests/support/ header, which I did not want to do blind.

One thing worth knowing for next time: at lines 454 and 531 the old test asserted mut_hipcc.run != 0 after the compile had already failed, so run was 127 and those two assertions passed vacuously. A mutant that fails to build reads as a caught defect. The repaired version asserts the compile status separately.

pr-size refused the whole PR with could not classify because nothing in scripts/check-pr-size.py matches .agents/evidence/. The five donor slices now sit beside the spec as SPEC_EVIDENCE .log files (.txt is unclassified too, so .log rather than a checker change), and MANIFEST.md became .agents/specs/rocm-gemma4-prefill-peer-helper-donor.md. Renaming does not touch bytes — I re-ran sha256sum and all five values in your table verify unchanged.

docs/FEATURES.md is back to main's text. Your rewrite dropped the spec link's companions — the VT_GEMMA4_*/VT_ATTN_* pointer and the test_gemma4_rocm_fp8_seams seam name — to make room, and main's cell is already 219 of the 220 characters check-public-doc-tables allows, so it cannot absorb a sentence. Nothing here owes that page anyway: check-doc-checkpoint has keyed feature_surface off a changed REGISTER_VLLM_MODEL set since #595, and this PR touches no file under src/vllm/model_executor/models/.

The PR body now carries the trailer block, which is what commit-protocol-tag was failing on — your commits always had it.


What still needs you.

1. The blocking retirement has no measurement. FinishGemma4Fp8ExpertGeGLUPrefillPeer now host-waits hipEventSynchronize(tls.ev_e) and hipStreamSynchronize(cst) on every call, and the same-dev arm gains a sync too, where the path was fully asynchronous before. It runs per expert per layer during prefill, so it serialises a pipeline that used to overlap — on a perf-labelled issue with no number in docs/BENCHMARKS.md, the spec or the body.

To be clear about where I stand: I am not asking you to remove the wait. Design rule 2 in your own spec is right, correctness comes first, and "enqueue then unpin" genuinely is a use-after-free against a process-wide cache. What is owed is the cost, not a rollback. Prefill throughput before and after on the 2x R9700 at the T values you smoked this on, and if it is expensive, that becomes the next row rather than a reason to ship the race.

2. PeerSlot s[2] with only slot 0 reachable from RunGemma4Fp8ExpertGeGLUPrefillOnExpertDevice. Either a production path that reaches slot 1, or narrow the array. Recorded under ## Owed for now so it is visible debt rather than a surprise later.

I also recorded a third item under ## Owed that is on us, not you: HostLaunch/HostFinish in include/vt/rocm/rocm_gemma4_prefill_dequant_cache.h are a hand-written second implementation of the product Launch/Finish, about 250 of that header's 624 lines, compiled into every HIP build — so the lifetime cases exercise the analogue, not the product. What binds them today is your source-slice mutation gates, which is why this is debt and not a blocker. Moving the Host* half under tests/ is the fix; I left it alone because only your box compiles the one TU that consumes that header, so I could not verify the move, and it would churn your other branches' includes.

Ordering with #1046, measured on a real merged tree: your FunctionBody() brace-matcher does its job. On the merge of the two rebased branches, src/vt/rocm/rocm_gemma4_experts.hip auto-merges clean, the FunctionBody slice of RetireGemma4Fp8TopKIndexedPeer is 13 lines on both trees and contains no return true;, and the whole #838 suite is 10/10, 1101 assertions against the merged sources. The old symbol-to-symbol slice would have grown 17 → 177 lines and picked up a return true;, so the de-brittling was load-bearing, not luck. Nice catch.

@bakon11

bakon11 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

Accepted — thanks for the CI toolchain-resolution repairs (and the vacuous-mutation catch at 454/531). Queued on our box, landing on this branch on top of cf31e5e98: (1) the blocking-retirement cost measurement (before/after prefill throughput at the smoked T values — agreed the wait stays; the cost gets recorded, and if it's big it becomes the next row), (2) the PeerSlot s[2] slot-1 answer (wire or narrow). The Host* analogue-under-tests move we'll take as follow-up debt since only our box compiles that TU.

bakon11 pushed a commit to bakon11/vllm.cpp that referenced this pull request Aug 18, 2026
…ca41)

Do not run the historical async-unpin product arm. Attribution +
optional synthetic overlap microbench only. Slot-1 wiring declined;
narrow to s[1] is the merge-gate answer.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
bakon11 pushed a commit to bakon11/vllm.cpp that referenced this pull request Aug 18, 2026
Launch/Finish fail-closed on slot != 0. No overlap wiring.
Host 23/23. No GPU.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
bakon11 pushed a commit to bakon11/vllm.cpp that referenced this pull request Aug 18, 2026
One checkpoint: glance/open-gaps rows, forensic record, and the ROCm
STATUS clause. Attribution only; not a license to delete the waits.
Does not amend ba112f14.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
Don Mirror and others added 14 commits August 18, 2026 01:47
Independent row/ROCM-GEMMA4-PREFILL-PEER-HELPER from origin/main 3ce5a1d.
Pins donor Launch/Finish/cache bytes; does NOT copy unpin-before-ev_e.
Product pin lives in PeerSlot until host-observed ev_e retirement (64cb mudler#3).
Hypothesis C only; T=2029 accumulation cause unconfirmed.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
Pin complete PrefillDequantCache donor 60-209 (SHA256 cdb4da13…).
Pre-ev_e / event-record-fail path must hipStreamSynchronize expert
(same-dev: compute) stream before unpin. Ensure rejects reconfiguration
while any pin is live. 231f38cf is not a review target.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
Port prefill peer helper to Launch/Finish + PeerPipeTls slot 0.
Dequant cache pin lives on PeerSlot until host-wait of ev_e (or
stream sync on pre-record errors). Ensure rejects reconfiguration
while any pin is live. Same-dev uses tls_slots[2] + same pin rule.
kPeerPipe stays off. No FP8 Lt / mudler#697.

Host: 5/5 cases, 52 assertions.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
e3ec/3ece stop-ships on a fresh head (3a425654 is not a review target):
- shared PrefillDequantCacheT is the product+host state machine
- unpin only after observed event/stream retirement; else quarantine
- FreeAll walks all slots so partial Ensure allocs cannot leak
- miss records ready before publishing key; hit waits on ready
- Launch arms rollback after first expert enqueue
- every Launch/Finish return restores compute device or fails closed

Host: 10/10 cases, 85 assertions. No GPU / p42k.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
ebc3/3a78 stop-ships on a fresh head (a6678928 is not a review target):
- ChoosePrefillRetire: rollback before this-gen ev_e syncs current stream
- GetLocked unpublishes victim before Fill; publish only after RecordReady
- RestoreComputeOrThrow / ComputeDevGuard: restore fail is fatal
- SameDevLife quarantines pin; no reenter/reconfigure until retire
- scratch alloc publishes only after complete local init
- spec: failed-retirement quarantine is the sole zero-pin exception

Host: 13/13 cases, 93 assertions. No GPU / p42k.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
c2ae/2bf6 stop-ships on a fresh head (e043208d is not a review target):
- GetLocked keeps a fill lease on Fill/RecordReady failure; slot stays non-reusable
- Same-dev persists pin on acquire; scratch published only after both allocs
- ComputeDevGuard: failed restore terminates on unwind; RestoreOrThrow throws
- Arm rollback after first successful ev_c enqueue; FailLaunchRestore syncs
  compute vs expert stream that actually received work
- Host SameDevSession + LifeFromSlot make early-unpin / this_gen remap / restore
  no-op mutations RED

Host: 18/18 cases, 136 assertions. No GPU / p42k.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
584e stop-ships on a fresh head (6dbaeafa is not a review target):
- HostLaunch/product GetLocked no longer retire under cache.mu (deadlock)
- Successful owning-stream retire uses RetireFillLocked, not UnpinLocked alone
- PublishThenRestoreOrThrow retires/quarantines before rethrowing RestoreFailed
- HostLaunch fail_record + restore-fail pin/pending assertions; product source gates

Host: 20/20 cases, 165 assertions. No GPU / p42k.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
978a addendum on a fresh head (8f66af43 / 6dbaeafa are not review targets):
- Finish RestoreFailed and catch(...) reconstruct cst (HIP compile)
- hipStreamSynchronize(cst) before pending_M/reuse after y copy
- OutputCopyGate two-stream mutation: wrong-stream retire cannot reuse scratch
- 584e fill-lease / restore-fail repairs remain

Host: 21/21 cases, 182 assertions. No GPU / p42k.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
6350 BLOCK on 8f66af43 is not a review target. Fresh head over 7f2689c0:

- Finish catch TU compiled with g++ and hipcc -x c++ (no GPU)
- Deleting catch `cst` decls makes that compile gate RED
- Product mutation deleting hipStreamSynchronize(cst) fails
  ProductFinishRetiresCstBeforeReuse
- HostFinish copy/retire stream ids: wrong-stream retire keeps
  pending; Launch refuses reuse while output_copy is live

Host: 23/23 cases, 200 assertions. No GPU / p42k.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
ea9c BLOCK on a007ec40 is not a review target. Fresh head:

- Extract product RestoreComputeOrThrow from rocm_gemma4_experts.hip
- Compile and run it with failing/succeeding hipSetDevice stubs
- Replacing the hipSetDevice check/throw with a no-op is RED
  (g++ and hipcc -x c++; no GPU)
- Discarded host mutated_restore lambda removed

Host: 23/23 cases, 205 assertions. No GPU / p42k.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
… the donor bytes

Three CI reds on this branch, none of them about the change under review.

`tests/vt/test_ops_gemma4_prefill_peer.cpp` named one absolute HIP compiler from
the contributor's box, `/opt/rocm-7.2.4/core-7.14/bin/hipcc`, in four places and
CHECKed that it returned 0. On every machine without that exact path the gate
therefore FAILED rather than reporting that it had not run, which is the whole of
`build-test-cpu` and both `sanitize-cpu` legs; all three are green on main. The
toolchain is now resolved the way `tests/vt/test_ops_getblas_product.cpp:15-21`
resolves its precondition -- `VLLM_CPP_HIPCC`, `HIPCC`, `ROCM_PATH/bin/hipcc`,
`hipcc` on PATH, `/opt/rocm/bin/hipcc`, each probed with `--version` -- and its
absence prints a loud "HIP COMPILE GATE NOT RUN" banner on stderr instead of
asserting. Absent hipcc does not exit 77 the way getblas does: that file is one
HIP gate and nothing else, while this one carries 21 host-lifetime cases that are
the only gate CI has on this change, and exiting would take those with it. The
g++ leg of both compile gates still runs, so neither case reports a
zero-assertion pass, and the HIP-header compile that is genuinely ROCm-only is
recorded under `## Owed`.

Measured, on this tree: 23/23 cases and 199 assertions with no toolchain, plus
two banners; 23/23 and 205 assertions with `VLLM_CPP_HIPCC` set, the six
recovered assertions being exactly the HIP legs. Both arms still catch the
mutation the cases exist for -- replacing the `hipSetDevice(compute_dev)` throw
in the product `RestoreComputeOrThrow` with `(void)compute_dev;` reds with three
failed assertions with and without hipcc -- so the repair did not widen the gate,
it only stopped it failing on absent hardware.

`.agents/evidence/` matches nothing in `scripts/check-pr-size.py`, so
`MANIFEST.md` under it made a required check refuse the entire change with
"could not classify". The five slices move beside the spec as `SPEC_EVIDENCE`
(`.log`, because `.txt` is unclassified too) and the manifest becomes
`.agents/specs/rocm-gemma4-prefill-peer-helper-donor.md`. Renaming does not touch
bytes and all five SHA256 values in the table verify unchanged.

`docs/FEATURES.md` returns to main's text. The branch rewrote that row and lost
the spec link, the `VT_GEMMA4_*`/`VT_ATTN_*` pointer and the
`test_gemma4_rocm_fp8_seams` seam name to fit; main's cell is already 219 of the
220 characters `check-public-doc-tables` allows, so the row cannot absorb a
sentence. Nothing here owes that page anyway: `check-doc-checkpoint` keys
`feature_surface` off a changed `REGISTER_VLLM_MODEL` set since mudler#595, and this
change touches no file under `src/vllm/model_executor/models/`.

`## Owed` now names the three things this row does not close: the host simulator
that is a second implementation of Launch/Finish, `PeerSlot s[2]` with only slot
0 reachable, and the unmeasured cost of the new per-call blocking retirement.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: ClaudeCode:claude-opus-5 [ClaudeCode]
…ca41)

Do not run the historical async-unpin product arm. Attribution +
optional synthetic overlap microbench only. Slot-1 wiring declined;
narrow to s[1] is the merge-gate answer.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
Launch/Finish fail-closed on slot != 0. No overlap wiring.
Host 23/23. No GPU.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
One checkpoint: glance/open-gaps rows, forensic record, and the ROCm
STATUS clause. Attribution only; not a license to delete the waits.
Does not amend ba112f14.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Hermes:grok-4.6 [Hermes]
@bakon11
bakon11 force-pushed the fix/gemma4-839-prefill-peer branch from 57d3e22 to de8d476 Compare August 18, 2026 06:48
Brings the branch up to `main` so GitHub can compute the merge, and resolves the
three record conflicts as unions rather than picks, per AGENTS.md: take the
complete target-branch version, then re-apply the scoped edit.

`.agents/benchmark-record.md` keeps both appended sections, `main`'s
ENG-CUDAGRAPH-BREAK W1 entry and this branch's ROCM-GEMMA4-PREFILL-PEER-BARRIER
entry, because it is an append-only log and either side alone drops a
measurement. `docs/STATUS.md` keeps `main`'s M4 gate results, which landed with
mudler#559, and re-appends this branch's mudler#1047 item-3 attribution sentence.
`docs/USAGE.md` keeps `main`'s KEEP recipe and its "This path does" wording from
mudler#676, with this branch's prefill-peer lifetime sentence re-applied ahead of it.

Every conflicted path was a record or documentation file. No `src/` or
`include/` path conflicted, so no product source was hand-resolved here; the
product changes this merge carries in are `main`'s own.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: claude:claude-opus-5 [claude-code]
@localai-bot
localai-bot merged commit 0794999 into mudler:main Aug 18, 2026
1 of 15 checks passed
localai-bot pushed a commit that referenced this pull request Aug 18, 2026
…d-only record

GitHub refused the squash while `merge-tree` was clean on the issue index, which
is the usual `merge=union` blind spot. This merge also carries one REAL conflict
that the union driver does not cover.

`.agents/benchmark-record.md` took an append from each side into the same
section: `ROCM-GEMMA4-PREFILL-PEER-BARRIER` on main (#1047 item 3) and
`LTX25-LORA-FUSE-SEAM` here (#1202). They are independent entries about
different rows, so both are kept, main's first, leaving this branch's as the
newest append. Nothing was dropped and no existing entry was edited, which is
what AGENTS.md requires of a keyed record: take the target-branch version and
re-apply the scoped edit rather than accept a three-way merge.

The issue index is verified rather than assumed, because the union driver can
interleave rows and reinstate a deleted one: 386 rows, zero duplicate row keys,
and #1202 and #1254 each present exactly once.
`check-issue-index-append-only.py` and `check-agent-record.py` both pass.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
@tbrasser tbrasser mentioned this pull request Aug 18, 2026
6 tasks
bakon11 pushed a commit to bakon11/vllm.cpp that referenced this pull request Aug 19, 2026
…ce invariant that survives mudler#1047

Maintainer repairs applied while landing mudler#1046. No product behavior changes.

Donor evidence moves from `.agents/evidence/` -- a path
`scripts/check-pr-size.py` cannot classify, so the gate refused the change
outright -- to `.agents/specs/rocm-gemma4-indexed-max-t-donor.{md,log}`, which
match `SPEC` and `SPEC_EVIDENCE` at `check-pr-size.py:188-189`. The bytes are
unchanged: both slice sha256s still match the manifest table, which is what
that table exists to check.

`docs/FEATURES.md` is left as `main` has it. The branch had rewritten the
Gemma4 row, dropping the spec link, the `VT_GEMMA4_*`/`VT_ATTN_*` env pointer
and the `test_gemma4_rocm_fp8_seams` seam name. Restoring those and adding the
mudler#838 sentence does not fit -- `check-public-doc-tables` caps a cell at 220
characters and main's is already 219 -- and `check-doc-checkpoint` does not ask
for a FEATURES edit here, because `feature_surface` now keys off a change to
the set of `REGISTER_VLLM_MODEL` registrations (mudler#595, mudler#1086) and this branch
changes none. The mudler#838 detail stays where the branch already put it in full:
`docs/USAGE.md`, `docs/ENVIRONMENT.md` and the spec.

The source-invariant case in `test_gemma4_indexed_max_t.cpp` sliced
`rocm_gemma4_experts.hip` between `RetireGemma4Fp8TopKIndexedPeer` and the next
symbol, then asserted `return true;` was absent from the result. That is a
slice of the GAP between two functions, not of the function, so any unrelated
definition added after the closing brace lands inside it. A `git merge-tree` of
this branch with open PR mudler#1047 (`3df512a11`) grows that gap from 16 lines to
176 and puts three `return true;` inside it, which turns `main` red on
`build-test-cpu` once both land -- this case is registered unconditionally and
reads the source from disk. It now brace-matches the function's own body, and
gains two assertions that the slice is bounded and that the function does sync
the compute stream, so the guarantee is stated more completely than before
rather than relaxed.

Proved against the real test binary, built standalone and pointed at a scratch
source root. The repaired case reds on a mutant that discards the sync status
and returns unconditional success (3 assertions fail), and stays green on the
actual merged tree, where the original case reds with 1 failed assertion. The
worktree sources were never mutated; the scratch copy was restored and its
sha256 compared against the tree.

The remaining review findings need a gfx1201 pair and are recorded under
`## Open on gfx1201 hardware` in the row spec instead of being papered over.
The `rw_idx` pooled buffer on the T=1 path contradicts Scope item 3's
hipGraph-stable invariant, and this branch still records no measurement for a
change that flips a product default across T=2..63.

The branch was rebuilt by rebase rather than merge, so it carries no
untrailered merge commit; all six original commits keep their authorship.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: ClaudeCode:claude-opus-5 [ClaudeCode]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ROCm Gemma-4 hyp C: prefill peer GeGLU helper is monolithic; retirement-safe Launch/Finish

3 participants