feat(LTX25-KEYFRAME-INTERP): the interpolation pipeline, on the conditioning builder that has no frame-0 branch (#1096) - #1209
Merged
Conversation
…rrect two of the three blockers #1096 recorded (#1096, #1187) `KeyframeInterpolationPipeline` (`keyframe_interpolation.py:55` @ `fd4ded7f`) is the last unported LTX-2.5 pipeline that is neither hardware- nor artifact-blocked. This commit is the spec alone, so that the commit order proves the spec preceded the code. #1096's audit named two blockers and a missing artifact, and this scoping pass re-derived all three at the pin rather than inheriting them. The per-sigma guided denoiser is STALE on this pipeline's default path. `main()` builds plain `MultiModalGuiderParams` (`:325-340`), so `create_multimodal_guider_factory` takes `constant()` and builds ONE `(inf, params)` bin (`guiders.py:312-315`) — `build_from_sigma` returns the same guider at every sigma and `FactoryGuidedDenoiser` delegates to `_guided_denoise`, which is what `Ltx2GuidedDenoise` ports. "Neither file is on the NAS" is stale too: both are byte-verified there and #1148 closed the pure-BF16 DiT refusal at `40a796aa9`. The multi-keyframe surface is real and is not the blocker. Two pinned keyframes at the two ENDS is what interpolation means at its default configuration; the general surface is filed as #1187 and owed. What the audit does NOT name is the difference that makes this pipeline what it is: `:211` and `:260` call `image_conditionings_by_adding_guiding_latent` (`helpers.py:343-367`), which has NO branch, where every other pipeline calls `combined_image_conditionings` (`:272-308`), which sends `frame_idx == 0` to `VideoConditionByLatentIndex` instead. Frame 0 is a keyframe that APPENDS, not a latent that REPLACES, and this engine hard-codes the other branch. A render on the wrong builder comes back the right size, the right frame count and the right sample rate with the image pinned into it; the only observable is the sequence length the DiT ran over. `audio_output_phase` is 1 here and 0 on `ti2vid_two_stage`, which shares this pipeline's parser, stage layout, stepper and sigma set. `:271` binds `video_state, audio_state` where `ti2vid_two_stages.py:289` binds `video_state, _` under its own "discard its audio" comment. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…tioning builder that has no frame-0 branch (#1096, #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. This 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, the same frozen three-sigma stage 2, the same `schedule_tokens = kSchedulerDefault` because `:200` calls `execute(steps=...)` with no latent. Two fields differ, and both of them render either way. The first is the row: `:211` and `:260` call `image_conditionings_by_adding_guiding_latent` (`helpers.py:343-367`), which has NO branch, where every other pipeline calls `combined_image_conditionings` (`:272-308`) and sends `frame_idx == 0` to `VideoConditionByLatentIndex`. So frame 0 is a keyframe that APPENDS a latent frame of tokens (`keyframe_cond.py:79-82`) rather than a latent that REPLACES them in place (`latent_cond.py:38-39`) — the first image is what the model interpolates FROM. That selection is now `Ltx2PipelineRecipe::image_conditioning`, defaulting to today's behaviour so no landed arm moves, read in exactly one place. A render on the wrong builder comes back the right size, the right frame count and the right sample rate with the image visibly present; the only observable is `Ltx2ConditioningTrace::video_tokens`, and the gate renders the same image on both kinds plus a bare control that pins the target grid. The second is `audio_output_phase`, which is 1 here and 0 on `ti2vid_two_stage`. `: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 "discard its audio" comment. Neither file argues the point, so the binding is the statement, and copying the neighbour would ship a soundtrack one refinement stage stale. Two of the three blockers #1096 recorded were stale and the spec re-derives all three. The per-sigma denoiser resolves ONE guider on this pipeline's default path, because `main()` passes plain `MultiModalGuiderParams` and `MultiModalGuiderFactory.constant` builds a single `(inf, params)` bin (`guiders.py:312-315`). Both checkpoints are on the NAS and #1148 closed the pure-BF16 DiT refusal. The multi-keyframe surface is real, is not what makes this pipeline different, and is #1187. Fixes #1191 in flow: `ltx2-gen` gains `--last-frame`. The ABI has carried `last_frame` and the engine has served it since #930, and this CLI had never read the field — which only starts to bite on a pipeline whose whole job is the motion between two pinned frames. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…ILD on its first shape, and a build failure has no test result (#1096) The row's `## Outcome`. Three things the code and the Git history do not carry. Deleting the whole `keyframe_interpolation` arm from `ResolveLtx2PipelineRecipe` left `KeyframeInterpolationRecipe` unreferenced in an anonymous namespace and `-Werror` killed the build: BUILT=NO, 1 error, no test result at all, behind a clean 26-line deletion in `git diff --stat`. The mutation that measures the thing renames the dispatch KEY instead, so every line stays compiled and referenced while the recipe stops being selectable through the request surface. Under it all six new cases go RED by name. Both runs are recorded; the first is the reason the harness prints four facts rather than two. The trace digest had to follow the tokens. `image_digest` reads the FRONT of the clean latent, which is where the replace arm writes; the append arm writes the TAIL. Left alone it would have reported a healthy conditioning for a state the keyframe never reached -- the exact failure the field's own comment says it exists to prevent, one arm down from where that comment is written. No test here compares the digest's value, so the range is correct because it was derived and not because a gate holds it, and that is said rather than implied. And #1096's audit was wrong about which blocker, in both directions, with one shape behind both errors: it read the pipeline's IMPORTS and inferred what would be hard. The import list says which machinery is reachable; only the call site says which behaviour runs. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
Four commits landed while this row was in flight: ENG-RESIDENCY-CONFIG, ENV-ORACLE-WHEEL-IN-LEASE, BACKEND-ROCM and the DSPARK-QWEN3-ROUTING spec. None touches the LTX-2.5 recipe table, the conditioning loop or `ltx2-gen`, so the merge is clean by construction rather than by luck; `.agents/issue-index.md` union-merged and was verified afterwards at 353 rows with 353 unique issue ids. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…, because the last-frame arm located its tokens by APPEND ORDER (#1219) Repairs six findings from the fresh review of #1096. The blocking one is a bug this row introduced two hundred lines away from the code that carries it. `ltx2_video.cpp`'s last-frame arm located its own appended tokens at `positions[target_tokens * 2]` and asserted their temporal position is `(frames - 1) / fps`. `target_tokens` is the phase's fixed target grid, so that index names the first token PAST the grid -- the first appended token in the sequence, whichever item appended it. It was this arm's own token only while this arm owned the first append. This row put a second appending item in front of it. On `image_conditionings_by_adding_guiding_latent` recipes the frame-0 image takes `VideoConditionByKeyframeIndex` and APPENDS (keyframe_cond.py:79-82) instead of replacing, and items are applied in list order. 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, plus a new check that the recorded `generated_keyframe_layout.first_token` equals that pre-append count -- a layout naming some other token counted the wrong window and still reported a healthy total. Three guarantees the review found ungated are now gated, and one is measured as ungateable: - `frame_idx = 0` on the new append arm. Changing it to 3 left both binaries green; the arm now asserts the first appended token spans `[0, 1/fps)`, recomputed from `fps` and never read back from the argument. - The trace's tail slice. Forcing `first` to 0 made `image_digest` and `image_absmax` describe the untouched FRONT of the sequence with both binaries green; the slice is now compared against the independently patchified conditioning volume, which serves both arms because both write those bytes. - `causal_fix = true` at that call site is INERT, not merely ungated, and no call-site check can detect a flip. MEASURED on a probe: at `num_pixel_frames = 1`, which both production arms pass, flipping it moves 0 of 48 position values at `frame_idx` 0 and 8 alike, because the temporal START clamps to 0 either way for a one-latent-frame keyframe and the `num_pixel_frames == 1` narrow overwrites the END the fix moved. The `frame_idx == 0` gate the argument passes through is what carries the risk, and it 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 -- the real `combined_image_conditionings` calls are `:231` and `:276`, and `:211` was the right line in the other file. And `ltx25-ti2vid-recipe.md` claimed this row moved the count owed to #1150 from six to five; the six-to-one split is the table's `latent` column, the count owed is three, and this row moved neither because the keyframe arm was unported rather than divergent. Two inherited off-by-N anchors are DEFERRED WHOLE to [#1230](#1230), and an earlier shape of this commit that corrected them on its own new lines is reverted. Both readings are right -- re-derived again here at the LTX-2 pin `fd4ded7f` by reading the pinned files: `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 the wrong move, because A PARTIAL CORRECTION IS STRICTLY WORSE THAN NONE. A uniformly wrong anchor is one grep away 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 a reader should 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. `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, so a one-line comment edit in a public header reads as a usage change and demands a `docs/USAGE.md` edit. There is no user-visible usage change in this commit. Writing one to turn the gate green is the move AGENTS.md forbids; removing the edit is the honest fix, and it retires the mixed file in the same stroke. This commit no longer touches `include/` at all. 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 this: 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. `#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. Owed in the row's spec. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
Three commits, none of them touching the LTX-2.5 conditioning path this branch edits, so the merge is clean and the gate is rerun on the merged tree rather than assumed from either side. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
localai-bot
force-pushed
the
row/LTX25-KEYFRAME-INTERP
branch
from
August 18, 2026 13:17
87e9f0e to
853384a
Compare
…nly conflict GitHub reported this branch CONFLICTING while `git merge-tree --write-tree` was clean, because GitHub ignores the `merge=union` driver that `.gitattributes` sets on `.agents/issue-index.md`. Both sides appended rows; only the forge saw that as a conflict. The pre-repair head conflicted identically, so this is not a defect introduced by the review repair. The union merge is verified rather than assumed, because that driver can interleave and can reinstate a deleted row: 365 rows, no duplicate row keys, this branch's #1219, #1220 and #1230 all present exactly once, and main's newest rows present. `check-issue-index-append-only.py` and `check-agent-record.py` both pass. Gate rerun on the merged tree, since main moved substantially: configure and build clean with zero compile errors, `ctest -N` 522, and 100% tests passed, 0 failed out of 522. `check-doc-checkpoint.py` against the true merge base exits 0. The LTX focused binaries are unchanged at 56/3316, 88/2755 and 43/3125, which is what proves the merge lost none of the repair. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
localai-bot
pushed a commit
that referenced
this pull request
Aug 18, 2026
#1209 landed and appended its own rows to `.agents/issue-index.md`, so GitHub refused the squash again while the local merge stays clean. GitHub ignores the `merge=union` driver `.gitattributes` sets on that path, and every concurrent append therefore reads as a conflict on the forge only. Verified rather than assumed, because the union driver can interleave rows and reinstate a deleted one: 376 rows, zero duplicate row keys, and this branch's #1202, #1208 and #1210 each present exactly once. `check-issue-index-append-only.py` and `check-agent-record.py` both pass. This branch carries no product code, so the merge cannot change a build result. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1096. Closes #1191.
pipeline_kind = keyframe_interpolationresolvesKeyframeInterpolationPipeline(
keyframe_interpolation.py:55@fd4ded7f) on all four generations the tablekeys. 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-resstage 1 on the UNADAPTED model, the same Euler stepper (derived — neither
self.stage_1(...)at:231norself.stage_2(...)at:271passes one, soutils/blocks.py:524-527applies), the same frozen three-sigma stage 2, and thesame
schedule_tokens = kSchedulerDefaultbecause:200callsexecute(steps=...)with no latent. Two fields differ, and both of them rendereither way.
The conditioning builder, which is what this pipeline is named after
:211and:260callimage_conditionings_by_adding_guiding_latent(
helpers.py:343-367). Every other pipeline callscombined_image_conditionings(
:272-308). The two differ by one branch:combined_image_conditionings..._by_adding_guiding_latentframe_idx == 0VideoConditionByLatentIndex(:295-300)VideoConditionByKeyframeIndexframe_idxVideoConditionByKeyframeIndex(:301-305)VideoConditionByKeyframeIndexlatent_cond.py:38-39)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_tokens12 onkeyframe_interpolationagainst 8 onti2vid_two_stageand 8 bare, withimage_tokens4 on both.audio_output_phaseis 1, and onti2vid_two_stageit is 0:271bindsvideo_state, audio_state = self.stage_2(...)and:293decodesthat name.
ti2vid_two_stages.py:289bindsvideo_state, _under its owncomment at
:287-288, "Stage 2 refines video only; discard its audio", anddecodes the name
:247bound. Neither file argues the point, so the binding isthe 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 andres2s_two_stage's 0, so itcannot 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:321named a multi-keyframe request surface, a per-sigmaguided denoiser, and two missing checkpoints. All three were re-derived at the
pin rather than inherited:
main()passes plain
MultiModalGuiderParams(:325-340), never a factory, socreate_multimodal_guider_factorytakes its last line,MultiModalGuiderFactory.constant(guiders.py:360), which builds_params_by_sigma = ((inf, params),)(:312-315) — ONE bin, sobuild_from_sigmareturns the same guider at every sigma andFactoryGuidedDenoiserdelegates to_guided_denoise, which isLtx2GuidedDenoise(landeddaeff67f2). The sigma-BINNED arm,MultiModalGuiderFactory.from_dict, is real, is reachable only by a callerwho constructs a factory, and is now The image-conditioning request surface is two fixed slots, against upstream's repeatable --image with a per-item frame index, strength and CRF #1187.
pure-BF16 DiT refusal at
40a796aa9. What is owed is the RUN.keyframes at the two ends is what interpolation means at its default
configuration. An interior
frame_idxis The image-conditioning request surface is two fixed slots, against upstream's repeatable --image with a per-item frame index, strength and CRF #1187.--last-frameonltx2-gen(#1191, in flow)vllm_video_paramshas carriedlast_frameand the engine has served it since#930;
ltx2-genparsed--first-frameand never read the field. That onlystarts 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-crfand strength the two slotsalready share.
The gate
Whole binaries, never a
--test-casefilter. Focused, at this head:test_ltx2_pipelinetest_ltx2_videoRED before the recipe landed, on the same binaries:
test_ltx2_pipeline56 / 54passed / 3183 assertions,
Status: FAILURE!, exit 1, both new cases throwingUnsupported LTX pipeline kind/version: 'keyframe_interpolation'/'2.5';test_ltx2_video87 / 83 passed / 2593, exit 1.Full gate at
251f76b5e, after mergingorigin/main:CONFIGURE_EXIT=0,BUILD_EXIT=0,: error:count 0,ctest -N515,CTEST_EXIT=0,100% tests passed, 0 tests failed out of 515.No space leftandBFDareeach 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
1d880bcc5before the merge, at 511 registered tests.The
READER ANCHORSinstrument inltx2_video.cppwas ARMED rather than assumedcorrect: inserting one line above
kKnownLoadExtrasshifted every derived anchorby 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.utimeand a confirmed ninjarecompile each time;
git statusis clean at the pushed head.Nine mutations, each printing four facts
git diff --stat, BUILT, the: error:count, and the exit code captureddirectly — with the expected OLD line content asserted unique before every edit.
recipe.image_conditioningassignment deletedaudio_output_phase1 -> 0stage1.schedule_tokensdeletedstage1.loras = kNoAdaptersdeletedrequires_distilled_loradeletedgen.steps3 -> 2M7 was run twice and the first run is recorded rather than dropped. Deleting
the whole dispatch arm left
KeyframeInterpolationRecipeunreferenced and-Werrorkilled the build —BUILT=NO, 1 error, no test result at all, which isexactly 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 countcomes back OUT of the render lambda and the trajectory recomputation runs at it,
with
rendered_steps > 2asserted by name, because at two stepsstretchpinsboth 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 theUNADAPTED 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:gpu0and 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 iswhat
ltx2-gendrives. #928 does not exclude the HTTP route here, because allthree knobs are LOAD extras and
requires_audio_inputis false; that is a claimabout the request surface, and no case here drives HTTP end to end.
Review repair
A fresh review of
251f76b5ereturned eight findings. Six are repaired in0e4357707; 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 whatltx2-gen --helptellsthe 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 = 0and thetrace's tail slice are now gated.
causal_fix = trueat that call site isINERT 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 == 1narrow overwrites the end the fix moved. No call-site check can detect that flip.
The risk lives in the
frame_idx == 0gate the argument passes through, and thatis gated in
test_ltx2_vaeatnum_pixel_frames != 1, where it shows.Two records are corrected:
ti2vid_two_stages.py:211is blank and the realcombined_image_conditioningscalls are:231and:276; and the claim thatthis 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
87e9f0e37returned two findings with one root cause. Anearlier 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.
Both readings are right, and that is not the point. Re-derived here at the
LTX-2 pin
fd4ded7fby reading the pinned files rather than inheriting thecitation:
latent_cond.py:38islatent_state = latent_state.clone()and:39is blank, so the two writes are
:40-41;schedulers.py:31is the returnannotation
) -> torch.FloatTensor:, so thetokens = math.prod(latent.shape[2:])read is:32. Correcting seven of thetwenty-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-39is a single mechanical edit for a single reviewer. A file citing BOTHforms is not.
src/vllm/multimodal/ltx2_video.cppreadlatent_cond.py:38-39at
:2208and:3174andlatent_cond.py:40-41a hundred lines later at:3392, with nothing in the tree recording which one to believe -- so the onefile a reader of this change opens was the one file stating both. The same split
ran through
schedulers.py:31intests/vllm/multimodal/test_ltx2_video.cpp,whose two near-identical schedule-anchor comments at
:7890and:8485disagreed 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:754was one of the seven,and
USER_USAGE_PREFIXESinscripts/check-doc-checkpoint.py:99is a pure pathmatch on
include/vllm/with no content analysis. A one-line comment edit in apublic header therefore reads as a usage change and demands a
docs/USAGE.mdedit.
.github/workflows/ci.yml:448runs that gate per commit over the range, sono 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.mdedit to turn thegate 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:check-doc-checkpoint.py--base 5af6e7631 --head 87e9f0e37commit 0e4357707: changed user_usage but did not update docs/USAGE.md--base 5af6e7631 --head 853384a8bOK: public documents match the claims this change makesThe 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-41and:32, because a record'sjob 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-41and:32, the source cites them uniformlyshort 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:625among them -- so the mixture is olderthan this change and outlives it either way.
#1230 is filed, listed under
## Owedin the row spec and appended to the issueindex, 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, noNo space left.ctest -N= 516 andctest -j3= 100% tests passed, 0 tests failed outof 516,
CTEST_EXIT=0.Focused, with ANSI stripped and
Status:read rather than inferred from the exitcode:
test_ltx2_pipeline56 cases / 3316 assertions,test_ltx2_video88 /2755,
test_ltx2_vae43 / 3125, allStatus: SUCCESS!at exit 0. The videocase 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-cpuandwindows-msvc-vulkanare red. They are red on every PR inthis repo -- confirmed on the unrelated #1186 and #1178 -- because those jobs are
PR-only with no
mainbaseline. Not attributable to this change.Gate rerun after the anchor revert
Rerun by the repairing session at
853384a8b, on the merged tree, in its ownworktree.
CONFIGURE_EXIT=0,BUILD_EXIT=0,: error:count 0,No space leftandBFDeach 0.ctest -N= 516 andctest -j3= 100% tests passed, 0 tests failed out of 516,CTEST_EXIT=0.Focused, whole binaries with no
--test-casefilter, ANSI stripped andStatus:read rather than inferred from the exit code:
Status:test_ltx2_pipelineSUCCESS!test_ltx2_videoSUCCESS!test_ltx2_vaeSUCCESS!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_videoholding at 88 rather than falling back to 87 is theload-bearing one, because a lost case is how a history rewrite drops work
silently.
Record gates on the rewritten range:
check-commit-trailersOK,check-commit-styleOK,check-issue-index-append-onlyOK,check-now-currentOK,
check-agent-recordOK.FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]