Skip to content

feat(LTX25-IC-LORA): read the IC-LoRA adapter, fuse it into every arm, and stop blaming the metadata (#923) - #938

Merged
localai-bot merged 9 commits into
mainfrom
row/LTX25-IC-LORA
Aug 16, 2026
Merged

feat(LTX25-IC-LORA): read the IC-LoRA adapter, fuse it into every arm, and stop blaming the metadata (#923)#938
localai-bot merged 9 commits into
mainfrom
row/LTX25-IC-LORA

Conversation

@localai-bot

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

Copy link
Copy Markdown
Collaborator

Closes #923. Row LTX25-IC-LORA, spec .agents/specs/ltx25-ic-lora.md. Upstream pin Lightricks/LTX-2 @ fd4ded7f2d88d3da713abcdd4ad41ecc4a9314ca, verified at the local checkout with a clean tree before any anchor was read.

Upstream ICLoraPipeline (ltx-pipelines/ic_lora.py) is video-to-video on the distilled model. This row builds the half of it that was actually missing — the adapter path — and leaves the other half refused, on the causes that genuinely remain.

The reference refusal named a seam that had already landed

This is the finding a fresh review returned FAIL on, and it is the most important thing in this pull request.

The refusal originally blamed the IC-LoRA metadata. That was true, and this row closed it. The row then rewrote the refusal onto the token-APPEND machinery, which was accurate on 2026-08-15 and false on 2026-08-16: row LTX25-TOKEN-APPEND landed that seam in c7cb59fbb while this pull request was open, and the LAST-frame keyframe arm is served on it today. #964 left the wording byte-identical only because #938 was open and its cause was then still true. Both reasons this message had ever given were false at once.

The determination was made again, from the merged tree, rather than inherited. The attention-strength wrapper is not in the way either: on the default arm upstream sets attn_mask = None at conditioning_attention_strength >= 1.0 with no latent mask (iclora_utils.py:159-160) and applies ConditioningItemAttentionStrengthWrapper only if attn_mask is not None (:168-169), so #932 is not the blocker for the default case.

Two causes remain, and the refusal now names both.

  1. The reference clip has no pixel path. Upstream reads it at height // scale by width // scale (iclora_utils.py:116-117), refuses a target the factor does not divide (:112-115), keeps frame 0 then every Nth frame (temporal_subsample, :87-89, called at :144) and encodes the whole clip (:145-148). This engine's only pixel-to-latent route encodes exactly one frame at the phase's own resolution and refuses an encode returning more, and nothing anywhere reads ref_video_dir.
  2. The reference item is a STAGE-1 item and stage 2 must run UNFUSED. ic_lora.py:108 gives stage 1 loras=tuple(loras) and the reference conditioning (:269-278, :377-402); :119 gives stage 2 loras=() and :314-321 gives it combined_image_conditionings with no reference item. This engine holds ONE DiT, fused at load, that every phase runs.

Serving the arm is filed as #975 rather than done here. Piece 2 changes how the engine holds its weights, not how it conditions; a second resident DiT is ~21 B parameters, which is a memory decision this row cannot take on its own.

The test could not have caught any of that, and now can

The case asserted five substrings of the message. Two were upstream symbol names, present in the pinned checkout whatever this engine can do; three were literals the message declared about itself. None could go red when the engine changed. The reviewer measured it rather than arguing it: replacing the local-cause sentence with a self-declared falsehood while keeping all five substrings left test_ltx2_video at 44 cases / 914 assertions / SUCCESS.

The case now measures the engine first. It renders with and without an appending conditioning item, reads video_tokens — the one trace field written inside the phase loop, so the only one that can observe what the loop does — and requires the grown count to exceed the plain one and both renders to return at the target frame count. Only then does it constrain the message, and the property is positional rather than lexical: every occurrence of a closed cause must sit after the WHAT IS *NOT* THE REASON marker, because recording a ruled-out cause is this message's own convention and has to stay possible.

R1 re-run on the repaired case, at f727cfd85: git diff --stat 1 file, +13/-29; BUILT yes, : error: count 0; exit 1, 49 cases → 1 failed, 1140 assertions → 1 failed (the suite was 49 cases before the #929 merge added one). It fails on REQUIRE(ruled_out != npos) — the restored message has no ruled-out section at all — and would fail again on the positional check.

The bf16 headline was only two thirds gated

The aggregation dtype binds three roundings and only two had cases: B * strength (fuse_loras.py:113) and deltas.add_(weight) (:67-68). The matmul result's own .to(dtype=dtype) had none.

Measured: widening only that one — keeping the f32 accumulator and adding the weight to it before the single store — left test_ltx2_lora at 13/13 and test_ltx2_loader at 31/31.

The new case puts acc = 1 + 2^-8 exactly on a bf16 tie and adds w = 2^-9, so the ported order stores 1.0 and an f32 accumulator stores 1.0078125 — one bf16 step apart in the STORED result, where the final rounding cannot absorb it. Mutation: git diff --stat 1 file, +4/-4; BUILT yes, : error: count 0; exit 1, test_ltx2_lora 14 cases → 1 failed, 2 assertions, reporting 16257 == 16256. test_ltx2_loader stayed 31/31 under the same mutation, and the other 13 lora cases stayed green, which is the measurement that the hole was real.

What lands

  • The adapter reader (ltx2_lora.h / .cpp): .lora_A.weight / .lora_B.weight pairs resolved onto the DiT contract through upstream's ComfyUI prefix strip (sd_ops.py:135-137), plus the file's whole __metadata__.
  • The fusion: sum((B * strength) @ A) added at load, mirroring fuse_loras.py:99-116.
  • Every dtype arm from one hook, placed immediately after MaterializeDitTensor because both quantized branches already return vt::DType::kBF16. On the streaming arm it runs before the device copy, so that arm's "one host buffer live at a time" invariant is unchanged.
  • The surface: lora_path / lora_strength load extras and ltx2-gen --lora PATH [STRENGTH]. No ABI change — both ride the existing parallel extras arrays. Load-time rather than per-request, because upstream takes the LoRAs as a DiffusionStage.from_checkpoint constructor argument (ic_lora.py:104-114).

Two deliberate divergences, both argued rather than silent

We do not re-quantize. Upstream's FP8 and NVFP4 rules dequantize, add, and re-quantize (fp8_scaled_mm.py:167-189, nvfp4/fuse.py:13-50) because they keep packed weights resident for their quantized kernels. This tree materializes bf16 on every arm and carries no FP8 or NVFP4 quantizer at all, so there is nothing to re-quantize into. Our fused weight skips upstream's lossy round trip and is slightly more precise on those two arms, at no extra bytes.

An adapter naming a module the contract lacks REFUSES. Upstream skips it (fuse_loras.py:135-137) because its state dict is the whole model. Here the contract is a fixed enumerated set with unported modules already stripped, so a skip would absorb a misnamed key and an inapplicable one alike.

kLoraFusion is retired, not reclassified

It carried DECLARED, NOT REQUESTABLE — an assertion that no request field or load extra asks for LoRA fusion. One now does. #691 predicted this exact drift in its own words and records that the ledger test gates the message text rather than the property. The compiler caught it here, which is weaker than what #691 asks for and does not close #691.

Reachability

Proven on the rendered pixels, not on last_conditioning(): the conditioning trace is filled before the denoise loop, so it cannot see a fused weight, and a first attempt comparing it found every arm identical for that reason rather than because the LoRA did nothing.

Production entry point: vllm_video_engine_loadLoadVideoEngineLtx2VideoEngine::LoadLtx2LoadDitFromSafetensors. The test enters there with a lora_path load extra and compares rendered artifact bytes against an identical request with no adapter.

Mutations

# mutation diff BUILT exit result
M1 accumulate the delta in f32 instead of bf16 1 file, +4/-4 yes, no compile_err 1 13 cases → 1 failed, 3 assertions
M2 reachability: delete dit_options.loras.push_back 1 file, +2/-1 yes, no compile_err 1 44 cases → 5 failed, 8 assertions
M3 disable the unknown-target refusal (upstream's skip) 1 file, +1/-1 yes, no compile_err 1 13 cases → 1 failed, 3 assertions
M4 ignore the adapter strength 1 file, +1/-1 yes, no compile_err 1 13 cases → 1 failed, 6 assertions
M5 remove the zero-fusion refusal 1 file, +1/-1 no-Werror=unused-parameter NOT_RUN establishes nothing; redone as M5b
M5b same, written to compile (fused >= 0) 1 file, +1/-1 yes, no compile_err 1 31 cases → 1 failed, 2 assertions
M6 report the A factor under the B key 1 file, +1/-1 yes, no compile_err 1 13 cases → 1 failed, 2 assertions
R1 restore the pre-repair reference refusal verbatim 1 file, +13/-29 yes, no compile_err 1 49 cases → 1 failed
F3 widen ONLY the matmul-result rounding to f32 1 file, +4/-4 yes, no compile_err 1 test_ltx2_lora 14 → 1 failed, 2 assertions; test_ltx2_loader 31/31 GREEN

R1 and F3 were run at f727cfd85, the commit before the #929 merge. Both carry to the pushed head: git diff f727cfd85 e367026cd is EMPTY over ltx2_lora.cpp and test_ltx2_lora.cpp, and over ltx2_video.cpp it changes only the refusal-counter comment and #929's own additions, leaving the reference Fail(...) string byte-identical. M1 to M6 were run before the review, at the counts the suites had then. Every mutation was restored byte-for-byte, proven by a clean git status. M5 is reported rather than dropped because a mutation that fails to build reads exactly like a passing test.

On porting upstream's tests

There are none to port. Measured at the pin with a positive control so a null result cannot be a wrong search term: find -name 'test_*.py' → 0, find -type d -name 'test*' → 0, conftest.py → 0, grep -rl 'import pytest\|import unittest' → 0, against find -name '*.py'280.

Records, and two merges resolved by key

origin/main moved three times during this repair — #966, #939 and #929 — so the branch carries two merges. Both keyed records were resolved by taking main's version and reapplying this row's scoped edit.

Gate

HEAD=e367026cd    dirty_files=0
CONFIGURE_EXIT=0
BUILD_EXIT=0      ": error:" count=0
"No space left"=0    "BFD.*assertion"=0
control_no_space=1   control_bfd=1   control_error=1
free disk 42G
ctest -N: Total Tests: 489        CTEST_N_EXIT=0
CTEST_EXIT=0
100% tests passed, 0 tests failed out of 489
loadavg 5.37 at start, 5.36 at end
scripts/agent-preflight.sh: PREFLIGHT_EXIT=0

The three grep counts carry positive controls, so a zero cannot be a broken instrument: the same patterns match their own sample strings and return 1.

scripts/check-doc-checkpoint.py walks per commit (#573) and was run that way, armed with a positive control: --commit b5618b305 exits 1 with the user_usage/docs/USAGE.md message, and both new commits plus the whole origin/main..HEAD range exit 0.

The gate on the pre-#929 tree was also green — 489/489, exit 0 — and is reported only to say that the #929 merge did not change the verdict.

Owed

GGUF k-quant LoRA fusion is not applicable rather than owed: the LTX-2.5 DiT ships FP8 and NVFP4, and no GGUF LTX DiT exists to fuse into.

Not measured, and not implied

No real-weights IC-LoRA fusion, and no render-quality or speed claim. No GPU was used. The fixture render moves 33 of 91169 artifact bytes, which is a reachability witness on a 2-layer reduced DiT and is not a quality result.

FOLLOWING_AGENTS_PROTOCOL

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

mudler added 6 commits August 15, 2026 15:43
…cond blocker the refusal did not name (#923)

The row was scoped as "port ICLoraPipeline so video-to-video works". Reading the
tree first says that is two pieces of work, not one, and only the first is what
the reference refusal blames.

`ltx2_video.cpp:1336-1346` refuses reference conditioning because the IC-LoRA's
`downscale_factor` and `temporal_scale_factor` live in the adapter's metadata and
nothing here reads it. True, and this row closes it. But thirty lines earlier the
last-frame refusal documents the other cause: every APPENDING conditioning item
is blocked on token-append machinery the phase loop does not have, and
`VideoConditionByReferenceLatent` appends. So closing the metadata half and
lifting the refusal would ship a wrong render. §6 sizes that second blocker and
§7 owes it; the DiT turns out not to be in the way, the fixed-width phase loop
is, and the same machinery is what blocks the last-frame keyframe arm.

Two findings worth the spec being written before the code. The tree already
dequantizes FP8 and NVFP4 to bf16 inside one function, so a single fusion hook
serves every arm and no quantizer is needed — the tree has none. And the
aggregation dtype is bfloat16 in all four of upstream's fuse rules, which is
precisely the too-wide-dtype trap a token gate cannot see, so it gets its own
mutation.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
…, and stop blaming the metadata (#923)

The reference-video refusal said this port could not read an IC-LoRA's
`downscale_factor` and `temporal_scale_factor`. That was true. It is now false,
and the refusal is rewritten rather than deleted, because a SECOND cause it
never named still holds the arm shut.

What lands is the adapter path: reading `.lora_A.weight` / `.lora_B.weight`
pairs, resolving them onto the DiT contract through upstream's ComfyUI prefix
strip, reading the file's `__metadata__`, and fusing `(B * strength) @ A` into
the weights at load. Reachable through the `lora_path` / `lora_strength` load
extras and `ltx2-gen --lora PATH [STRENGTH]`, mirroring upstream's own
constructor-argument shape: `DiffusionStage.from_checkpoint` takes the LoRAs, so
a per-generation field would promise something fusion cannot do.

One hook serves the F32, BF16, FP8 and NVFP4 arms, because both quantized
branches of `MaterializeDitTensor` already return bf16. That is a deliberate
divergence: upstream re-quantizes after adding the delta to keep its packed
weights resident, this tree keeps bf16 and carries no FP8 or NVFP4 quantizer to
round-trip with. Our fused weight therefore skips upstream's lossy round trip at
no extra bytes, and both quantized arms are gated against the loader's own
unfused output rather than against an independent dequantization.

The accumulator is bfloat16, which is what all four upstream fuse rules declare.
An f32 accumulator would be MORE precise and no golden could ever fail on it, so
it has its own case, built so the two orders separate by a whole bf16 step
instead of by a rounding that a bf16 store puts straight back. The first version
of that case compared rank-1 values and passed under both arms; the comment on
it now says so.

An adapter naming a module the contract does not bind REFUSES here where
upstream skips it. Upstream can afford a skip because its state dict is the
whole model; this contract is a fixed enumerated set, so a skip would absorb a
misnamed key and an inapplicable one alike.

`kLoraFusion` is retired, not reclassified. It carried DECLARED, NOT REQUESTABLE
- an assertion that no load extra asks for LoRA fusion - and one now does. #691
predicted this drift in its own words and records that the ledger test gates the
message text rather than the property; the compiler caught it here, which is
weaker than what #691 asks for and does not close it.

Reachability is proven on the rendered pixels, not on `last_conditioning()`: the
conditioning trace is filled before the denoise loop, so it cannot see a fused
weight, and a first attempt comparing it found every arm identical for that
reason rather than because the LoRA did nothing.

Two defects found in this row's own work and fixed here rather than filed. The
reader kept one key per target, so a malformed A factor was reported under the B
factor's name - "refuse BY NAME" defeated by the message itself - and the case
that would have caught it checked only the dtype. Both are now per-side and
mutation-proven.

`docs/FEATURES.md` and `docs/USAGE.md` ride with the change that made them
stale, which is also what `check-public-doc-tables` and the doc checkpoint
require. FEATURES had disagreed with itself: `:165` said LoRA was `refused`
while `:337` correctly said `Declared, not requestable`. Both keys move
together. Each edit was applied by key with an asserted single match and the
unrelated keys then proved byte-identical - FEATURES 366 of 368 lines untouched,
USAGE 3117.

Owed and named in the pull request body: #930 for the token-append grow-and-trim
that the reference and last-frame arms both wait on, and #932 for the sub-1.0
attention-strength arm and N-adapter fusion.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
`origin/main` moved to 5a0ffe9 (ENG-EXPERT-STREAM W3) while this row was in
review, and `check-commit-style.py` refused the range because its base was no
longer an ancestor of its head. Merging rather than rebasing keeps the spec
commit's position ahead of the implementation, which is what proves spec-before-code.

The incoming commit touches `CMakeLists.txt` and `tests/CMakeLists.txt`, both of
which this row also edits, so the clean auto-merge is not on its own evidence:
the merged tree is rebuilt from an empty build directory and the full gate is
re-run on THIS sha rather than on the pre-merge one.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
…m this seam (#923)

Both were read rather than ported, so the adapter path would not have to be
rewritten when their rows arrive. The finding is that neither would touch it.

Both fuse exactly one adapter at load - `dubit.py:364-365` refuses any other
count and `hdr_ic_lora.py:271-272` hard-codes one - so the N-adapter work this
row owes is not on their path. Everything else they want lands on the pipeline
and the conditioning state: per-tile conditioning slices, a frozen audio
modality, a text context loaded from a file instead of encoded.

One place the seam WAS shaped for them: `metadata()` returns the whole
`__metadata__` map rather than the two factors `ic_lora.py` happens to read,
because `hdr_ic_lora` reads `hdr_transform` and `use_hdr_transform` instead and
does not use the `iclora_utils` readers at all.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
…NTRY budgets (#923)

The pre-push hook refused: line 165 was 654 chars against a 600-char ENTRY
budget and line 337 was 281 against 220. Both are rows this row itself widened.

Shortened MY rows rather than deleting anyone else's to make room, which is the
distinction AGENTS.md draws - the budget is per ENTRY and the page has none, so
a shared-file budget's "remove another entry to add one" failure mode does not
apply here and must not be imitated. The forensics the rows no longer carry are
in `.agents/specs/ltx25-ic-lora.md`, which is where they belong.

Re-proved after the trim: 366 of 368 FEATURES lines byte-identical, only 165 and
337 moved.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
Three commits landed on main during this row's gate: VT-FP8-W8A8-CPU-ARM (#842)
and the two MODEL-NEMOTRON-H changes (#872, #903). None touches this row's code.

`docs/FEATURES.md` and `docs/USAGE.md` AUTO-MERGED, and an automatic three-way
merge of a keyed record is exactly what the rule forbids accepting on trust. So
both were verified by hand against the target-branch version rather than by the
merge reporting success: FEATURES 366 of 368 lines byte-identical with only 165
and 337 moving, USAGE 3117 lines byte-identical, and zero unrelated keys touched
in either. Main had not edited either file, so the merge was trivial - but that
is a conclusion from checking, not an assumption that let the check be skipped.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
@localai-bot

Copy link
Copy Markdown
Collaborator Author

Operator ruling on the NEEDS_DECISION: #930 dispatches as its own row, not as a tail of this one. You were right to stop rather than lift the refusal.

Three reasons, and the third is the decisive one.

It is a shared seam, and the protocol says so. Token-append blocks at least three arms today, each refused for the same reason by a different row: the last-frame keyframe arm, reference video (VideoConditionByReferenceLatent, which appends — reference_video_cond.py:97-100), and generated keyframe slots (#920, filed independently by a sibling row that hit the identical wall). AGENTS.md: extend a shared seam, never write a parallel path by hand. Three rows queuing behind one fixed-width phase loop is exactly the shape that wants one row.

Two agents found it independently, which is evidence it is real. #920 and #930 were filed hours apart by rows that never spoke, both naming the phase loop and Ltx2LatentState. That is a structural gap, not a local awkwardness.

Lifting the refusal here would have shipped a wrong render, and you caught that the refusal named only the first of two causes. Closing the adapter blocker and then rewriting the refusal onto the cause that actually remains is the correct outcome for this row — the refusal is now true, which is worth more than a feature that is quietly false.

Two things from your report I want recorded rather than lost.

M5 not building, reported as NOT_RUN establishing nothing, is exactly right. A mutation that fails to compile reads as a passing test, and the honest move is the one you made — re-run it compiling as M5b rather than bank the green.

Proving reachability on rendered pixels rather than last_conditioning() is the sharper method, and your reason is the useful part: the trace is filled before denoise, so it cannot observe a fused weight, and your first attempt found every arm identical for that reason. That belongs in the spec, because the next person will reach for the trace first.

Also noted: another agent overwrote your PR-body file in the shared scratchpad. That is the third occurrence today; the scratchpad is contended and nothing generically named is safe there. And thank you for flagging the pkill -9 ninja — the box is shared, and killing by PID after checking /proc/<pid>/cwd is the right correction.

FOLLOWING_AGENTS_PROTOCOL

localai-bot added a commit that referenced this pull request Aug 16, 2026
…rim it back (#930) (#964)

The LTX-2.5 phase loop was fixed at one `Ltx2VideoTokenCount(vshape, 1)`, and that single limitation blocked three conditioning arms. Two rows walked into it hours apart without talking to each other — [#930](#930) from the IC-LoRA port and [#920](#920) from the generated-keyframe-slots port — which is why it gets a seam of its own rather than a fix inside whichever row noticed it last.

Issue: [#930](#930). Spec: [`.agents/specs/ltx25-token-append.md`](.agents/specs/ltx25-token-append.md). Campaign [#644](#644). Pin `Lightricks/LTX-2 @ fd4ded7f2d88d3da713abcdd4ad41ecc4a9314ca`, verified at the working checkout.

## Why this branch replaces #948

**This is a history repair, not a content one.** [#948](#948) carried this row through implementation and a fresh review to PASS at head `a1b1d8da4`. It cannot land, and the reason is entirely in its commit ORDER.

`scripts/check-doc-checkpoint.py` walks a range **per commit** ([#573](#573)). The review-repair commit `b5618b305` edited `include/vllm/model_executor/models/ltx2_conditioning.h` and `include/vllm/multimodal/ltx2_video.h` — the `user_usage` class — while the `docs/USAGE.md` edit that pays for them sat in a sibling commit. The final tree satisfied the obligation; only the ordering failed. Measured on this branch, the checker still REDs on `b5618b305` in isolation and is green on both commits here, so the instrument is armed rather than merely quiet.

A commit message cannot be corrected in place and this repository uses no force variant, so the branch is rebuilt with the shape the checker requires: **one spec commit, then one implementation commit** carrying `docs/USAGE.md` and `docs/FEATURES.md` alongside the code they describe. #678 was rebuilt as #880 and #661 as #882 for exactly this rule.

**The reviewed content is proven unchanged.** Built on `2daa3287f` — `origin/main` when the rebuild started — this branch's head tree hash was `d937252dd06efd6a07c4affebff42153b25075a5`, byte-for-byte equal to `a1b1d8da4^{tree}`, and `git diff a1b1d8d HEAD` was empty. `origin/main` then advanced to `f365cc299` (#958), so the branch was rebased onto it; restricted to this row's ten files, the only remaining differences against `a1b1d8da4` are the two lines #958 itself changed — its own appended issue-index row and its `--offload-config` rewrite in `docs/USAGE.md`. The eight code, test, spec and `docs/FEATURES.md` files are byte-identical. Nothing below was re-derived.

## The sizing in #930 is corrected, not inherited

#930 names two blocked arms. There are three: reference video, the last-frame keyframe, and generated keyframe slots. #930 predates #920 and its author asked for the correction. This spec is the record that supersedes both counts.

## What was actually missing

The appending conditioning ITEMS were already ported and gated. Two halves were not.

`Ltx2ExtendKeyframesMask` mirrors `extend_keyframes_mask` (`conditioning/mask_utils.py:76-105`). Upstream's docstring makes the call an obligation of appending itself — *"Every conditioning item that appends tokens must call this, otherwise the per-token marker goes out of sync with the token sequence"* (`:83-85`) — so it lives inside `AppendTokens` rather than at the three call sites. Both `None` branches are mirrored, including the zero-fill only `marked=true` reaches (`:100-101`), so #920's arm finds it already correct.

`Ltx2ClearConditioning` mirrors `clear_conditioning` (`tools.py:88-117`). Two things there are not a truncation: the denoise mask comes back **all ones** rather than sliced (`:104`), because the returned state describes a finished latent, and `keyframes_mask` is dropped entirely (`:113`).

`Ltx2LatentState` grows a `keyframes_mask` field so the marker can be extended with the sequence it describes.

## Three findings that changed the design

**The attention mask is not the gap, and no field is added for it.** Both appending video items pass a literal `attention_mask=None` (`keyframe_cond.py:70`, `reference_video_cond.py:88`), `update_attention_mask` returns `None` for that case (`mask_utils.py:110-143`), and the only route to a non-`None` mask is `ConditioningItemAttentionStrengthWrapper`, whose sole application site is `iclora_utils.py:169` on the IC-LoRA path. `combined_image_conditionings` — the route this engine mirrors — never wraps. A field here would be one no ported item could populate, which is the unpassed-parameter shape [`.agents/reachability.md`](.agents/reachability.md) enumerates. The refusals that cite the absent field were right to mention it and wrong to offer it as the blocker.

**The sigma schedule must keep reading the target count.** Upstream fixes it twice over: the argument is `math.prod(latent.shape[2:])` of the *unpatchified* target (`schedulers.py:32`), which by construction cannot contain appended tokens, and every pipeline computes its sigmas before a state exists (`ti2vid_one_stage.py:207` passes no latent; `distilled.py:200-201` uses frozen constants). This engine's call sits **after** the conditioning block, so before this change it would have re-shifted the whole trajectory the moment anything appended. It now reads a `target_tokens` local.

**The trim cannot be gated on pixels alone, so it is gated on a guard.** Appended tokens sit at the tail of a contiguous `[tokens, width]` buffer and `Ltx2VideoUnpatchify` takes a bare pointer, so an un-trimmed state would unpatchify the same head bytes and render pixel-identical frames. Correct output for the wrong reason. A `VT_CHECK` at the pointer boundary is what turns "the head happens to be right" into "the buffer *is* the target grid", and it is what makes deleting the trim a RED (mutation M3).

## The arm lifted, and the two left alone

The **last-frame keyframe** arm is served, mirroring `combined_image_conditionings` (`helpers.py:272-308`): one preprocess-and-encode shared by both arms, and a branch on `frame_idx` sending 0 to `VideoConditionByLatentIndex` (`:296`) and anything else to `VideoConditionByKeyframeIndex` (`:302`). Both may be supplied at once. Nothing is added to `include/vllm.h` — `last_frame` was already on the ABI.

**Reference video stays refused and its message is byte-identical.** At this base, PR #938 (`row/LTX25-IC-LORA`) is open and unmerged and `git log --grep '#923'` is empty, so `--lora` does not read the IC-LoRA scale factors and that refusal's stated cause is still true. #930's body describes the message as already rewritten onto token-append; **that is not the state of `origin/main`.** Rewording it would have shipped a refusal naming the first of two causes — the defect a sibling row nearly shipped.

**Generated keyframe slots** (#920, PR #929, also open) and **reference audio** stay refused for their own reasons.

## What lands unreached, named rather than discovered

`Ltx2ExtendKeyframesMask`'s **`marked=true` branch lands with a unit driver and no production caller.** The only upstream construct that passes `true` is `VideoGeneratedKeyframeSlots` (`keyframe_slots.py:121`), which belongs to the generated-keyframe-slots arm and not to this row. Every appending item ported here passes `false`.

It is landed rather than stubbed because building half of `extend_keyframes_mask` is the worse option: the two `None` branches are not symmetric, and a port that mirrors one and guesses the other gets #920's arm silently wrong. Mutation M5 shows the branch is not inert — appending with `marked=true` instead of `false` is RED.

Owner of the wiring: row `LTX25-GENERATED-KEYFRAMES`, issue [#920](#920). Listed under `## Owed` in the spec. Declared here per [`.agents/reachability.md`](.agents/reachability.md) `## Landing a slice that is not reached yet`.

## Evidence

RED first, on the pre-row engine with the tests in place: `test_ltx2_video` FAILED, the new case throwing the last-frame refusal verbatim. That run also printed `assertions: 825 | 825 passed | 0 failed` beside `Status: FAILURE!` — the thrown-case trap, which is why the exit code is the authority throughout.

The witness is on **rendered artifact bytes with a no-op control**, because `Ltx2ConditioningTrace` is filled before denoise and cannot observe the loop. A keyframe render differs from a no-keyframe render in **59773 of 91169** bytes; two different keyframes differ from each other in **334**; the same request twice is byte-identical. The control is what separates "the append reached the maths" from "the instrument is blind" — a sibling row's first attempt found every arm identical *including* its control, and without that control a blind instrument reads as a real-but-subtle effect, which is the direction that ships.

Two new trace fields, `video_tokens` and `schedule_tokens`, are written *inside* the phase loop for that same reason, and the sigma claim is checked as a relation between two measured values rather than against a literal.

Upstream ships **no tests** — `find /home/mudler/_git/LTX-2 -name 'test_*.py'` returns 0 across the whole repository at the pin — so nothing was ported and every case is written against an upstream anchor instead.

## Review repairs

A fresh review returned FAIL on six findings. The seam itself survived: every upstream design decision checked out and the pixel witness reproduced exactly. Two claimed guarantees survived mutation and are now gated.

**The all-ones restore was not gated.** `Ltx2CreateVideoLatentState` already sets every target-range mask value to `1.0`, and the append writes `1 - strength` only at the tail, which the trim drops under either implementation — so "restore all ones" and "slice" produced identical bytes over the range the test walked, and mutation M6 (slice instead of restore) was **GREEN**. The case now conditions token 0 *inside* the target with `Ltx2ConditionVideoByLatentIndex` before the append, which writes `1 - strength` at `start .. start + count` (`latent_cond.py:41`). A slice then leaves `0.4` at token 0. M6 is **RED**, and a `REQUIRE` on `mask.front()` before the trim asserts the instrument is armed rather than assuming it.

**Nothing gated that the keyframe lands on the last frame.** Mutation M10 (`frame_idx` from `frames - 1` to `0`) was **GREEN**: both renders still differed from the no-op control and from each other, and the token count was identical, because a keyframe pinned to the first frame appends exactly as many tokens as one pinned to the last. The engine now asserts the first appended token's temporal position, recomputed from `frames` and `fps` rather than read back from the argument, so the two are independent expressions (`keyframe_cond.py:52-58`). M10 is **RED**.

`origin/main` advanced to `c2019b0e3` (#935) during review and the branch stopped merging. Three conflicts, two with a trap: `docs/FEATURES.md`'s LTX-2.5 cell is at `MAX_CELL_CHARS` exactly, so the obvious merge lands at 226 and REDs — resolved by keeping main's complete cell and trimming only this row's own wording, to 220. And both sides had rewritten the `READER ANCHORS` line, so **neither** value was correct afterwards; it was re-derived a third time from the merged file.

Every upstream `file:line` in this change was re-derived at the final tree by a needle taken from the **claim**, never read out of the cited span, and the load-bearing needles were checked for uniqueness in their files. The sweep covers **119** citations in this row's added lines — 108 explicit and 11 bare `:NN` continuations — and corrected **13**.

Four of those were the review's: `tools.py:103` and `:112` in the `Ltx2ClearConditioning` declaration and in the spec, and `schedulers.py:38-39` in four places including the append-only issue-index row, which is fixed first and SHA-anchored because it could not be corrected after landing.

Three were not, and two of those three are anchors written *during this repair* and caught by the sweep over its own output:

- `ltx2_recipes.py:125-158` names a file that exists neither upstream nor in this tree. The distilled recipe's frozen sigmas are `distilled.py:200-201` over `utils/constants.py:17-23`.
- A bare `(:100-101)` resolves against `tools.py`, the nearest file named above it, whose `:100-101` is a real and unrelated statement. It means `mask_utils.py`. The two bare continuations in `Ltx2ClearConditioning`'s declaration are spelled with their file for the same reason: the nearest name above them is `blocks.py`, and `blocks.py:104` and `:113` are both real imports.
- `latent_cond.py:36-38`, written here for the M6 repair, is `stop_token` and a `clone()`. The `1 - strength` write is `:41`.

**One anchor is left alone deliberately.** `latent_cond.py:38-39` is cited at eight pre-existing sites across specs, golden scripts and other rows' tests for "only `clean_latent` and `denoise_mask` are written". Those writes are `:40-41`; `:38-39` is a `clone()` and a blank line. The drift predates this row and the sites belong to other rows, so correcting them here would be scope this PR did not claim. It is named rather than left for the next sweep to rediscover.

## Gate on the rebuilt branch

Rerun in full rather than inherited, on the rebased head `fd9948f14`, CPU-only Release, `-j6` build and `ctest -j4`:

`CONFIGURE_EXIT=0`, `BUILD_EXIT=0`, `: error:` count **0**, `No space left` **0**, `BFD assertion` **0** (all three greps positive-controlled against a synthetic line), `ctest -N` **485**, `CTEST_EXIT=0`, `100% tests passed, 0 tests failed out of 485`. Load average 4.94 at start and 11.02 at end; 46 G free.

Row suites, run directly for their assertion counts: `test_ltx2_video` 44/44 cases, 1024/1024 assertions; `test_ltx2_vae` 39/39, 3092/3092; `test_ltx2` 43/43, 4388/4388; `test_ltx2_device` 18/18, 546/546.

M6 and M10 were rerun on this branch rather than carried over. **M6** — `Ltx2ClearConditioning` slices instead of restoring — `git diff --stat` one file `1 +/1 -`, BUILT with 0 compile errors, exit **1**: `38 passed | 1 failed`, four `CHECK( 0.4 == 1 )` at mask tokens 0-3. **M10** — `frame_idx` `frames - 1` to `0` — one file `1 +/1 -`, BUILT with 0 compile errors, exit **1**: `42 passed | 2 failed`, both cases THREW the temporal-position `VT_CHECK` (`0.333333` wanted, `0.000000` got). M10 prints `assertions: 923 | 923 passed | 0 failed` beside `Status: FAILURE!`, which is the thrown-case trap; the exit code is the authority. The tree was restored byte-for-byte after each — `git write-tree` back to `d937252dd0` — and rebuilt before remeasuring.

`READER ANCHORS` were re-derived at the final tree and are unchanged: `756 811 907 923 925 1003 1028 1133 1174`. The `docs/FEATURES.md` LTX-2.5 support cell measures **220** characters, exactly `MAX_CELL_CHARS`, with main's `, A2V WAV` and every host and measurement qualifier intact.

Checkers, exit codes captured directly: `check-doc-checkpoint` **0** over the range and **0** on each commit individually, `check-commit-trailers` **0**, `check-commit-style` **0**, `check-issue-index-append-only` **0**, `check-agent-record` **0**, `check-public-doc-tables` **0**.

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 16, 2026
…the first, and it came from main (#968, #672)

With #965's `C4456 'loaded' shadow` removed, `windows-msvc-cpu` and
`windows-msvc-vulkan` failed again on this row's pull request — and on a
different cause:

  include\vector(1461,29): error C2220: the following warning is treated as an error
  include\vector(1461,29): warning C4244: '=': conversion from 'const double'
                           to 'float', possible loss of data

raised from `src/vllm/multimodal/ltx2_video.cpp:203,214`, the two narrowing
`positions.assign` calls that `c7cb59fbb` (#964, LTX25-TOKEN-APPEND) landed on
`main` while this row was in flight. `StreamState::positions` and
`Ltx2LatentState::positions` differ in element type; GCC and Clang narrow
silently, MSVC diagnoses and the build treats it as an error.

NOT FIXED HERE, deliberately. #964's own comment at `ltx2_video.cpp:129-132`
reasons that "double -> float -> double reproduces the bits", so the narrowing
is intentional and a silencing `static_cast` would be a claim about that
reasoning rather than a formatting repair. It belongs to the lane that owns the
round trip. Filed as #968 with the evidence rather than papered over.

THE MATCHED ARM SPLITS EXACTLY ON THE MERGE BASE, which is what makes it
inherited rather than mine. Grepping each `windows-msvc-cpu` job log for the
warning: #966 and #951, both on `c7cb59fbb`, hit it twice each; #967, #956,
#950, #939 and #938, all based before it, do not hit it at all. This row's diff
touches zero LTX2 files.

THE FINDING WORTH CARRYING is not either warning. It is that TWO INDEPENDENT
CAUSES WERE STACKED BEHIND ONE HABITUALLY-RED JOB NAME, and the first hid the
second — and that neither was #645, the `M_PI` regression both jobs are usually
attributed to. A known-red list tells you a job is often red. It never tells you
that today's red is the same one. Only reading the log does.

Issue: #968

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
localai-bot added a commit that referenced this pull request Aug 16, 2026
… heard, and the five keys upstream refuses that we dropped (#672, #953, #965) (#966)

feat(MODEL-MUSIC-MUSIC3): a music-only server, an example that can be
heard, and the five keys upstream refuses that we dropped (#672, #953)

Developer directive: parity on what upstream supports — "we want to be a
good
reference" — usage docs for MiniMax-Music3, and in those docs the
weights. Then,
mid-flight: "we should allow to load only the music model" and "we need
to have
an e2e test working".

FOLLOWING_AGENTS_PROTOCOL

## The upstream surface, enumerated rather than summarized

SGLang-Omni `748a0b43` `models/minimax_music3/` and diffusers `c6da9936`
`modular_pipelines/minimax_music3/`, read field by field and recorded
with
`file:line` in spec §10.1 so the next reader re-derives nothing.

**Closed here:** the music-only server, the missing example, and five
refusals.
**Owed and named:** the non-`wav` response formats, request batching and
`/v1/audio/speech/batch`, the 32 kHz delivery resample.
**Permanently refused rather than owed:** streaming — neither upstream
arm has
it (`supports_streaming_vocoder=False`).
**One place we are ahead of both arms:** `guidance_scale` is a real
per-request
control here, where diffusers freezes it at 1.7 into the guider
component
(`denoise.py:180`) and SGLang exposes it only as a serve-time knob.

## `--model` is optional when `--speech-model` is given

Serving a 28.5 GB music model also forced loading an unrelated text
model, and
on this box the smallest text checkpoint is 35B — so the recipe this
project
documented was effectively unrunnable. Upstream's own is `sgl-omni serve
--model
MiniMaxAI/MiniMax-Music3`, no text tower anywhere.

    vllm-server --speech-model /path/to/minimax-music3

Third instance of a shape already in `server_main.cpp`: a pooling
checkpoint
serves `/v1/embeddings` alone, a Parakeet checkpoint serves
`/v1/audio/transcriptions` alone. It mirrors vLLM's task-conditional
registration (`api_server.py:255-265`).

**Additive, and proved rather than argued.** The only case whose verdict
changes
is BOTH flags absent, which was an error and remains one with a message
naming
both options. The route table is gated in both directions over a real
socket,
because a handler-dispatch test cannot see route registration at all.

## The example the music family did not have

`examples/minimax_music3_gen` — a thin client of `include/vllm.h` and
nothing
else, like `parakeet-transcribe` and `vllm-cli`. Hearing this model
previously
needed a running server plus a `curl`, or a C ABI caller nobody had
written.

## Five keys upstream refuses by name were SILENT here (#953)

`temperature`, `top_p`, `top_k`, `repetition_penalty` — refused upstream
at
`request_builders.py:14-19,109-114`, because this model's autoregressive
stage
has ONE sampler, a fixed top-50 draw (`encoders.py:48,94-103`). And
`max_new_tokens`, upstream's LENGTH spelling in 25 Hz frames rather than
seconds
(`request_builders.py:56-68`), so a 250-frame request silently became
the
family's 60 s default. That is the #925 class exactly, in the same file
that
already carries #925's refusal one paragraph above. Fixed in flow.

## The e2e gate no longer reports a skip wearing a pass

It read 5 cases / 5 passed with **`assertions: 0`** whenever the
checkpoint was
absent — the same shape that fooled this project on
`test_qwen3_paged_engine`.
Split into a checkpoint-free half that runs unconditionally in CI
(request
contract, both ceilings, the speech-only route table over a real socket
with a
stub synthesizer) and the env-gated half, whose HTTP case now drives the
real
engine over a real socket against the music-only server shape. A
coverage-report
case prints which arms ran, every run.

The full arm was run: `POST /v1/audio/speech -> 200 audio/wav, 12332
bytes in
518 s wall`, 2 AR frames -> 6 latent frames -> 3072 samples/channel,
6144 int16
samples all non-zero, 0 clipped, 2818 of 3072 positions differing
between L and
R, and `/v1/completions` + `/v1/chat/completions` both 404 from the
route table.
`checkpoint_arms_run=5`.

| arm | cases | assertions |
|---|---|---|
| `test_minimax_music3_e2e_real`, no env vars | 9 | 37 (was 5 / **0**) |
| `test_minimax_music3_e2e_real`, checkpoint only | 9 | 86 |
| `test_minimax_music3_e2e_real`, checkpoint + `VLLM_CPP_MUSIC3_DIT=1` |
9 | **582** |
| `test_speech_api` | 6 | 67 |
| `test_openai_api_server` | 62 | 733 |
| `test_openai_conformance` | 23 | 252 |
| `test_minimax_h3` (unchanged) | 79 | 57395 |
| server flag ctest cases | 7 passed | |

## The weights are documented (porting-a-model.md §2.1)

`docs/USAGE.md` gains component-by-component tables: the diffusers arm
at
`MiniMaxAI/MiniMax-Music3`@`fbdf52fbaaca799592917417eb05f1899f1255ec`,
**28.5 GB
resident** (28 517 617 303 B, measured) out of a 57.4 GB repository and
why they
differ; the native `.pth` arm we refuse and SGLang-Omni serves; the one
implemented GGUF Q4_K artifact with its sha256; and the fourteen
third-party
quantized repositories in five formats, each marked refused. The
revision is
verified rather than copied —
`condition_encoder/diffusion_pytorch_model.safetensors`
on disk hashes to that revision's own LFS record.

## A sample a human can hear

2.0 s of 44100 Hz stereo from this engine in 3286 s of wall clock: RMS
0.03169,
peak 0.97437 with 0 clipped samples, 84 073 of 88 064 positions
differing
between left and right. **Its samples are compared to nothing** — §5
withdrew
the token gate — so it shows the pipeline runs, not that the music is
right. It
is not committed: `check-pr-size.py` classifies every path and none
takes a
`.wav` outside `tests/`, where a file compared to nothing would sit
beside the
goldens and imply it was one.

## The four asks, answered directly

**1. Music-only server.** `vllm-server --speech-model <dir>` with NO
`--model`
starts and serves, observed live rather than inferred:

    server: speech/music-only model (family=minimax-music3, 44100 Hz,
text-only synthesis, family DETECTED); serving /v1/audio/speech
    server: listening on http://0.0.0.0:18923 (model 'minimax-music3')

`--model` alone and `--model` + `--speech-model` are **byte-identical in
behaviour**. The whole change is one new early branch, `if
(args.model_dir.empty())`,
which loads the speech engine and `return 0`s before reaching a single
line of
the existing path; nothing downstream of it was touched. The only case
whose
verdict changes is BOTH flags absent, which was an error and remains
one.
Server suites: **7 of 7** ctest cases (4 pre-existing + 3 new — neither
flag is
still an error and now names both options; `--speech-model` alone
reaches the
speech LOAD; `--speech-family` alone still demands a checkpoint),
`test_openai_api_server` **62 cases / 733 assertions** (+1 case / +24
assertions,
the speech-only route table over a real socket),
`test_openai_conformance`
**23 / 252** unchanged.

**2. e2e, three arms.** What a bare CI run executes unconditionally: the
request
contract on the exact body the real case posts, the near-miss and
sampling
refusals, the duration arithmetic including both ceilings, and the
speech-only
route table over a real socket with a stub synthesizer. What stays
env-gated:
everything needing the 28.5 GB checkpoint, plus the two 2.4B-DiT arms
behind
`VLLM_CPP_MUSIC3_DIT`.

| arm | cases | assertions | checkpoint arms run |
|---|---|---|---|
| no env vars (what CI runs) | 9 | **37** | 0 — was 5 cases / **0
assertions** |
| `VLLM_CPP_MUSIC3_CHECKPOINT` | 9 | **86** | 3 |
| + `VLLM_CPP_MUSIC3_DIT=1` | 9 | **582** | 5 |

**3. The five keys.** All five were **accepted and silently dropped**;
all five
are **now refused by name**.

| key | upstream anchor | why it cannot be honoured |
|---|---|---|
| `temperature` | `request_builders.py:14-19,109-114` | the AR stage's
only sampler is a fixed top-50 draw, `encoders.py:48,94-103` |
| `top_p` | same | no nucleus branch exists |
| `top_k` | same | `_AR_SAMPLING_TOP_K` is a module constant of 50 |
| `repetition_penalty` | same | no penalty is applied anywhere in the
loop |
| `max_new_tokens` | `request_builders.py:56-68`, `constants.py:4-5` |
upstream's LENGTH, in 25 Hz frames not seconds; the refusal names
`audio_duration` and the /25 conversion |

**4. The weights table** (`docs/USAGE.md`, "MiniMax-Music3: the exact
weights").
It carries: repo **and** revision —
`MiniMaxAI/MiniMax-Music3`@`fbdf52fbaaca799592917417eb05f1899f1255ec`,
verified
rather than copied, since
`condition_encoder/diffusion_pytorch_model.safetensors`
on disk hashes to `83179c5e…a202c2a4d`, that revision's own LFS record;
the
Q4_K artifact's sha256 `4c5d41b2…c70cbdd0` at revision `c36aaeed…` with
its exact
byte count; **28.5 GB resident (28 517 617 303 B, measured) versus 57.4
GB
repository**, with the reason they differ; the refused native `.pth` arm
(`qwen_7B/`, `flowmatching_vae.pth`, `dav.pth`) and that SGLang-Omni
serves it;
and all fourteen community quant repositories across five formats, each
marked
refused and each marked **third-party** rather than first-party. This is
the
first application of `.agents/porting-a-model.md` §2.1 (landing as
#951).

## Two reds stacked behind one habitually-red job name

`windows-msvc-cpu`/`windows-msvc-vulkan` are usually attributed to #645
(`M_PI`
in three LTX2 sources). **Neither of the two causes here was #645**, and
the
first hid the second.

**#965, fixed in flow.** `C4456: declaration of 'loaded' hides previous
local
declaration` at `server_main.cpp:1315` — W6's own speech-attach block
declaring
`loaded` inside the text engine's `loaded`. The only warning in the job,
on
`main` since W6 landed. Matched arm: #956, #950 and #939, none touching
the
speech surface, fail identically. Renamed; nothing suppressed.

**#968, filed and NOT fixed here.** With the shadow gone the same jobs
failed
again on `C4244: conversion from 'const double' to 'float'`, raised
inside
MSVC's `<vector>` from `ltx2_video.cpp:203,214` — two narrowing
`positions.assign` calls that `c7cb59fbb` (#964) landed on `main` while
this row
was in flight. **This branch touches zero LTX2 files.** The matched arm
splits
exactly on the merge base: #966 and #951 (on `c7cb59fbb`) hit it,
#967/#956/
#950/#939/#938 (before it) do not. It is deliberately left to the
LTX-2.5 lane —
#964's own comment reasons that "double -> float -> double reproduces
the bits",
so a silencing cast is a claim about that reasoning rather than a
formatting fix.

**The finding, which outlives both:** a known-red list tells you a job
is often
red. It never tells you that today's red is the same one. Only reading
the log
does — and here it took two readings, because removing the first cause
is what
made the second visible.

<!-- kept for the record -->
### The first of the two, in detail (#965)

`windows-msvc-cpu`/`windows-msvc-vulkan` failed here, and they are
**not** #645
(`M_PI` in three LTX2 sources). They were W6's own
`C4456: declaration of 'loaded' hides previous local declaration` at
`server_main.cpp:1315` — the only warning in the job, on `main` since W6
landed.
The matched-arm check is what separated it from my diff: #956, #950 and
#939, all
touching no speech surface, fail identically. `main` has no baseline
because
`windows-msvc-*` are PR-only (#584), so it presents to each author in
turn as
their own red. Filed and fixed in flow by renaming the inner
declaration; nothing
suppressed.

## Mutations

Four run, four fire: sampling refusal neutered (5 assertions red),
`max_new_tokens` refusal neutered (2 red), `--model` made mandatory
again
(2 ctest cases red), generate routes registered unconditionally (3 cases
/
6 assertions red in the api-server suite, 1 / 2 in the e2e suite).
Sources
restored and verified sha256-identical.

Supersedes #954 (untrailered merge commits) and #963 (a
`server_main.cpp` commit
that owed `docs/USAGE.md` under the per-commit documentation
checkpoint). Same
tree, linear history, every commit green on `check-doc-checkpoint`,
`check-commit-trailers` and `check-commit-style` locally before pushing.
Every
source file is byte-identical to the one built and gated.

Issue: #672

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

---------

Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
mudler added 3 commits August 16, 2026 02:38
Four files conflicted and each was resolved by taking main's version and
reapplying this row's scoped edit, rather than by accepting a three-way merge of
a keyed record.

`docs/FEATURES.md` is keyed: 186 of the 188 keys are byte-identical to
`origin/main` and the two that differ are the two this row owns. The LTX-2.5 DiT
cell had to absorb main's `IMAGE+LAST kf SERVED` and `A2V WAV` alongside this
row's served LoRA, and main's cell was already at the 220-character `MAX_CELL_CHARS`
limit, so this row's own wording was trimmed to fit rather than any measurement
or host qualifier.

`docs/USAGE.md` takes main's served-last-frame-keyframe paragraph whole, and its
reference paragraph is rewritten rather than picked from either side: both
reasons the two sides gave for that refusal - main's IC-LoRA metadata and this
branch's token-append - are now false, and the page records that rather than
deleting it.

`src/vllm/multimodal/ltx2_video.cpp` keeps BOTH parsers: main's `ExtraDouble` for
seconds-valued extras and this row's `ParseLoraStrength`. Git overlapped them
because they share a `std::stod` body; they are different functions with
different messages and different defaulting, and folding them would have given a
strength the wrong noun in its refusal.

`tests/vllm/multimodal/test_ltx2_video.cpp` keeps both appended blocks, this
row's IC-LoRA reachability cases and main's audio-to-video cases.

READER ANCHORS were rewritten on both sides, so neither list survives the merge.
They are re-derived a third time with the same rule `test_ltx2_video` uses.

FOLLOWING_AGENTS_PROTOCOL

Issue: #923

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
…ady landed, and its test could not tell (#975)

A fresh review returned FAIL on three findings, and the largest of them is that
the refusal this row rewrote was FALSE before it merged.

## The refusal named a cause the tree had closed

Row `LTX25-IC-LORA` moved the reference-video refusal off the IC-LoRA metadata,
which it had genuinely closed, and onto the token-APPEND machinery. That was
accurate on 2026-08-15. Row `LTX25-TOKEN-APPEND` landed the seam in `c7cb59fbb`
on 2026-08-16, before this branch merged, so both reasons the message had ever
given were false at once. `#964` left the wording byte-identical only because
this pull request was open and its cause was then still true.

The determination was made again from the merged tree rather than inherited.
The attention-strength wrapper is not in the way either: on the DEFAULT arm
upstream sets `attn_mask = None` at `conditioning_attention_strength >= 1.0`
with no latent mask (`iclora_utils.py:159-160`) and applies
`ConditioningItemAttentionStrengthWrapper` only `if attn_mask is not None`
(`:168-169`), so #932 is not the blocker for the default case.

TWO causes remain, and the refusal now names both.

1. The reference CLIP has no pixel path. Upstream reads it at `height // scale`
   by `width // scale` (`iclora_utils.py:116-117`), refuses a target the factor
   does not divide (`:112-115`), keeps frame 0 then every Nth frame
   (`temporal_subsample`, `:87-89`, called at `:144`) and encodes the whole clip
   (`:145-148`). This engine's only pixel-to-latent route encodes exactly one
   frame at the phase's own resolution, and nothing reads `ref_video_dir`.
2. The reference item is a STAGE-1 item and stage 2 must run UNFUSED.
   `ic_lora.py:108` gives stage 1 `loras=tuple(loras)` and the reference
   conditioning (`:269-278`, `:377-402`); `:119` gives stage 2 `loras=()` and
   `:314-321` gives it `combined_image_conditionings` with no reference item.
   This engine holds ONE DiT, fused at load, that every phase runs.

Serving the arm is therefore its own row, filed as #975. Piece 2 changes how the
engine holds its weights, not how it conditions, and a second resident DiT is a
memory decision this row cannot take on its own.

## The test could not have caught any of that, and now can

The case asserted five SUBSTRINGS. Two were upstream symbol names, present in
the pinned checkout whatever this engine can do, and three were literals the
message declared about itself. None could go red when the ENGINE changed. The
reviewer proved it: replacing the local-cause sentence with a self-declared
falsehood, keeping all five substrings, left `test_ltx2_video` at 44 cases /
914 assertions / SUCCESS.

The case now MEASURES the engine first. It renders with and without an appending
conditioning item, reads `video_tokens` — the one trace field written inside the
phase loop — and requires the grown count to exceed the plain one and both
renders to come back at the target frame count. Only then does it constrain the
message, and the property it asserts is positional rather than lexical: every
occurrence of a closed cause must sit after the `WHAT IS *NOT* THE REASON`
marker, because recording a ruled-out cause is the message's own convention and
must stay possible.

## The bf16 headline was only two thirds gated

The aggregation dtype binds three roundings, and only two had cases:
`B * strength` (`fuse_loras.py:113`) and `deltas.add_(weight)` (`:67-68`).
The matmul RESULT's own `.to(dtype=dtype)` had none — widening only it left
`test_ltx2_lora` at 13/13 and `test_ltx2_loader` at 31/31.

The new case puts `acc = 1 + 2^-8` exactly on a bf16 tie and adds `w = 2^-9`, so
the ported order stores 1.0 and an f32 accumulator stores 1.0078125, one bf16
step apart in the STORED result where the final rounding cannot absorb it.

## Records

`.agents/issue-index.md` carried a SECOND `#930` row describing that blocker as
open. #930 is closed, and main already carries the authoritative row, so the
duplicate is dropped before it lands — the append-only rule protects rows that
exist on `main`, and this one never did. The `#932` row's "blocked behind #930"
is corrected, and #975 is appended.

FOLLOWING_AGENTS_PROTOCOL

Issue: #975

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
One conflict, in `docs/FEATURES.md`, and one reconciliation that no conflict
marker could have raised.

The keyed FEATURES row is resolved by taking main's version and reapplying this
row's scoped edit: 186 of the 188 keys are byte-identical to `origin/main`, the
two that differ are the two this row owns, and no key is added or removed.
Main's LTX-2.5 DiT cell had trimmed `IMAGE` to `IMG` and `Speed PENDING` to
`PENDING` to fit `GENkf`; that trim is kept and the merged cell is 218
characters against a 220 limit, with `LoRA` moved from the refused list to the
served one.

The reconciliation is the campaign's refusal counter. #929 and this row both
rewrote a refusal onto the token-append machinery, and `c7cb59fbb` falsified
both on 2026-08-16 while both were open in review. #929 landed leaving the
counter at SIX and reasoning that a near-miss caught in review is not one of
them. That reasoning applies to this row too, so the counter stays at SIX here
and the comment records both near-misses instead of claiming a seventh. Git
would have merged a SIX and a SEVEN into one of them silently.

`docs/USAGE.md`, `src/vllm/multimodal/ltx2_video.cpp` and
`tests/vllm/multimodal/test_ltx2_video.cpp` auto-merged; #929's additions sit
below this row's, and the READER ANCHORS were re-derived and are unchanged
because #929 deliberately placed its own namespace below the last anchored line.

FOLLOWING_AGENTS_PROTOCOL

Issue: #923

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]
@localai-bot
localai-bot merged commit 885c96f into main Aug 16, 2026
21 of 27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants