Name and Version
Built from the pwilkin strix-halo stack (merged into this repo by #63) at two
pins, both affected:
pwilkin/llama.cpp branch strix-halo @ be905cf7 (Sep 13 2026 build)
pwilkin/llama.cpp branch strix-halo @ d67d5883 (Sep 14 2026 build;
includes the sparse QSA decode rewrite and the incremental indexer state
with cache-lifecycle invalidation hooks)
Rest of the stack: installer pwilkin/strix-halo @ fa169251,
pwilkin/rocm-systems @ 7dda3ac6c (custom ROCr/HIP runtime), SDK ROCm
7.14.1. Hardware: Radeon 8060S (gfx1151) on Ryzen AI Max+ 395, 128 GB unified
memory, Linux 7.0.0-31-generic.
Operating systems
Linux
Which llama.cpp modules do you know to be affected?
- libllama (core library)
- llama-server
Command line
llama-server \
-m <qwen4exp target gguf> \
-md <shared-MTP draft gguf, Q8_0> \
--spec-type draft-mtp --spec-draft-device ROCm0 --spec-draft-ngl 99 \
--spec-draft-n-max 2 --spec-draft-p-split 0.10 \
-dev ROCm0 -ngl 999 -fa on -fit off \
--load-mode none --lazy-mode on-direct \
-ctk f16 -ctv f16 \
-c 262144 -b 16384 -ub 16384 --parallel 1 --metrics
-ctk/-ctv f16 is forced by the QSA kernels (the assert at
qwen4exp.cpp:1309 rejects non-F16 KV). Env vars come from the launcher
profile: HSA_OVERRIDE_GFX_VERSION=11.5.1,
GGML_HIP_ENABLE_UNIFIED_MEMORY=1, ENABLE_RETAINED_PM4=1.
Client side: POST /completion with temperature 0, n_predict 256, a prompt of
N tokens, then read the delta of llamacpp:spec_decode_num_draft_tokens_total
/ llamacpp:spec_decode_num_accepted_tokens_total on /metrics across exactly
that request (the counters are process-lifetime cumulative, so only deltas
are meaningful).
Problem description & steps to reproduce
With a qwen4exp target and the shared-MTP draft, acceptance is 0.0 whenever
the prompt is >= ~4k tokens (threshold not bisected between 200 and 4096);
shallow prompts accept normally. Greedy output is unaffected (canary hashes
byte-identical across spec / no-spec legs at both pins), so this is an
efficiency bug, not corruption: at depth, spec decode runs below the no-spec
control.
Acceptance vs prompt length, same server and session (pin d67d588,
n-max 2; acceptance identical at be905cf):
| prompt tokens |
acceptance |
tok/s (spec) |
tok/s (no-spec control, be905cf) |
| ~200 |
0.754 (765/1015) |
37.5 |
26.0 |
| 4096 |
0.0 (0/2535) |
15.2 |
24.4 |
| 32768 |
0.0 (0/2535) |
15.0 |
20.8 |
| 131072 |
0.0 (0/1521) |
14.5 |
14.1 |
What we ruled out:
- Not prefix-cache restore.
cache_prompt=false at 4k on d67d588:
accept 0/251 over 128 generated tokens.
- Not n-max. With n-max 3 acceptance is 0.0 at every depth including
shallow; dropping to n-max 2 restores shallow only.
- Not the draft weights. The draft file is the flash-next profile pin
mtp-Qwen3.8-Flash-Next-shared-Q8_0.gguf, sha256
5ff54097406a905cf3a724c709124ceb0e3e10235ee862298969e91c96fa96e6,
byte-identical to the installer pin. The same draft file with the same
target on a different llama.cpp build (Vulkan backend) accepts 816/1020
shallow, and 955/955, 955/955 and 573/573 at 4k/32k/128k.
- Not the engine's spec plumbing at depth in general. The published
27B IQ4_XS + DFlash2 pair through the same engine at a 31.5k prompt:
accept 218/220. That pair uses the DFlash2 draft path, not shared-MTP.
- Not output corruption. Greedy canary sha256 identical across spec /
no-spec / n-max 2 legs on this engine (a73e540c...).
- Not a stale build. Identical at be905cf and d67d588, i.e. across
the sparse QSA decode rewrite and the cache-lifecycle invalidation hooks.
One cell we did not run: this engine + shared-MTP + your own PROJFIX sharded
quant (93 GiB download). Our target was an own quant of the same public
weights, so a target-quant interaction is not fully excluded. But the draft
file is exactly yours and the same pair accepts normally at shallow length,
so the likelier cause is the qwen4exp shared-MTP path at length, not the
draft weights.
First Bad Commit
Not bisected. Present at both be905cf and d67d588.
Relevant log output
Server's own timing line for the cache_prompt=false 4k probe (d67d588),
confirming the same reading from the server side:
Sep 14 09:29:30 ... slot print_timing: id 0 | task 4148 | draft acceptance = 0.00000 ( 0 accepted / 251 generated), mean len = 1.00
AI disclosure (per AGENTS.md): assembled by an AI agent (Hermes, GLM)
from saved run artifacts on the machine above; every number is quoted from
those artifacts; the one untested cell is named; the operator reviewed this
report before filing.
Name and Version
Built from the pwilkin strix-halo stack (merged into this repo by #63) at two
pins, both affected:
pwilkin/llama.cppbranchstrix-halo@be905cf7(Sep 13 2026 build)pwilkin/llama.cppbranchstrix-halo@d67d5883(Sep 14 2026 build;includes the sparse QSA decode rewrite and the incremental indexer state
with cache-lifecycle invalidation hooks)
Rest of the stack: installer
pwilkin/strix-halo@fa169251,pwilkin/rocm-systems@7dda3ac6c(custom ROCr/HIP runtime), SDK ROCm7.14.1. Hardware: Radeon 8060S (gfx1151) on Ryzen AI Max+ 395, 128 GB unified
memory, Linux 7.0.0-31-generic.
Operating systems
Linux
Which llama.cpp modules do you know to be affected?
Command line
-ctk/-ctv f16is forced by the QSA kernels (the assert atqwen4exp.cpp:1309 rejects non-F16 KV). Env vars come from the launcher
profile:
HSA_OVERRIDE_GFX_VERSION=11.5.1,GGML_HIP_ENABLE_UNIFIED_MEMORY=1,ENABLE_RETAINED_PM4=1.Client side: POST /completion with temperature 0, n_predict 256, a prompt of
N tokens, then read the delta of
llamacpp:spec_decode_num_draft_tokens_total/
llamacpp:spec_decode_num_accepted_tokens_totalon /metrics across exactlythat request (the counters are process-lifetime cumulative, so only deltas
are meaningful).
Problem description & steps to reproduce
With a qwen4exp target and the shared-MTP draft, acceptance is 0.0 whenever
the prompt is >= ~4k tokens (threshold not bisected between 200 and 4096);
shallow prompts accept normally. Greedy output is unaffected (canary hashes
byte-identical across spec / no-spec legs at both pins), so this is an
efficiency bug, not corruption: at depth, spec decode runs below the no-spec
control.
Acceptance vs prompt length, same server and session (pin d67d588,
n-max 2; acceptance identical at be905cf):
What we ruled out:
cache_prompt=falseat 4k on d67d588:accept 0/251 over 128 generated tokens.
shallow; dropping to n-max 2 restores shallow only.
mtp-Qwen3.8-Flash-Next-shared-Q8_0.gguf, sha2565ff54097406a905cf3a724c709124ceb0e3e10235ee862298969e91c96fa96e6,byte-identical to the installer pin. The same draft file with the same
target on a different llama.cpp build (Vulkan backend) accepts 816/1020
shallow, and 955/955, 955/955 and 573/573 at 4k/32k/128k.
27B IQ4_XS + DFlash2 pair through the same engine at a 31.5k prompt:
accept 218/220. That pair uses the DFlash2 draft path, not shared-MTP.
no-spec / n-max 2 legs on this engine (
a73e540c...).the sparse QSA decode rewrite and the cache-lifecycle invalidation hooks.
One cell we did not run: this engine + shared-MTP + your own PROJFIX sharded
quant (93 GiB download). Our target was an own quant of the same public
weights, so a target-quant interaction is not fully excluded. But the draft
file is exactly yours and the same pair accepts normally at shallow length,
so the likelier cause is the qwen4exp shared-MTP path at length, not the
draft weights.
First Bad Commit
Not bisected. Present at both be905cf and d67d588.
Relevant log output
Server's own timing line for the cache_prompt=false 4k probe (d67d588),
confirming the same reading from the server side:
Sep 14 09:29:30 ... slot print_timing: id 0 | task 4148 | draft acceptance = 0.00000 ( 0 accepted / 251 generated), mean len = 1.00AI disclosure (per AGENTS.md): assembled by an AI agent (Hermes, GLM)
from saved run artifacts on the machine above; every number is quoted from
those artifacts; the one untested cell is named; the operator reviewed this
report before filing.