Skip to content

feat(ENG-CUDAGRAPH-DEDUP): fold captures that share a topology onto one graph executable - #1178

Merged
localai-bot merged 14 commits into
mainfrom
row/ENG-CUDAGRAPH-DEDUP
Aug 18, 2026
Merged

feat(ENG-CUDAGRAPH-DEDUP): fold captures that share a topology onto one graph executable#1178
localai-bot merged 14 commits into
mainfrom
row/ENG-CUDAGRAPH-DEDUP

Conversation

@localai-bot

@localai-bot localai-bot commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

feat(ENG-CUDAGRAPH-DEDUP): fold captures that share a topology onto one graph executable

Refs #1162 and #1184, deliberately not Closes on either. Row ENG-CUDAGRAPH-DEDUP in
.agents/engine-matrix.md, spec .agents/specs/eng-cudagraph-dedup.md, derived
by the analysis in #1161. #1162 owns every item under the spec's ## Owed --
the device A/B, the executable-count ratio, and the default flip -- and
docs/BENCHMARKS.md records that A/B as PENDING against it. The row stays
ACTIVE. Closing the issue on squash-merge would delete the tracking the
"Nothing lands dead" exception requires while three items are still owed. #1184
stays open for the same reason: the repair below is proven structurally on the
CPU tier and only a device can prove it on a device.

Every capture used to instantiate its own executable and throw the raw graph
away, so a model held one executable per padded decode bucket -- 7 at
max_num_seqs=32, 11 at 64 -- and nine capture drivers each held their own
set. grep -rn "cudaGraphExecUpdate" src include returned nothing. The decode
graphs of two padded batch sizes are usually the same node topology with
different parameters, which is the case cudaGraphExecUpdate exists for.

src/vt/graph_dedup.h is the registry: it keys captures by a structural
signature and re-points one executable instead of instantiating a second.
src/vt/graph_dedup_runtime.h binds it to CUDA and to HIP from one source,
because the two graph APIs differ only by symbol prefix and by two call shapes,
and a second hand-written copy is the parallel path the protocol forbids. Both
accelerator backends route EndCaptureGraph, ReplayGraph and DestroyGraph
through it, dispatching on Owns() rather than a mode flag so a handle minted
before the registry existed is still replayed as the plain executable it is.

The signature is a lookup key and never an authority. Every candidate fold is
probed with the real driver update on a throwaway executable first, and a probe
the driver refuses gives that capture its own executable, so a signature that is
too coarse costs a wasted probe and can never make a replay wrong. That is also
why the probe is transient where SGLang's is persistent: its backend seals the
capture phase and frees the probes, and ours has no capture phase to seal --
the drivers capture lazily, interleaved with replay -- so a persistent probe
would hold two executables per group forever, which is worse than the baseline
this row exists to reduce.

vLLM has no analogue, because its executables come from torch.compile. The
construction is therefore ported from the secondary oracle SGLang at the pinned
f63458b5be, cuda_graph_dedup_mixin.py:27-37,105-179,219-242,258-275,353-358,
with three divergences recorded in the spec's ## Upstream chain.

Not a throughput change

A deduped replay launches the same nodes with the same parameters. The
reportable axes are executable count and capture wall time, and the row must not
be sold as speed. It matters because on GB10 unified memory an out-of-memory
event reboots the box, capture time is startup latency, and bucket count is
exactly what #1163 would raise if it widened coverage.

Gates

  • RED first. tests/vt/test_graph_dedup.cpp was written and run before the
    header existed: fatal error: vt/graph_dedup.h: No such file or directory,
    exit 1. Then green, non-vacuous. Now 13 cases / 65 assertions, after the
    review repair below added four cases: two source-behaviour ones run red against
    the unfixed source first (6 failing assertions), one pinning the environment
    polarity, and one gating the loud-failure claim the transitivity note rests on.
  • Mutation, 13 of 13 detected (9 at implementation, 4 at review repair). Never fold; never re-point; always re-point;
    trust the signature without probing; keep a freed graph's address; Owns
    claims every pointer; wrong counts in the log line; inverted environment
    polarity; free the shared executable with the first sibling instead of the
    last. Every one printed a non-empty git diff --stat and a successful
    compile before its verdict, and every one restored byte-for-byte -- an earlier
    pass had to be re-run because the file was still untracked, so git diff --stat came back empty and would have hidden a mutation that never applied.
  • The suite measures the right thing. The fake device's launch records
    which graph the executable actually reflects, so "the right nodes ran" is an
    observable sequence rather than an assertion about intent, and every case
    replays more than once per shape: the risk of a shared executable is that the
    second visit to a shape is the one that has to re-point it. One case had to
    be added after the fact, because the original sibling-destroy case could not
    see a capture landing on a freed graph's address; the fake now hands that
    storage back and the new case reuses it deliberately.
  • CUDA compile. g++ -std=c++20 -fsyntax-only -Wall -Wextra -Werror against
    real CUDA 12.9 runtime headers, exit 0, over a translation unit that also
    mirrors the three call shapes cuda_backend.cu now uses. The cuda-fat-build
    job compiles the real thing on this PR.
  • scripts/agent-preflight.sh: All gates green., exit 0.

Review repair

A fresh review returned FAIL. Three findings were code or tests; three are
records, because fixing them would change behaviour the device A/B is measuring
on this branch, or is not code at all.

  • A failed cudaGraphInstantiate was silently accepted. Register assigned
    the result and moved on while the runtime returns null on failure. The
    pre-dedup path threw at the capture site with the driver's code; with dedup on
    the same failure minted a valid-looking handle over a null executable, counted
    it live in ExecCount(), and surfaced at the first replay as "graph launch
    failed" -- wrong site, wrong message, driver reason discarded. It now fails at
    the capture site and releases the capture whose ownership it had taken.
  • A failed probe drove a null executable. The probe instantiates a second
    executable and can fail on its own; cudaGraphExecUpdate has no defined
    behaviour for a null one. ProbeAccepts now answers "cannot fold", which
    degrades to today's one-executable-per-capture path.
  • The environment polarity was unpinned. Deleting && value[1] == '\0'
    left the whole suite green, so "10" enabling a default-off,
    correctness-sensitive path went undetected. The adversarial values are now
    asserted, and the deletion is a detected mutation.
  • The fold probed is not the fold replayed (comment and record only).
    Register probes (raws.front(), raw_graph); Replay issues
    (current_raw, entry.raw), and those diverge from a group's third member
    onwards -- so honouring the probe treats cudaGraphExecUpdate compatibility
    as transitive across a group, which no documentation states and nothing here
    asserts. The failure polarity is what makes it survivable: a refusal lands on
    the VT_CHECK in Replay, loudly, never on a silent launch of the previous
    contents. The comment claiming the fold was already probed is corrected to
    say what is actually assumed, and the loud-failure claim it now rests on is
    gated rather than asserted: a new case refuses exactly the
    (currently-reflected, target) pair Register never asks about, so all three
    probes succeed, the group folds to one executable, and the second replay
    throws. Deleting that VT_CHECK makes the replay launch graph 2's nodes under
    graph 3's handle. The stronger fix -- probe current_raw -- is owed, not
    taken: it changes probe behaviour while the A/B is measuring this code.
  • src/vt/graph_dedup_runtime.h has no executable coverage (record only).
    The mutation count is a statement about graph_dedup.h. The signature builder
    -- Kahn ordering, topological re-index, sorted edge emission, five node-payload
    cases, the depth-4 bound, five degradation escapes -- is reached by no test on
    any tier, and cuda-fat-build proves only that it compiles. The probe caps the
    blast radius so it can never cause a wrong replay, but one mode is silent: a
    signature unstable for some topology folds nothing while every CPU test stays
    green, with the device-side log line as the only observable. Stated in the spec
    and the matrix row, with the two device-tier tests that would cover it named
    and owed.
  • One unsynchronised registry serves every model on a device (record only).
    CudaBackend::dedup_ belongs to a process-singleton per-device backend, so the
    constraint is wider than the "one runner thread" note it carried. Correct
    today; recorded where a future capture-widening row will read it.

Also in this branch: merged up to ab6e6521, resolving the engine-matrix.md
conflict by taking both row sets -- #1177's ENG-CUDAGRAPH-BREAK content
byte-identical, this row's ACTIVE content with #1179's corrected NINE-driver
count carried onto it, and both moves composed in the summary counters. Every
other line of that file is byte-identical to both parents. The spec's Stats()
never existed and is corrected to the shipped CapturedCount() / ExecCount().

#1184: the file was allowed to see the runtime fail, and never consumed the latch

The device gate found that VT_CUDA_GRAPH_DEDUP=1 could not complete one decode
step once a graph was actually captured. 6/6 deterministic on GB10:

vt graph dedup: captured 1 graphs, deduped to 1 execs
[Qwen3DenseDecodeGraph] captured dense decode graph for padded size S=8 (real B=8)
engine-fatal: EngineCore busy loop threw:
  vt cuda: greedy_argmax launch: invalid device function

from a greedy_argmax launch that had SUCCEEDED. The OFF and =0 arms were
clean and byte-identical across 7 runs of the same binary against the same
libraries, which is what makes the asymmetry arm-attributable rather than an
environment artefact.

greedy_argmax was never at fault. The entire safety argument for this row is
that the signature is a lookup key and cudaGraphExecUpdate is the authority --
so a probe the driver REFUSES is normal operation, not an exception -- and the
topology walk has five more escapes of the same kind. A CUDA call that fails
also latches its code in the runtime's sticky per-thread slot, and none of the
twelve fallible calls in src/vt/graph_dedup_runtime.h consumed it: grep -n "cudaGetLastError\|hipGetLastError" on that file returned nothing. The next
unrelated kernel, launched with the ordinary kernel<<<>>>(...); Check(cudaGetLastError()) pattern this tree uses everywhere, then read our
routine refusal and reported it as its own failure.

Every symptom follows from the mechanism and none from the kernel named. It
needs BOTH dedup=1 and a real capture, because with no capture there is no
probe and no latch. CUDA_LAUNCH_BLOCKING=1 does not move it, because the latch
is host-side and synchronous rather than a deferred async error.
cudaGraphLaunch itself returns success, because reading a return value does
not consume the latch.

The repair is one type, not twelve clears. Twelve hand-placed
cudaGetLastError() calls are a fix the thirteenth fallible call silently
misses, and a file whose design is "these calls are allowed to fail" will grow a
thirteenth. So the clear lives in ScopedLatchClear's destructor
(src/vt/graph_dedup_latch.h, new, device-free) and is installed at the
binding's ENTRY POINTS, which are exactly the six GraphDedupOps members the
backends reach through Ops(). Every exit runs it: a plain return, a
degradation escape, and the VT_CHECK unwinding out of Launch.
MakeLatchGuardedOps is the table's only constructor and takes the raw
functions as template arguments, so no raw address reaches a field; a seventh
operation wired anywhere else leaves its field null, and GraphDedupRegistry's
constructor refuses an incomplete table, so the bypass does not merely get
discouraged, it does not construct. One line covers CUDA and HIP because there
is one source: VTGD_FN(GetLastError) resolves to cudaGetLastError or
hipGetLastError.

The fold and probe decision logic is untouched. This is error-state hygiene.

MEDIUM-3: the coverage gap that hid it

src/vt/graph_dedup_runtime.h was reached by no test on any tier --
grep -rn "graph_dedup_runtime\|AppendGraphSignature" tests/ returned nothing --
so its Kahn ordering, topological re-index, sorted edge emission, depth-4 child
bound and degradation escapes were compile-gated only. That is where #1184 sat
through a whole review cycle.

The device-free half moves to src/vt/graph_dedup_signature.h behind an Rt
policy, byte-identical in output, and the new
tests/vt/test_graph_dedup_runtime.cpp drives it with a fake runtime: 13 cases,
51 assertions.

  • RED first. With ScopedLatchClear's destructor emptied to the pre-fix
    state, the suite reports 22 failed assertions, including
    CHECK_NOTHROW(NextUnrelatedKernelLaunch()) THREW exception: "greedy_argmax launch: invalid device function" -- the production message reproduced from
    the mechanism alone, with no CUDA in the room. Then green, 13/13.
  • Mutation, 7 of 7 detected. The clear does nothing; one table field takes
    the raw address; the Kahn re-index becomes the identity; the edge sort is
    dropped; the depth bound moves 4 to 3; a child graph is noted but never
    walked; the unknown-endpoint escape becomes a continue. Each printed its
    git diff --stat, its compile status and its exit status, and restored
    byte-for-byte. One first attempt failed to build under -Werror and was
    re-run in a compiling form, because a mutation that fails to build reads as a
    passing test.
  • What the suite pins: that every entry point clears on every exit path
    including an unwinding one, that a refused fold leaves nothing latched for the
    next caller, that no table field skips the guard, and that the signature walk
    is order-independent, discriminating, bounded and degrading.
  • What it cannot pin, stated plainly: a CPU test drives a FAKE runtime. It
    cannot observe the CUDA runtime's real latched-error state, so it proves the
    guard's structure and not that VT_CUDA_GRAPH_DEDUP=1 dies after one replay: the signature walk latches a CUDA error that the next unrelated kernel reports as its own #1184 is gone on a device. The five
    node-payload cases stay device-only behind the policy.
  • Both arms compile. No CUDA toolkit was reachable from this session, so a
    scratch header-shape stub instantiated Ops() at CUDART_VERSION 12090 and
    13030 and on the HIP arm under -Werror: all three clean, with a negative
    control proving the instrument can fail. That is a proxy and not the gate --
    the CUDA 13 cudaGraphGetEdges break this file already took was invisible to
    exactly this kind of local check, and only cuda-fat-build reported it.

What the device gate also found, and it bounds this row

vllm-bench on the async path captures NO decode graph at all, because
DenseDecodeGraphForward returns nullopt whenever input.device_token_ids != nullptr -- the #323 mitigation that #1179 re-derived. No capture means no
registration, so dedup engages only under VT_ASYNC_RUNNER=0, which is not the
default and not what a user serves with. This changes nothing about the row's
correctness argument and it does change its worth: on the default path the
executables this row folds are executables that are never instantiated in the
first place. Recorded in the spec rather than sold around; the repair is the
StepDevInputs-shaped one #1179 already owns.

Owed, and named rather than implied

Scope

No driver internals, no bucket selection, no break points (#1163, parallel
owner), no diffusion path (#1164, blocked). docs/ENVIRONMENT.md changes only
because a config key does. scripts/check-gate-commands.py re-pins
RUNNABLE_BASELINE in the same change, which is what its own message demands
when a row enters the runnable population.

What was NOT verified on this head, stated plainly

This lands with two gaps named rather than papered over. Both are owed under #1162
and #1184, which stay open.

The device A/B never ran on the fixed code. dgx:gpu0 was held by another
session's oracle-build130 for over 90 minutes with this job queued at position 1.
The previous run, on the pre-fix head e4ce5571a, is what FOUND #1184: every
dedup=1 cell died after exactly one replay. The repair is therefore unverified on
hardware.
What the repair does have is a red-first test reproducing the exact
production string greedy_argmax launch: invalid device function from the mechanism
alone with no CUDA present, 7/7 negative mutations, and a structural design in which
the bypass does not compile.

cuda-fat-build did not report on this head. It passed on e4ce5571a earlier,
before the latch fix added graph_dedup_latch.h and the signature extraction, so the
CUDA leg of the current code is covered only by -Werror stub syntax checks at
CUDART_VERSION 12090 and 13030 plus HIP, each with a negative control proving the
instrument can fail. That job has already caught one real defect on this PR that every
local check passed through, so its silence here is a gap, not a pass.

Why landing anyway is the honest call: VT_CUDA_GRAPH_DEDUP is default OFF, the
unset and =0 arms were byte-identical across 7 runs, and nothing on a default
configuration reaches this code. The "Nothing lands dead" exception is satisfied
literally and was verified by a fresh reviewer. Holding the branch indefinitely on
another session's resource hold would not make any of it more true.

Also bounding this row's value, and measured rather than assumed: the signature
hashes gridDim/blockDim (graph_dedup_runtime.h:121-128), and decode buckets
differ precisely in launch dimensions, so buckets may be structurally unfoldable --
which is the premise #1162 was filed on. The first run logged captured 1 graphs, deduped to 1 execs in every ON cell, a 1:1 ratio with no fold. Separately, the shipped
async serving path captures no decode graph at all, so dedup engages only under
VT_ASYNC_RUNNER=0. Both are recorded in the spec.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]

mudler added 11 commits August 18, 2026 00:43
… per bucket

We instantiate a fresh `cudaGraphExec` for every capture and throw the raw graph
away (`src/vt/cuda/cuda_backend.cu:222-232`), so the executables multiply along
two axes: 7 padded decode buckets at `max_num_seqs=32` or 11 at 64, times eight
hand-rolled drivers. `grep -rn "cudaGraphExecUpdate" src include` returns nothing.

This spec lands before its implementation, as the protocol requires. It ports the
construction from SGLang's dedup mixin at the pinned `f63458b5be`, because vLLM
gets its executables from `torch.compile` and therefore has no analogue to
mirror, and it records the three places where our seam forces a divergence. The
load-bearing one: SGLang holds a second probe executable per group for the whole
capture phase and frees it in `seal()`, but our drivers capture lazily and
interleaved with replay, so there is no capture-phase end and a persistent probe
would hold two executables per group forever — worse than the baseline this row
exists to reduce. The probe is therefore transient, and a probe the driver
rejects falls back to a private executable instead of aborting, which is what
makes an under-specified signature safe rather than wrong.

The row moves `INVENTORIED` to `ACTIVE`, which owes `docs/STATUS.md`,
`docs/BENCHMARKS.md` and the spec's own `## Now`. The benchmark entry is recorded
`PENDING` rather than left absent: the device byte-identity A/B needs a leased
CUDA box, and this session has none.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…ne graph executable

Every capture used to instantiate its own executable and throw the raw graph
away, so a model held one executable per padded decode bucket — 7 at
`max_num_seqs=32`, 11 at 64 — and eight capture drivers each held their own set.
The decode graphs of two padded batch sizes are usually the same node topology
with different parameters, which is the case `cudaGraphExecUpdate` exists for.

`src/vt/graph_dedup.h` is the registry: it keys captures by a structural
signature and re-points one executable instead of instantiating a second.
`src/vt/graph_dedup_runtime.h` binds it to CUDA and to HIP from one source,
because the two graph APIs differ only by symbol prefix and by two call shapes,
and a second hand-written copy is the parallel path the protocol forbids. Both
accelerator backends route `EndCaptureGraph`, `ReplayGraph` and `DestroyGraph`
through it, dispatching on `Owns()` rather than a mode flag so a handle minted
before the registry existed is still replayed as the plain executable it is.

The signature is a lookup key and never an authority. Every candidate fold is
probed with the real driver update on a throwaway executable first, and a probe
the driver refuses gives that capture its own executable, so a signature that is
too coarse costs a wasted probe and can never make a replay wrong. That is also
why the probe is transient where SGLang's is persistent: its backend seals the
capture phase and frees the probes, and ours has no capture phase to seal, so a
persistent probe would hold two executables per group forever.

Default OFF behind `VT_CUDA_GRAPH_DEDUP`, and it stays off until measured. A
workload that alternates padded buckets every step pays one update per switch,
and the device byte-identity A/B that would price that needs a leased CUDA box
this session does not have. It is owed under the spec's `## Owed`, together with
the ROCm compile: no ROCm hardware or `hipcc` is reachable here and CI has no
ROCm job, so the HIP leg is written against the shared header but unverified.

Gated RED-first: `tests/vt/test_graph_dedup.cpp` was written and run against an
absent header, then 10 cases / 43 assertions green. The suite drives the registry
through a fake device whose launch call records which graph the executable
actually reflects, so "the right nodes ran" is an observable sequence rather than
an assertion about intent, and every case replays more than once per shape —
the risk of a shared executable is that the second visit to a shape is the one
that has to re-point it. One case had to be added after the fact: the original
sibling-destroy case could not see a capture landing on a freed graph's address,
so the fake now hands that storage back and the case reuses it deliberately.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
… just entered it

`ENG-CUDAGRAPH-DEDUP` reached `ACTIVE` with a spec whose `## Gates` section names
commands that can actually fail, so the row entered the runnable gated population
and `test_check_gate_commands` went red on the equality that makes the ratchet
work in both directions.

Growth, re-pinned in the same change and named the way the checker's own message
demands. The credit is not inherited: `ctest -R test_graph_dedup` detected 9 of 9
negative mutations of the registry the row adds, which is the strongest kind of
credit in this set rather than the weakest.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…erlapped

Three commits arrived while this branch was in flight: the block-wise FP8 load
refusal (#1172) and two GDN specs (#1173, #1174). None of them touches the vt
graph seam, the capture path, or the `ENG-CUDAGRAPH-DEDUP` row, and the only
keyed records they move are `.agents/issue-index.md` (append-only) and a kernel
matrix row this branch never wrote, so the automatic merge was verified rather
than trusted: the engine matrix still carries this row and its summary counts,
and `check-agent-record.py` re-runs clean at 160 engine rows.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…ts own mutation

`check-pr-size` refuses a change to a governance checker that arrives without
semantic mutation evidence in the paired suite, and it is right to: a re-pin that
only added a row to a frozenset would be indistinguishable from one that quieted
the gate.

Two cases, mirroring what `SPEC-MTP-K-GT-1` and `SERVE-RECIPE-ARGS` already
carry. The first pins both halves of the credit -- the row is in the exact pin,
AND its `## Gates` section really does yield a command that can fail -- and
asserts on `ctest -R test_graph_dedup` rather than on the `git diff --stat` the
extractor also finds, because that one exits 0 unconditionally in a repo and is
one of the weak credits this checker's own header names. It also asserts the
spec still says the device leg is owed, so the credit cannot quietly come to
rest on the CPU tier while the record goes silent about the arm nobody ran.
The second removes the entry and proves the set equality goes red.

Verified by mutation: deleting `ENG-CUDAGRAPH-DEDUP` from `RUNNABLE_BASELINE`
fails both new cases (7 of 39 in total), and the file restores byte-for-byte.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…erlapped

Two more commits arrived while this branch was in flight: the
`--speculative-config` key drop (#1175) and the inert `--gpu-memory-utilization`
budget (#1176). Neither touches the vt graph seam, the capture path, or the
`ENG-CUDAGRAPH-DEDUP` row, and the keyed records they move are
`.agents/issue-index.md` (append-only) and rows this branch never wrote, so the
automatic merge was verified rather than trusted: the engine matrix still carries
this row and its summary counts, and `check-agent-record.py` re-runs clean.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…h changed at CUDA 13

`cuda-fat-build` on `nvidia/cuda:13.3.0` failed with four errors at
`graph_dedup_runtime.h:161,168`: CUDA 13's `cudaGraphGetEdges` takes a fifth
`cudaGraphEdgeData*` parameter, where CUDA 12 and HIP take four.

The local syntax check could not have caught this and did not. It ran against
CUDA 12.9 runtime headers and passed, which is the honest limit of a proxy: a
header-shape check is only as current as the toolkit it ran against. Recorded in
the spec's `## Risks/decisions` beside the `cudaGraphExecUpdate` split, which was
foreseen, so the difference between the two is on the record.

Both shapes are bound in one `GetEdges` helper rather than at the two call sites,
so the topology walk stays one piece of code. The CUDA 13 arm fills a real edge
data buffer instead of passing null, because null edge data on the filling call
is not a shape the documentation promises; the data is discarded, since an edge
annotation is not part of the identity this key needs. Verified locally against
CUDA 12.9's `cudaGraphGetEdges_v2`, whose signature is identical to CUDA 13's:
both arms compile clean under `-Wall -Wextra -Werror`.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…#1179's corrected driver count

`origin/main` moved `ENG-CUDAGRAPH-BREAK` to `READY` and corrected the
hand-rolled decode-driver count from eight to nine (#1179: `9bc4d7f44` missed
the DFlash draft graph). This branch moved `ENG-CUDAGRAPH-DEDUP` to `ACTIVE`.
Both edits land on the same three lines of `.agents/engine-matrix.md`, so the
automatic merge conflicted.

Resolved by taking both row sets rather than either side: the `BREAK` row is
byte-identical to `origin/main`, the `DEDUP` row keeps this branch's `ACTIVE`
content with the corrected NINE-driver wording carried onto its baseline field,
and the two summary counters compose both moves (`READY` 3 to 4, `ACTIVE` 6 to
7, `INVENTORIED` 7 to 5 for the area; `READY` 12, `ACTIVE` 33, `INVENTORIED` 38
in the total). Every other line of the file is byte-identical to both parents,
verified by diffing the resolved file against each parent's version.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…apture happened, not at the first replay

Repairs the findings of the fresh review of #1178. Issue #1162.

`Register` assigned `ops_.instantiate(raw_graph)` and moved on, while
`graph_dedup_runtime.h` returns null on a driver failure. The pre-dedup path
wrapped `cudaGraphInstantiate` in `Check()` and threw at the capture site with
the driver's code; with dedup on the same failure minted a valid-looking handle
over a null executable, counted it live in `ExecCount()`, and surfaced at the
first replay as "graph launch failed" -- the wrong site, the wrong message, and
the driver's reason already discarded. `Register` now fails there, releasing the
capture whose ownership it had taken so the throw does not also leak it.

The probe instantiates a SECOND executable, so it can fail independently, and
`cudaGraphExecUpdate` has no defined behaviour for a null one. `ProbeAccepts`
now answers "cannot fold" instead of asking the driver about nothing, which
degrades to exactly today's one-executable-per-capture path.

Two guarantees were unpinned and are now tests. A capture the driver cannot
instantiate must throw and count nothing; a probe that cannot instantiate must
not drive a null executable. Both were run RED against the unfixed source (6
failing assertions) before the fix. The environment polarity gained the values
only the terminator check rejects: deleting `&& value[1] == '\0'` left the whole
suite green, so "10" enabled a default-off correctness-sensitive path
undetected. 12/12 cases, 61 assertions; three negative mutations applied,
detected and byte-restored.

Three findings are records rather than code, because fixing them would change
behaviour a device A/B is measuring on this commit or is not code at all.

`Replay` does not issue the fold `Register` probed: the probe tests
`(raws.front(), raw_graph)` and the replay issues `(current_raw, entry.raw)`,
which diverge from a group's third member onwards. Honouring the probe therefore
treats `cudaGraphExecUpdate` compatibility as transitive across a group, which
no documentation states and nothing here asserts. The comment claiming the fold
was already probed is corrected to say what is actually assumed; the stronger
fix -- probe `current_raw` -- is owed rather than taken, because it changes probe
behaviour mid-measurement.

`src/vt/graph_dedup_runtime.h` has zero executable coverage on any tier. The
mutation count is a statement about `graph_dedup.h` and must not be read as
covering the signature builder, whose one silent mode is a signature unstable
for some topology: it folds nothing while every CPU test stays green. Recorded
in the spec and in the matrix row, with the two device-tier tests that would
cover it named and owed.

The registry lives on a process-singleton per-device backend, so one
unsynchronised registry serves every model on a device. That is wider than the
"one runner thread" note it carried, and it is now stated where a future
capture-widening row will read it.

Also merged up: the spec's `Stats()` never existed and is corrected to the
shipped `CapturedCount()` / `ExecCount()`, and the decode-driver count moves
from eight to nine here too, after #1179 corrected it on main.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…unt correction broke

The nine-driver correction was spliced into a wrapped comment paragraph and left
one 129-column line in a file wrapped at 88. Reflowed the paragraph; no other
change. The signature note also said "described there" of a call site 150 lines
below it and then restated its own conclusion, so it now points at the call site
and says the thing once.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…ty note now rests on

The previous commit corrected the Replay comment to say that honouring
Register's probe assumes cudaGraphExecUpdate compatibility is transitive across
a group's members, and argued the assumption is survivable because a refusal
lands on a VT_CHECK rather than on a silent launch of the executable's previous
contents. Nothing gated that argument, and an argument for why a defect is
tolerable is exactly the kind of claim that has to be executable.

The fake driver can now refuse a (currently-reflected, target) PAIR, which is
the shape the target-only rejection set cannot express and the shape that
matters. Refusing exactly the pair Register never asks about reproduces the
case: all three probes succeed, the group folds to one executable, and the
second replay is where the assumption is tested for real. The case asserts the
throw AND that the launch log is unchanged.

Mutation, detected: replacing the VT_CHECK with `(void)ok` makes the replay
launch graph 2's nodes under graph 3's handle, which is the silent wrong answer
the check exists to prevent, and fails three assertions. Non-empty `git diff
--stat`, clean compile, byte-for-byte restore. 13/13 cases, 65 assertions.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
mudler added 3 commits August 18, 2026 07:31
… counts both sides moved

Both sides added to the same keyed records, so none of the three conflicts is a
disagreement. `.agents/engine-matrix.md` takes main's KV-cache row and its new
total row count, then re-applies this branch's own lifecycle move
(ENG-CUDAGRAPH-DEDUP INVENTORIED -> ACTIVE) on top: Engine and scheduling ACTIVE
6 -> 7 and INVENTORIED 6 -> 5, and the totals become 161 rows with ACTIVE 34 and
INVENTORIED 38. Every column and every row of the table sums.

`docs/BENCHMARKS.md` and `scripts/check-gate-commands.py` each keep BOTH
entries, in date order, because a second row arriving does not retire the first.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
… consume the latched error, and this one never did (#1184, #1162)

`VT_CUDA_GRAPH_DEDUP=1` could not complete one decode step once a graph was
actually captured. 6/6 deterministic on GB10:

    vt graph dedup: captured 1 graphs, deduped to 1 execs
    [Qwen3DenseDecodeGraph] captured dense decode graph for padded size S=8
    engine-fatal: EngineCore busy loop threw:
      vt cuda: greedy_argmax launch: invalid device function

from a `greedy_argmax` launch that had SUCCEEDED. The OFF and `=0` arms were
clean and byte-identical across 7 runs on the same binary and libraries, which
is what makes the asymmetry arm-attributable.

`greedy_argmax` was never at fault. The whole safety argument for this row is
that the signature is a lookup key and `cudaGraphExecUpdate` is the authority,
so a probe the driver REFUSES is normal operation, not an exception; the
topology walk has five more escapes of the same kind. A CUDA call that fails
also latches its code in the runtime's sticky per-thread slot, and none of the
twelve fallible calls in `graph_dedup_runtime.h` consumed it -- `grep -n
cudaGetLastError` on that file returned nothing. The next unrelated kernel,
launched with the ordinary `kernel<<<>>>(); Check(cudaGetLastError())` pattern,
then read our routine refusal and reported it as its own failure. Every symptom
follows from that and none from the kernel named: it needs BOTH `dedup=1` and a
real capture, `CUDA_LAUNCH_BLOCKING=1` does not move it because the latch is
host-side and synchronous rather than a deferred async error, and
`cudaGraphLaunch` returns success because a return value does not consume the
latch.

The fix is one type, not twelve clears. Twelve hand-placed `cudaGetLastError()`
calls are a fix the thirteenth fallible call silently misses, and a file whose
design is "these calls are allowed to fail" will grow a thirteenth. The clear
lives in `ScopedLatchClear`'s destructor and is installed at the binding's ENTRY
POINTS, which are exactly the six `GraphDedupOps` members the backends reach
through `Ops()`. Every exit runs it: a plain return, a degradation escape, and
the `VT_CHECK` unwinding out of `Launch`. `MakeLatchGuardedOps` is the table's
only constructor and takes the raw functions as template arguments, so no raw
address reaches a field; a seventh operation wired anywhere else leaves its
field null and `GraphDedupRegistry` refuses an incomplete table. One line covers
both arms because there is one source: `VTGD_FN(GetLastError)` resolves to
`cudaGetLastError` or `hipGetLastError`.

The fold and probe DECISION logic is untouched. This is error-state hygiene.

MEDIUM-3, the coverage gap that hid this for a whole review cycle, closes in the
same change. `graph_dedup_runtime.h` was reached by no test on any tier, so its
Kahn ordering, topological re-index, sorted edge emission, depth-4 child bound
and degradation escapes were compile-gated only. The device-free half moves to
`src/vt/graph_dedup_signature.h` behind an `Rt` policy and is driven by a fake
runtime in the new `tests/vt/test_graph_dedup_runtime.cpp`: 13 cases, 51
assertions, RED-first against the pre-fix guard, where the suite reports 22
failed assertions including the production message reproduced from the mechanism
alone. 7/7 negative mutations detected, each with its `git diff --stat`, compile
status and exit status; one first attempt failed to build under `-Werror` and
was re-run in a compiling form, because a mutation that fails to build reads as
a passing test.

BE HONEST ABOUT THE LIMIT. A CPU test drives a FAKE runtime. It cannot observe
the CUDA runtime's real latched-error state, so it proves the guard's structure
and not that #1184 is gone on a device. The device A/B re-run stays owed under
#1184, and the five node-payload cases stay device-only. The spec also now
records what the device gate found: the shipped async serving path captures no
decode graph at all (the #323 mitigation in `DenseDecodeGraphForward`), so dedup
engages only under `VT_ASYNC_RUNNER=0`, which materially bounds this row's worth
and is owned by #1179.

No local CUDA toolkit was reachable. A scratch header-shape stub instantiated
`Ops()` at CUDART_VERSION 12090 and 13030 and on the HIP arm under `-Werror`,
all three clean, with a negative control proving the instrument can fail. That
is a proxy; `cuda-fat-build` remains the gate, exactly as it was for the CUDA 13
`cudaGraphGetEdges` break this file already took.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
Takes the W1 break-point seam (#1192) and the rest of main so the range gates
can report and the PR is mergeable.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]

# Conflicts:
#	.agents/engine-matrix.md
@localai-bot
localai-bot merged commit 2a976eb into main Aug 18, 2026
1 check failed
@localai-bot
localai-bot deleted the row/ENG-CUDAGRAPH-DEDUP branch August 18, 2026 10:57
localai-bot added a commit that referenced this pull request Aug 18, 2026
…tioning builder that has no frame-0 branch (#1096) (#1209)

Closes #1096. Closes #1191.

`pipeline_kind = keyframe_interpolation` resolves
`KeyframeInterpolationPipeline`
(`keyframe_interpolation.py:55` @ `fd4ded7f`) on all four generations
the table
keys. Asking for it used to get the generic table refusal. It was the
last
unported LTX-2.5 pipeline that is neither hardware- nor
artifact-blocked.

Structurally it is `ti2vid_two_stage`: the same parser, the same guided
half-res
stage 1 on the UNADAPTED model, the same Euler stepper (derived —
neither
`self.stage_1(...)` at `:231` nor `self.stage_2(...)` at `:271` passes
one, so
`utils/blocks.py:524-527` applies), the same frozen three-sigma stage 2,
and the
same `schedule_tokens = kSchedulerDefault` because `:200` calls
`execute(steps=...)` with no latent. **Two fields differ, and both of
them render
either way.**

## The conditioning builder, which is what this pipeline is named after

`:211` and `:260` call `image_conditionings_by_adding_guiding_latent`
(`helpers.py:343-367`). Every other pipeline calls
`combined_image_conditionings`
(`:272-308`). The two differ by one branch:

| | `combined_image_conditionings` | `..._by_adding_guiding_latent` |
|---|---|---|
| `frame_idx == 0` | `VideoConditionByLatentIndex` (`:295-300`) |
`VideoConditionByKeyframeIndex` |
| any other `frame_idx` | `VideoConditionByKeyframeIndex` (`:301-305`) |
`VideoConditionByKeyframeIndex` |
| what frame 0 does | REPLACES latent frame 0's clean tokens; the count
never moves (`latent_cond.py:38-39`) | APPENDS a latent frame of tokens
(`keyframe_cond.py:79-82`) |

The second has no branch at all. The first image is a keyframe the model
interpolates **from**, not a frame it overwrites. This engine hard-coded
the
other arm, and both conditioning primitives were already ported and
gated — what
was missing is the SELECTION, now
`Ltx2PipelineRecipe::image_conditioning`,
defaulting to today's behaviour so nothing landed moves, read in exactly
one
place.

**Nothing about a render can see this.** The wrong builder returns a
clip of the
right size, the right frame count and the right sample rate with the
image
visibly present. It is conditioned; it is conditioned as a different
pipeline.
The only observable is the sequence length the DiT ran over, and the
gate renders
the same image at the same geometry on both kinds plus a **bare**
control that
pins the target grid — without which the comparison passes on a tree
where both
arms append. Measured: `video_tokens` 12 on `keyframe_interpolation`
against 8 on
`ti2vid_two_stage` and 8 bare, with `image_tokens` 4 on both.

## `audio_output_phase` is 1, and on `ti2vid_two_stage` it is 0

`:271` binds `video_state, audio_state = self.stage_2(...)` and `:293`
decodes
that name. `ti2vid_two_stages.py:289` binds `video_state, _` under its
own
comment at `:287-288`, "Stage 2 refines video only; discard its audio",
and
decodes the name `:247` bound. Neither file argues the point, so the
binding is
the statement — and copying the neighbour ships a soundtrack one
refinement stage
stale, at the right length and the right sample rate. The recipe case
asserts
both polarities beside `a2vid_two_stage`'s 1 and `res2s_two_stage`'s 0,
so it
cannot pass because every two-stage recipe happens to agree.

## Two of #1096's three blockers were stale, and the real difference was
in none

`.agents/issue-index.md:321` named a multi-keyframe request surface, a
per-sigma
guided denoiser, and two missing checkpoints. All three were re-derived
at the
pin rather than inherited:

- **The per-sigma denoiser is stale on this pipeline's default path.**
`main()`
passes plain `MultiModalGuiderParams` (`:325-340`), never a factory, so
  `create_multimodal_guider_factory` takes its last line,
  `MultiModalGuiderFactory.constant` (`guiders.py:360`), which builds
  `_params_by_sigma = ((inf, params),)` (`:312-315`) — ONE bin, so
  `build_from_sigma` returns the same guider at every sigma and
  `FactoryGuidedDenoiser` delegates to `_guided_denoise`, which is
  `Ltx2GuidedDenoise` (landed `daeff67f2`). The sigma-BINNED arm,
`MultiModalGuiderFactory.from_dict`, is real, is reachable only by a
caller
  who constructs a factory, and is now #1187.
- **Both checkpoints are on the NAS**, byte-verified, and #1148 closed
the
  pure-BF16 DiT refusal at `40a796aa9`. What is owed is the RUN.
- **The multi-keyframe surface is true and is not the blocker.** Two
pinned
  keyframes at the two ends is what interpolation means at its default
  configuration. An interior `frame_idx` is #1187.

## `--last-frame` on `ltx2-gen` (#1191, in flow)

`vllm_video_params` has carried `last_frame` and the engine has served
it since
#930; `ltx2-gen` parsed `--first-frame` and never read the field. That
only
starts to bite on a pipeline whose whole job is the motion between two
pinned
frames, so this row is the first caller it narrows. One flag, parsed and
assigned
beside `--first-frame`, sharing the `--image-crf` and strength the two
slots
already share.

## The gate

Whole binaries, never a `--test-case` filter. Focused, at this head:

| Binary | cases | assertions | exit |
|---|---|---|---|
| `test_ltx2_pipeline` | 56 | 3316 | 0 |
| `test_ltx2_video` | 87 | 2713 | 0 |

RED before the recipe landed, on the same binaries: `test_ltx2_pipeline`
56 / 54
passed / 3183 assertions, `Status: FAILURE!`, exit 1, both new cases
throwing
`Unsupported LTX pipeline kind/version: 'keyframe_interpolation'/'2.5'`;
`test_ltx2_video` 87 / 83 passed / 2593, exit 1.

Full gate at `251f76b5e`, after merging `origin/main`:
`CONFIGURE_EXIT=0`,
`BUILD_EXIT=0`, `: error:` count 0, `ctest -N` **515**, `CTEST_EXIT=0`,
`100% tests passed, 0 tests failed out of 515`. `No space left` and
`BFD` are
each 0 in the build and ctest logs, against injected controls that
returned 1.
Load average 50 to 76 across the run and 24 GiB free; none of the four
load-dependent gates (#618, #294, #1052, #428) went red at that load.
The same
gate ran green at `1d880bcc5` before the merge, at 511 registered tests.

The `READER ANCHORS` instrument in `ltx2_video.cpp` was ARMED rather
than assumed
correct: inserting one line above `kKnownLoadExtras` shifted every
derived anchor
by one and the gate went RED naming both lists. On the real tree
recorded and
derived agree at `[823 833 834 896 992 1008 1056 1147 1172 1277 1318
1360 1362]`,
because this row's edits are below the last anchor. The tree was
restored
byte-for-byte after every mutation, with `os.utime` and a confirmed
ninja
recompile each time; `git status` is clean at the pushed head.

### Nine mutations, each printing four facts

`git diff --stat`, BUILT, the `: error:` count, and the exit code
captured
directly — with the expected OLD line content asserted unique before
every edit.

| # | mutation | diff | built | errors | result |
|---|---|---|---|---|---|
| M1 | `recipe.image_conditioning` assignment deleted | 1 deletion | YES
| 0 | video rc=1 (1 case), pipeline rc=1 (2 cases) |
| M2 | reader forced to the REPLACE arm | 1+/2- | YES | 0 | video rc=1
(1 case) |
| M3 | `audio_output_phase` 1 -> 0 | 1+/1- | YES | 0 | pipeline rc=1 (2
cases) |
| M4 | `stage1.schedule_tokens` deleted | 1 deletion | YES | 0 | video
rc=1, pipeline rc=1 |
| M5 | `stage1.loras = kNoAdapters` deleted | 1 deletion | YES | 0 |
pipeline rc=1 |
| M6 | `requires_distilled_lora` deleted | 1 deletion | YES | 0 | video
rc=1, pipeline rc=1 |
| M7 | the dispatch key made UNREACHABLE | 1+/1- | YES | 0 | **all six
new cases RED**, by name |
| M8 | the COND arm left in velocity space | 3+/1- | YES | 0 | video
rc=1, 7 cases / 13 assertions |
| M9 | the anchor case's `gen.steps` 3 -> 2 | 1+/1- | YES | 0 | video
rc=1 (2 assertions) |

**M7 was run twice and the first run is recorded rather than dropped.**
Deleting
the whole dispatch arm left `KeyframeInterpolationRecipe` unreferenced
and
`-Werror` killed the build — `BUILT=NO`, 1 error, no test result at all,
which is
exactly the shape that reads as a passing test. Renaming the dispatch
key keeps
every line compiled and referenced while making the recipe unselectable
through
the request surface, which is what the reachability mutation is there to
measure.

M9 is the mutation `ltx25-ti2vid-recipe.md`'s first head passed. The
step count
comes back OUT of the render lambda and the trajectory recomputation
runs at it,
with `rendered_steps > 2` asserted by name, because at two steps
`stretch` pins
both non-zero sigmas and the schedule is `{1, 0.1, 0}` for every token
count.
Measured here: `keyframe: 4096 / 4096   res2s: 2 / 8`.

## What is NOT verified

**No real-weights render.** Upstream marks this arm `Full + distilled
LoRA`
(`packages/ltx-pipelines/CLAUDE.md:24`), so stage 1's identity is CFG on
the
UNADAPTED model and the checkpoint it needs is
`ltx-2.5-22b-dev-transformer-bf16.safetensors`. It is on the NAS and
loadable.
What is owed is a GPU lease and the two renders; another agent holds
`dgx:gpu0`
and no GPU work is in this row's scope. Running the arm against a
**distilled**
checkpoint instead would be worse than not running it — the distilled
scales are
trained into those weights, so a CFG-guided stage 1 samples a trajectory
they
were never trained for and renders a plausible clip with no diagnostic
(#1137).

The reach claim rests on the ABI path — `LoadVideoEngine` + `Generate`,
which is
what `ltx2-gen` drives. #928 does not exclude the HTTP route here,
because all
three knobs are LOAD extras and `requires_audio_input` is false; that is
a claim
about the request surface, and no case here drives HTTP end to end.

## Review repair

A fresh review of `251f76b5e` returned eight findings. Six are repaired
in
`0e4357707`; the other two are recorded rather than silently absorbed.

The blocking one was a bug this row introduced two hundred lines from
the code
that carries it. The last-frame arm located its own appended tokens at
`positions[target_tokens * 2]`, the first token past the fixed target
grid --
which is its own token only while this arm owns the first append. This
row put a
second appending item in front of it, so with both ends pinned the index
named
the FIRST frame's keyframe at temporal 0 and the arm threw. That is
`docs/USAGE.md`'s worked example for this kind and what `ltx2-gen
--help` tells
the reader to do, so the documented headline command did not run.

Neither assertion is weakened. Each arm now captures the sequence length
at the
moment of its own append and locates its tokens from that, so no arm
depends on
being first. The generated-keyframe-slot arm carried the same derivation
and is
repaired the same way.

Three guarantees the review found ungated are addressed, and one of the
three
turned out to be a different problem than reported. `frame_idx = 0` and
the
trace's tail slice are now gated. `causal_fix = true` at that call site
is
INERT rather than merely ungated, measured on a probe: at
`num_pixel_frames = 1`,
which both production arms pass, flipping it moves 0 of 48 position
values,
because the temporal start clamps to 0 either way and the
`num_pixel_frames == 1`
narrow overwrites the end the fix moved. No call-site check can detect
that flip.
The risk lives in the `frame_idx == 0` gate the argument passes through,
and that
is gated in `test_ltx2_vae` at `num_pixel_frames != 1`, where it shows.

Two records are corrected: `ti2vid_two_stages.py:211` is blank and the
real
`combined_image_conditionings` calls are `:231` and `:276`; and the
claim that
this row moved #1150's owed count from six to five was wrong on both
numbers and
on the premise, since the keyframe arm was unported rather than
divergent. Both
of those corrections stand.

Closes #1219.

#1220 is filed and NOT fixed: the two schedule-anchor cases return the
request
step count where their comment claims to read the render. It still
catches the
mutation it was built for, so it is a weakened guard rather than a
vacuous one,
and re-deriving it changes what a landed case measures on both
pipelines. Listed
under the row spec's `## Owed`.

## The scoped re-review: two off-by-N anchors, deferred whole

A scoped re-review of `87e9f0e37` returned two findings with one root
cause. An
earlier shape of the repair corrected two inherited off-by-N upstream
anchors on
the seven new lines that restated them. Those corrections are now
REVERTED and
the whole question is deferred to
[#1230](#1230).

**Both readings are right, and that is not the point.** Re-derived here
at the
LTX-2 pin `fd4ded7f` by reading the pinned files rather than inheriting
the
citation: `latent_cond.py:38` is `latent_state = latent_state.clone()`
and `:39`
is blank, so the two writes are `:40-41`; `schedulers.py:31` is the
return
annotation `) -> torch.FloatTensor:`, so the
`tokens = math.prod(latent.shape[2:])` read is `:32`. Correcting seven
of the
twenty-two citations was still wrong, because **a partial correction is
strictly
worse than none.**

A uniformly wrong anchor is one grep from being right, and a whole tree
citing
`:38-39` is a single mechanical edit for a single reviewer. A file
citing BOTH
forms is not. `src/vllm/multimodal/ltx2_video.cpp` read
`latent_cond.py:38-39`
at `:2208` and `:3174` and `latent_cond.py:40-41` a hundred lines later
at
`:3392`, with nothing in the tree recording which one to believe -- so
the one
file a reader of this change opens was the one file stating both. The
same split
ran through `schedulers.py:31` in
`tests/vllm/multimodal/test_ltx2_video.cpp`,
whose two near-identical schedule-anchor comments at `:7890` and `:8485`
disagreed after the correction and agree again now.

It also **cost a gate, which is the blocking half.**
`include/vllm/model_executor/models/ltx2_pipeline.h:754` was one of the
seven,
and `USER_USAGE_PREFIXES` in `scripts/check-doc-checkpoint.py:99` is a
pure path
match on `include/vllm/` with no content analysis. A one-line comment
edit in a
public header therefore reads as a usage change and demands a
`docs/USAGE.md`
edit. `.github/workflows/ci.yml:448` runs that gate per commit over the
range, so
no follow-up commit could clear it -- the commit itself had to change.
There is
no user-visible usage change here, so writing a `docs/USAGE.md` edit to
turn the
gate green is the move AGENTS.md forbids. Removing the header edit is
the honest
fix, and it retires the mixed file in the same stroke. **The repair
commit no
longer touches `include/` at all.**

Measured, on the rewritten repair commit `263f82b67`:

| range | `check-doc-checkpoint.py` |
|---|---|
| `--base 5af6e76 --head 87e9f0e` | exit **1**, `commit 0e43577:
changed user_usage but did not update docs/USAGE.md` |
| `--base 5af6e76 --head 853384a` | exit **0**, `OK: public
documents match the claims this change makes` |

The old head is kept as the control so the instrument is shown armed
rather than
assumed: the checker still reds on the pre-rewrite range from the same
working
tree that returns 0 on the new one. It is also 0 against today's
`origin/main`.

**The RECORDS go the other way on purpose and are not reverted.** The
spec's
port-map table and #1219's index row state `:40-41` and `:32`, because a
record's
job is to say what is true, and the index is append-only, so a wrong
anchor
written there could never be swept. What the record now says, and what
#1230
carries, is: the anchors are `:40-41` and `:32`, the source cites them
uniformly
short in twenty-two places, and one row corrects all of them at once.
The tree
already carried eight citations of the CORRECT form before this row
existed --
`include/vllm/multimodal/ltx2_video.h:625` among them -- so the mixture
is older
than this change and outlives it either way.

#1230 is filed, listed under `## Owed` in the row spec and appended to
the issue
index, and stays OPEN.

## Gate rerun by the operator

Rerun at `87e9f0e37`, not taken from the implementer's report.
`CONFIGURE_EXIT=0`, `BUILD_EXIT=0`, `: error:` 0, no `No space left`.
`ctest -N` = **516** and `ctest -j3` = **100% tests passed, 0 tests
failed out
of 516**, `CTEST_EXIT=0`.

Focused, with ANSI stripped and `Status:` read rather than inferred from
the exit
code: `test_ltx2_pipeline` 56 cases / 3316 assertions, `test_ltx2_video`
**88 /
2755**, `test_ltx2_vae` 43 / 3125, all `Status: SUCCESS!` at exit 0. The
video
case count moved from the reviewed head's 87 / 2713, which is what
proves the new
both-keyframes case ran rather than matching nothing -- a doctest filter
that
selects zero cases prints `SUCCESS!` and exits 0.

`windows-msvc-cpu` and `windows-msvc-vulkan` are red. They are red on
every PR in
this repo -- confirmed on the unrelated #1186 and #1178 -- because those
jobs are
PR-only with no `main` baseline. Not attributable to this change.

## Gate rerun after the anchor revert

Rerun by the repairing session at `853384a8b`, on the merged tree, in
its own
worktree. `CONFIGURE_EXIT=0`, `BUILD_EXIT=0`, `: error:` count **0**,
`No
space left` and `BFD` each **0**.
`ctest -N` = **516** and `ctest -j3` = **100% tests passed, 0 tests
failed out of 516**, `CTEST_EXIT=0`.

Focused, whole binaries with no `--test-case` filter, ANSI stripped and
`Status:`
read rather than inferred from the exit code:

| Binary | cases | assertions | `Status:` | exit |
|---|---|---|---|---|
| `test_ltx2_pipeline` | 56 | 3316 | `SUCCESS!` | 0 |
| `test_ltx2_video` | 88 | 2755 | `SUCCESS!` | 0 |
| `test_ltx2_vae` | 43 | 3125 | `SUCCESS!` | 0 |

Unchanged from the operator's rerun at `87e9f0e37`, which is the
expected result:
the revert touches eight comment lines and no executable statement.
`test_ltx2_video` holding at 88 rather than falling back to 87 is the
load-bearing one, because a lost case is how a history rewrite drops
work
silently.

Record gates on the rewritten range: `check-commit-trailers` OK,
`check-commit-style` OK, `check-issue-index-append-only` OK,
`check-now-current`
OK, `check-agent-record` OK.

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>
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.

2 participants