feat(LTX25-IC-LORA-REF-VIDEO): serve the IC-LoRA reference clip and its conditioning attention mask - #3024
Merged
Merged
Conversation
…ention mask A15 and A16 of the LTX-2.5 completion scope, specced as one row because upstream applies ConditioningItemAttentionStrengthWrapper at exactly one site and its argument is always the VideoConditionByReferenceLatent built two lines above it (iclora_utils.py:162-169). combined_image_conditionings never wraps, so A16 alone has nowhere to attach that upstream also attaches to. The spec records what settling gateability found first: the reference pixel path needs no media decoder, no checkpoint and no GPU. Ltx2ReadFrameDirectory is already video_preprocess over a frame directory, Ltx2ConvVideoEncode already runs multi-frame for retake, and ltx2_video_fixture.h is a real reduced checkpoint that Generate runs end to end. What is missing is the reference item's own geometry, the temporal subsample, and the whole mask half. It also records the defect this row retires. Ltx2ModalityInput::attention_mask and its whole consumption chain are built and dead: the only assignments in the tree are four lines in a BuildModalities test helper, and nothing in src/ writes either field. FOLLOWING_AGENTS_PROTOCOL Refs: #3020 Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…he oracle it will be gated on The case enters through LoadVideoEngine and Generate, which is where vllm_video_generate arrives, and asks for a render conditioned on a reference clip. It is red for the intended reason and the red is literal: ERROR: test case THREW exception: Unsupported LTX pipeline kind/version: 'ic_lora'/'2.5'. Recipes are resolved from an EXACT (kind, version) table The goldens beside it are produced by EXECUTING the pinned module, never by reading it. iclora_utils cannot be imported here because it reaches PyAV, so its three functions are lifted by AST and their own source text is compiled verbatim, and append_ic_lora_reference_video_conditionings is run with only its frame decode, its pixel preprocess and its VAE replaced by recording stubs, so every branch under test is upstream's own control flow. Every case carries the hypothesis it rejects and the generator REFUSES to write one whose separation is zero. That guard is not decorative: build_attention_mask on a fixture with no PRIOR conditioning item is elementwise equal to the plausible wrong reading, measured 0 separating elements, and such a golden is a mute switch. The fixture carries a prior item and separates on 4. The fixture renders at 128x128 rather than the 64x64 every other case uses, because the distilled recipe halves it for stage 1 and 64x64 would give a 1x1 spatial reference grid where row-major and column-major patchify are identical and a downscale factor of 2 collapses onto a factor of 1. FOLLOWING_AGENTS_PROTOCOL Refs: #3020 Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…ts attention mask, and retire a dead seam A15 and A16. An `ic_lora` render can now be conditioned on a reference clip through the public video ABI, and an optional per-region mask attenuates that clip's attention. WHAT WAS ACTUALLY MISSING, once gateability was settled, was small. Ltx2ReadFrameDirectory already IS upstream's video_preprocess over a frame directory, and Ltx2ConvVideoEncode already runs multi-frame for retake. What no code supplied was the reference item's own geometry: the divisibility refusal, `height // scale`, the temporal subsample that keeps index 1 rather than stepping from 0, and the stage-1-only placement. Those are ported here, together with the whole mask half — the area downsample with its causal first-frame carve-out, resolve_cross_mask, and build_attention_mask's block structure. THIS RETIRES A DEFECT THAT WAS ON MAIN. Ltx2ModalityInput::attention_mask, Ltx2PrepareSelfAttentionMask, self_bias on host and device and the additive bias inside vt::AttentionCross were all built and all correct, and nothing in src/ assigned either field: the only four assignments in the tree were in a BuildModalities test helper. ltx2_video.cpp now makes that assignment on a production path, and the reachability case renders through it. ONE PREDICATE ROUTES AND REFUSES. `serves_reference` is bound once from the recipe and read by the routing branch and by every refusal beside it, because a refusal and its route predicate written as two expressions is how this campaign shipped a silently wrong answer. TWO MEASUREMENTS SHAPED THE FIXTURE RATHER THAN THE OTHER WAY ROUND. The render is 128x128 because at 64x64 the distilled recipe's half-resolution stage 1 gives a 1x1 spatial reference grid, where row-major and column-major patchify agree and a downscale factor of 2 collapses onto 1. And the reference clips are CONSTANT frames, not ConditioningPpm's seeded ramp: the VAE downsamples 32x, two seeds of that ramp average to nearly the same latent (absmax 0.539 against 0.464) and the two renders came out byte-identical. The pixel claim is made on the skip-stage-2 arm for a measured reason recorded in the case: stage 2 re-noises to 0.909375, and on a 2-block random-weight DiT the surviving difference falls below 1/255. Four refusals this change had to reconcile, each rewritten rather than relaxed: the reference arm is served, so a message may no longer describe it as missing, and the case that held it to naming a live cause now holds it to naming the pipeline that serves it. The reference-IMAGE arm stays refused and is the one refusal in the file no later row can serve, because upstream has no such flag. test_ltx2_video: 131 cases, 5225 assertions, 0 failed. FOLLOWING_AGENTS_PROTOCOL Refs: #3020 Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…inst the executed oracle Thirteen cases against `tests/vllm/models/ltx2_iclora_reference_goldens.inc`, which the generator produces by importing and executing the pinned module. These are NOT the proof that anything is reached and the file says so: every case here stays green when the production call site is deleted, which is why the reachability cases live in test_ltx2_video and render through Generate. What each case can actually see is measured rather than asserted in prose. The build_attention_mask fixture carries a PRIOR reference token because without one the true block structure and the plausible reading "cross on all existing rows" are elementwise equal; the downsample is checked against both bilinear spatial interpolation and uniform temporal pooling; temporal_subsample is checked against `range(0, F, N)` case by case rather than once over the set, because the two agree at factor 1 and on a single-frame clip and an aggregate check would pass on a build that got only the live cases wrong. 13 cases, 97 assertions, 0 failed. FOLLOWING_AGENTS_PROTOCOL Refs: #3020 Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…o Owed entries this retires ltx2-gen grows the rest of upstream's two flags: --ref-video-strength is the second half of --video-conditioning PATH STRENGTH, and --conditioning-attention-mask with --conditioning-attention-strength is the whole of the other. The clip itself was already --ref-video. docs/models/ltx-2-5.md gets the invocation and, more usefully, the three things a reader cannot infer: the reference geometry comes from the ADAPTER's metadata and not from a flag, the adapter rides stage 1 and stage 2 runs bare, which is the mirror image of every other two-stage arm here, and a sub-1.0 strength with no mask is refused because upstream's own CLI cannot reach that branch either. ltx25-ic-lora.md's two Owed rows are struck through rather than deleted, with what discharged them and what did not: the reference-IMAGE arm is not discharged and never will be, because upstream has no such flag. Both rows cited #932 and #975, which 404 (#2899); they now point at #3020. FOLLOWING_AGENTS_PROTOCOL Refs: #3020 Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…r the one residual it found Twelve mutations, each confirmed APPLIED and COMPILED before its result was recorded, each restored with an emptiness assertion on git status. Ten were DETECTED. Both survivors are identities, and the difference between an identity and a blind spot is the whole content of this commit. M6 drops `scale != 1` from the divisibility guard. For scale 1 the remaining conjunct is `h % 1 || w % 1`, which is 0 for every h and w, so the two predicates agree on every input and no fixture can separate them. Upstream writes the guard and this port mirrors it; it states intent, not behaviour. M11 replaces `serves_reference` in the refusal with a string compare on pipeline_kind. Those are the same predicate over the recipe table as it stands, because ic_lora_reference is set by IcLoraRecipe alone. They stop being the same the moment a second recipe sets the flag — and that is the day this campaign's "a refusal and its route predicate must be the SAME predicate" lesson would be paid for again. So test_ltx2_pipeline now walks all eleven kinds against all four versions and requires exactly ONE resolved pair to carry the flag, naming ltx2_video.cpp's refusals as what must be re-read. It does not make M11 detectable today, because today it changes nothing; it makes the identity guarded instead of assumed. test_ltx2_pipeline: 76 cases, 4712 assertions, 0 failed. FOLLOWING_AGENTS_PROTOCOL Refs: #3020 Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
… what it did not The declared gate named a whole-tree ctest and this run did not produce one, so the spec says which fifteen suites were built and run instead, with their numbers, and why the whole-tree run is PENDING on CI rather than passed. The reason is mechanical and worth writing down because it will happen again: a kill -9 during this row's scheduling corrupted build/.ninja_deps, ninja discarded its dependency graph and scheduled all 1338 edges, and under -j 2 on a box carrying four other compiling agents at load 20-33 that does not finish. It reached 76 of 1338. A pending result on an external resource is not a pass, and saying so is cheaper than a green nobody can reproduce. The fifteen suites are every target that links a translation unit this change touches, plus ltx2-gen, the shipped CLI that reaches the new knobs. Nothing was narrowed to reach a green. FOLLOWING_AGENTS_PROTOCOL Refs: #3020 Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…an see it `mode="area"` pools output index i over `[floor(i*I/O), ceil((i+1)*I/O))`. The port writes that general form and its own comment names the hazard: an integer-stride box filter `[i*(I//O), (i+1)*(I//O))` is elementwise EQUAL to it whenever O divides I. Every fixture that reached the code was divisible. The only golden was 8 -> 2 and the e2e case reads a 64x64 mask and pools to 2x2, so replacing the general form with the stride form survived both suites entirely: 13/13 cases and 97/97 assertions in test_ltx2_iclora_reference, 131/131 and 5225/5225 in test_ltx2_video. Three mute fixtures, and the difference is not nothing — at 224 -> 3 it is ~2300x the port's error against torch. So this adds a second downsample fixture at 9 -> 2, where the two readings separate. The generator measures the separation the same way it measures every other case and refuses to emit a golden that cannot fail; this one separates on 0.08158010244369507, against a 1e-3 threshold. The same mutation now reds 2 assertions with max |diff| 0.0815801 against the oracle and 5.96e-08 against the stride reading, which is the mutant reproducing the rejected hypothesis exactly rather than merely differing from the truth. The fixture asserts its own indivisibility, because a later edit that made the shape divisible would restore the mute switch silently. FOLLOWING_AGENTS_PROTOCOL Refs: #3020 Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
… upstream does, with no mask `ic_lora.py:230-233` is UNCONDITIONAL. It sits in the method body, after `assert_resolution` and outside every `if args.conditioning_attention_mask is not None`. This port guarded its copy on a non-empty mask directory, and the two refusals below it did not close the hole: 1.5 is not below 1.0, so the scalar-arm refusal does not fire either. The result was a knob that accepted 1.5, refused nothing, and was then never read, because its only reader is inside the mask branch. Accepted, ignored, silent, on every pipeline. The new subcase supplies no mask directory at all and asserts the refusal by name. Without the fix it does not merely mis-assert: the render is SERVED, and the case fails on its own FAIL rather than on a CHECK, which is the shape of the defect rather than a restatement of it. The refusal now reads: ltx-2.5 video: 'conditioning_attention_strength' must be in [0.0, 1.0], got 1.500000. This is upstream's own refusal (ic_lora.py:230-233): the value multiplies a mask in [0, 1] and the product becomes a log-space attention bias, so a value above 1 would AMPLIFY attention rather than attenuate it. Ordering is unchanged where it mattered: 0.5 with no mask still passes the range check and still lands on the scalar-arm refusal. Two record repairs ride along, because both are prose this row falsified. The paragraph above `wants_image` said the reference arm "stays refused and #975 stays open". This row serves it, #975 is one of the five 404s #2899 tracks, and the paragraph was orphaned besides: it sat between an unrelated local and the new section header, and the correct version of the same text already stands beside `wants_reference_video`. Deleted rather than corrected, because the surviving copy is the one in the right place. `docs/models/ltx-2-5.md` named `ltx-2.5-ic-lora-depth.safetensors`, which the pinned checkout does not contain anywhere. The doc's convention is real published filenames, so an invented one reads as a pin. It is now an unmistakable placeholder, with the adapters upstream actually publishes named beside it and the reason none of them fits: upstream's only 2.5 IC-LoRA is DFRPipeline's stage-2 detailing adapter, and every published reference-control adapter is 2.3-era or 19b. Nothing was downloaded; this was verified against the pinned checkout at fd4ded7f alone. FOLLOWING_AGENTS_PROTOCOL Refs: #3020 Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
… list the tripwire walks Five record defects, each of which read as a measurement and was not one. The separation table recorded 0.1890125870704651 and 0.16791561245918274 for the mask downsample. The committed `.inc` says 0.20678424835205078 and 0.049749284982681274, and a byte-identical regeneration against the pinned checkout says the same. The wrong pair had already propagated through two briefings, so these are re-derived from the generated file rather than copied forward again. The new non-dividing case joins them. The generator was cited as `tools/ltx2/gen_iclora_reference_goldens.py`. That directory does not exist; the file is `scripts/gen-ltx2-iclora-reference-goldens.py`. `Ltx2ReadMaskFrameDirectory(dir, height, width)` was named in the anchor table and again in §4.2. `grep -rn` finds it in those two lines and nowhere else: the tree composes the mask read at the call site out of `Ltx2ReadFrameDirectory` and `Ltx2MaskVideoFromPixels`. A function named in a spec and absent from the tree sends the next reader looking for a seam that was never built. The mutation table said M1 reds four assertions. Re-measured, it reds three, and WHICH fourth it is matters more than the count: `trace.ic_lora_attention_mask_rows == 16` still passes under the mutant, because the trace records that the mask was built and the deleted line is the one that hands it to the model. Only the pixel compares see the drop. That is now written down, because a trace assertion is cheaper than a render and the next reader will be tempted. The table's other counts are left as measured and dated instead, since restating a mutation result against a tree it was not run on is the drift these tables exist to prevent. `ref_video_strength` with no `ref_video_dir` is the same accept-and-ignore shape as the strength defect this round fixed. It is recorded owed rather than fixed, because upstream cannot express it — `--video-conditioning` is `nargs=2` — so any refusal here would be a product decision and not a port. The M11 tripwire now DERIVES its kind list. It walked a hand-copied eleven-entry array, which is complete only until the next kind lands: a twelfth would have gone unwalked while the case went on reporting "exactly one". The kinds now come out of every `pipeline_kind == "..."` branch in `ltx2_pipeline.cpp`, the file a new kind must be written into to exist, and the count is asserted before it is used so a derivation that found nothing cannot pass silently. Measured: adding a twelfth branch to the resolver reds the case with `declares 12 kinds, not the 11 this tripwire was written against`, and the assertion count rises by two, which is the new kind actually being walked. The hand-copied version stayed green. Issue #3020's records section named `docs/USAGE.md`, and this change does not write it. Struck there rather than written, because that file no longer carries per-model commands: its own "Find a model recipe" section routes commands, weights, settings and limits to `docs/models/<family>.md`, which is where these knobs are, and it names no `ltx2-gen` flag at all. Writing them in both places would put one fact in two documents. FOLLOWING_AGENTS_PROTOCOL Refs: #3020 Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
Ten commits, no conflicts. Merged before the landing gate so what is measured is what would land, rather than a base ten commits stale. FOLLOWING_AGENTS_PROTOCOL Refs: #3020 Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…s no longer pending This spec recorded the whole-tree `ctest` as PENDING on CI, and gave the reason: a corrupted `.ninja_deps` turned it into a from-scratch 1338-edge rebuild, which under `-j 2` alongside four other compiling agents at load 20-33 reached 76 edges and stopped. That was an honest PENDING and it is now discharged, twice, on an idle box: fresh review ran it on `975bc0906` and this round ran it again on the merged head, both 100% tests passed, 0 failed out of 714, with the same 7 skips, every one GPU- or checkpoint-gated and none in this row's reach. The per-suite numbers move with it, because this round's repairs added assertions: 102 over 14 cases on the value suite and 5237 over 131 on test_ltx2_video. Recording it here rather than leaving the pull request to say it, because the spec is what a reader consults after the branch is gone. FOLLOWING_AGENTS_PROTOCOL Refs: #3020 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.
Gaps A15 and A16 of the LTX-2.5 completion scope, landed as one row. An
ic_lorarender can now be conditioned on a reference clip through the publicvideo ABI, and an optional per-region mask attenuates that clip's attention.
WHY THEY ARE ONE ROW. Upstream applies
ConditioningItemAttentionStrengthWrapperat exactly one site(
iclora_utils.py:168-169) and its argument is always theVideoConditionByReferenceLatentbuilt two lines above it;combined_image_conditioningsnever wraps. In this treeLtx2ConditionVideoByReferencehad zero production call sites, so A16 alone hadnowhere to attach that upstream also attaches to. Putting the mask on the
keyframe or image items that ARE reached would have invented behaviour upstream
does not have.
WHAT WAS ACTUALLY MISSING was smaller than the standing refusal claimed, and
settling that was the first thing this row did.
Ltx2ReadFrameDirectoryalreadyIS upstream's
video_preprocessover a frame directory, andLtx2ConvVideoEncodealready runs multi-frame for retake. What no code suppliedwas the reference item's own geometry — the divisibility refusal,
height // reference_downscale_factor, and the temporal subsample that keepsindex 1 rather than stepping from 0 — plus the stage-1-only placement and the
whole mask half. So no media decoder, no checkpoint and no GPU lease were
needed.
THIS RETIRES A DEFECT THAT WAS ON
main.Ltx2ModalityInput::attention_mask,Ltx2PrepareSelfAttentionMask,self_biason host and device and the additivebias inside
vt::AttentionCrosswere all built and all correct, and nothing insrc/assigned either field: the only four assignments in the tree were in aBuildModalitiestest helper intest_ltx2.cppandtest_ltx2_device.cpp.That is the shape
.agents/reachability.mdnames — a unit test that constructsthe type proves the class works, never that anything reaches it.
ltx2_video.cppnow makes that assignment on a production path.ONE PREDICATE ROUTES AND REFUSES.
serves_referenceis bound once from therecipe and read by the routing branch and by every refusal beside it, because a
refusal and its route predicate written as two expressions is how this campaign
shipped a silently wrong answer.
The oracle
Lightricks/LTX-2 @ fd4ded7f, EXECUTED.ltx_pipelines.iclora_utilscannot beimported here — it reaches PyAV, which this project does not vendor — so
scripts/gen-ltx2-iclora-reference-goldens.pylifts its three functions BY ASTand compiles their own source text verbatim, and runs
append_ic_lora_reference_video_conditioningswith only its frame decode, itspixel preprocess and its VAE replaced by recording stubs, so every branch under
test is upstream's own control flow.
EVERY CASE CARRIES ITS REJECTED HYPOTHESIS AND THE GENERATOR REFUSES A CASE
WHOSE SEPARATION IS ZERO. That guard is not decorative:
build_attention_maskon a fixture with no PRIOR conditioning item is elementwise equal to the
plausible reading "cross on all existing rows" — measured 0 separating elements
— so such a fixture is a mute switch. The shipped fixture carries a prior item
and separates on 4.
A THIRD MUTE FIXTURE SURVIVED TO FRESH REVIEW, and the repair is in this branch.
mode="area"pools output indexiover[floor(i*I/O), ceil((i+1)*I/O)), andthe port writes that general form. An integer-stride box filter
[i*(I//O), (i+1)*(I//O))is elementwise EQUAL to it wheneverOdividesI—and every fixture that reached the code was divisible. The only golden was
8 -> 2 and the reachability render reads a 64x64 mask and pools to 2x2, so
replacing the general form with the stride form survived BOTH suites entirely:
13/13 cases and 97/97 assertions on the value suite, 131/131 and 5225/5225 on
test_ltx2_video. The difference is not nothing — at 224 -> 3 the mutant is~2300x further from torch than the port is. A 9 -> 2 golden now separates them
on 0.08158010244369507, the fixture asserts its own indivisibility so a later
edit cannot restore the mute switch, and the same mutation reds 2 assertions:
max |diff| 0.0815801 against the oracle and 5.96e-08 against the stride reading,
which is the mutant reproducing the rejected hypothesis exactly.
Two measurements shaped the fixture
The reachability render is 128x128, not the 64x64 every other case uses, because
the distilled recipe halves it for stage 1 and 64x64 gives a 1x1 spatial
reference grid where row-major and column-major patchify agree and a downscale
factor of 2 collapses onto 1.
The reference clips are CONSTANT frames, not
ConditioningPpm's seeded ramp.The VAE downsamples 32x, so two seeds of that ramp average to nearly the same
latent — measured absmax 0.539 against 0.464, with the two renders identical to
the byte. Constant frames survive it: 20 against 235 give 0.434 against 0.637.
The pixel claim is made on the skip-stage-2 arm, which is upstream's own
--skip-stage-2(ic_lora.py:442-448) and not a test hook. MEASURED: stage 1'slatents for the two clips do differ (clean digests 7109293506366579875 against
12136535927875385707) and stage 2's inputs still differ, but stage 2 re-noises
to
STAGE_2_DISTILLED_SIGMAS[0]= 0.909375 and on a 2-block random-weight DiTthe surviving difference falls below 1/255.
Mutations
Twelve, each confirmed APPLIED and COMPILED, each restored with an emptiness
assertion on
git status. Ten DETECTED. Both survivors are identities,proved rather than argued:
scale != 1from the divisibility guard. For scale 1 the remainingconjunct is
h % 1 || w % 1, which is 0 for everyhandw, so the twopredicates agree on every input and no fixture can separate them.
serves_referencein the refusal with a string compare onpipeline_kind. Those are the same predicate over the recipe table as itstands, because
ic_lora_referenceis set byIcLoraRecipealone. They stopbeing the same the moment a second recipe sets the flag, so this change adds
the tripwire:
test_ltx2_pipelinewalks every kind against all four versionsand requires exactly ONE resolved pair to carry it, naming
ltx2_video.cpp's refusals as what must be re-read. The kind list isDERIVED, out of every
pipeline_kind == "..."branch inltx2_pipeline.cpp. It was a hand-copied eleven-entry array until freshreview, which is a list complete only until the next kind lands. Measured:
adding a twelfth branch now reds the case and raises the assertion count by
two, which is the new kind being walked; the hand-copied version stayed green.
M1, the reachability mutation, deletes the production
vin.attention_maskassignment: the reachability suite reds on 3 assertions — this said 4, and
fresh review re-measured it — while the value suite stays 97/97. M2 stops the
reference item being applied: the reachability suite throws while the value
suite stays 97/97.
WHICH ASSERTION DOES NOT RED UNDER M1 IS WORTH MORE THAN THE COUNT.
trace.ic_lora_attention_mask_rows == 16still PASSES: the trace records thatthe mask was built and how many rows it has, and the deleted line is the one
that hands it to the model, so the counter stays right about a mask nothing
consumes. Only the pixel compares see the drop. Gating this seam on the trace
alone would measure construction and not consumption.
The refusal fresh review found, and it was a silent one
ic_lora.py:230-233refuses aconditioning_attention_strengthoutside[0.0, 1.0]UNCONDITIONALLY. It sits in the method body, afterassert_resolutionand outside everyif args.conditioning_attention_mask is not None. This port guarded its copy on a non-empty mask directory, and the tworefusals below it did not close the hole either: 1.5 is not below 1.0, so the
scalar-arm refusal does not fire. The result was a knob that ACCEPTED 1.5,
refused nothing, and was then never read, because its only reader is inside the
mask branch. Accepted, ignored, silent, on every pipeline.
The guard is dropped and a gate rides with it. Without the fix the new subcase
does not merely mis-assert: the render is SERVED and the case fails on its own
FAIL, which is the shape of the defect rather than a restatement of it. Therefusal now reads:
Ordering is unchanged where it mattered: 0.5 with no mask still passes the range
check and still lands on the scalar-arm refusal.
ref_video_strengthwith noref_video_diris the same accept-and-ignoreshape, and it is recorded OWED rather than fixed. Upstream cannot express it —
--video-conditioningisnargs=2— so there is no upstream reading to mirror,and inventing a refusal would be a product decision.
Gates
Run on the MERGED head —
origin/mainwas ten commits ahead and is merged in,so what was measured is what would land.
-j 2, CPU only, idle box.THE WHOLE-TREE GATE IS NO LONGER PENDING. This body previously recorded it
as PENDING on CI, because a
kill -9during the row's scheduling corruptedbuild/.ninja_deps, ninja rescheduled all 1338 edges from scratch, and under-j 2alongside four other compiling agents at load 20-33 that reached 76 of1338 and stopped. It has now been run twice on an idle box and it passes:
Fresh review, head
975bc0906: full build rc 0,ctest -j 2100% testspassed, 0 tests failed out of 714, 7 skipped.
This repair round, merged head: full build rc 0 over all 1337 edges, then
ctest --test-dir build -j 2 --output-on-failurerc 0, 100% tests passed, 0tests failed out of 714, the same 7 skipped. Every skip is GPU-gated or
checkpoint-gated and none is in this row's reach.
test_ltx2_iclora_reference102/102 over 14 cases,test_ltx2_video5237/5237 over 131,
test_ltx24581/4581,test_ltx2_pipeline4726/4726,
test_ltx2_retake69/69scripts/agent-preflight.sh: rc 0, 155okand 5SKIP, every skip achecker preflight cannot supply arguments for
check-pr-size.py --base origin/main --head HEAD: rc 0 — run explicitly,because it is one of the five preflight skips
agent-issue-index.py --refresh(316 open issues) thencheck-agent-record.pywith the snapshot PRESENT: rc 0Records
ltx25-ic-lora.md's two## Owedrows are struck through with what dischargedthem and what did not. Both cited #932 and #975, which 404 (tracked by
#2899); they now point at #3020, and so does the user-facing refusal text that
cited #932.
Refused by name and recorded owed: the EXR/HDR reference arm,
tiled_encodefora reference, reference IMAGES (upstream has no such flag, so this is the one
refusal in the file no later row can serve), the reference-AUDIO arm, the bf16
storage width of every buffer added here (the A24 campaign's standing
divergence), and upstream's scalar-only
strength < 1.0branch, which its ownCLI cannot reach either because the strength is assigned only alongside a mask.
FRESH REVIEW REPAIRED FIVE MORE RECORD DEFECTS, each of which read as a
measurement and was not one.
ltx2_video.cppsaid the reference arm "stays refused and LTX-2.5 reference video: the clip has no pixel path, and stage 2 must run UNFUSED — neither reason the refusal ever gave is still true #975stays open". This row SERVES it, LTX-2.5 reference video: the clip has no pixel path, and stage 2 must run UNFUSED — neither reason the refusal ever gave is still true #975 is one of 23 tracked files cite three issue numbers that 404 (#611, #1854, #2295), so those obligations cannot be traced #2899's five 404s, and the
paragraph was orphaned besides — it sat between an unrelated local and the new
section header, while the correct version of the same text already stood
beside
wants_reference_video. Deleted rather than corrected.docs/models/ltx-2-5.mdnamedltx-2.5-ic-lora-depth.safetensors, which thepinned checkout does not contain ANYWHERE. The doc's convention is real
published filenames, so an invented one reads as a pin. It is now an
unmistakable placeholder, with the adapters upstream actually publishes named
beside it and the reason none fits: upstream's only 2.5 IC-LoRA is
DFRPipeline's stage-2 detailing adapter, and every publishedreference-control adapter is 2.3-era or 19b. Verified against the pinned
checkout at
fd4ded7falone; nothing was downloaded.0.1890125870704651and0.16791561245918274. The committed.inc— and a byte-identicalregeneration against the pin — say
0.20678424835205078and0.049749284982681274. Re-derived from the generated file rather than copiedforward a third time.
tools/ltx2/gen_iclora_reference_goldens.py;that directory does not exist. It is
scripts/gen-ltx2-iclora-reference-goldens.py.Ltx2ReadMaskFrameDirectory(dir, height, width)twice.grepfinds it in those two lines and nowhere else: the tree composes the mask read
at the call site out of
Ltx2ReadFrameDirectoryandLtx2MaskVideoFromPixels. A function named in a spec and absent from the treesends the next reader looking for a seam that was never built.
Issue #3020's records section named
docs/USAGE.md, and this change does notwrite it. STRUCK there rather than written: that file no longer carries
per-model commands — its own "Find a model recipe" section routes commands,
weights, settings and limits to
docs/models/<family>.md, which is where theseknobs are — and it names no
ltx2-genflag at all. Writing them in both placeswould put one fact in two documents.
Spec:
.agents/specs/ltx25-ic-lora-ref-video.md, committed before anyimplementation.
Closes #3020
FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]