Skip to content

port(ltx-2.5): L10 — a text prompt drives the 12B tower, and the oracle L3 said did not exist (#435) - #554

Merged
localai-bot merged 6 commits into
row/MODEL-DIFFUSION-LTX25from
row/LTX25-L10-TEXT-TOWER
Aug 13, 2026
Merged

port(ltx-2.5): L10 — a text prompt drives the 12B tower, and the oracle L3 said did not exist (#435)#554
localai-bot merged 6 commits into
row/MODEL-DIFFUSION-LTX25from
row/LTX25-L10-TEXT-TOWER

Conversation

@localai-bot

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

Copy link
Copy Markdown
Collaborator

Phase L10 of the LTX-2.5 campaign (#435): make a text prompt actually work.

Branched from row/MODEL-DIFFUSION-LTX25, merged back up to 3f5ae3f45.

What changed

LTX-2.5 advertises text-to-video and could not be typed at. This makes the prompt path run end to end: a real string tokenizes with the tokenizer the checkpoint ships as a tensor, the torchao-NVFP4 Gemma-4 tower is materialized onto Gemma4Weights and forwarded, all 49 hidden states feed the existing aggregation, and both caption projections emit the 4096-wide video and 2048-wide audio conditioning.

The headline finding

L3's "the tower cannot be gated" was a transformers version, not a property of the tower. Measured both ways on this box: 5.3.0 raises KeyError 'gemma4_unified'; 5.12.1 builds and runs it. Gemma4Model::ForwardHiddenStates is now held to a running upstream for the first time — gemma4.h has said "grounded + compiles" since it landed.

The tolerance is a measurement, not a constant: the generator measures how far upstream's own answer moves between f32 and bf16, per state, and that is the bound. Worst state reaches 0.71x of its own floor; state 0 is bit-identical. We are closer to upstream-in-bf16 than upstream-in-bf16 is to upstream-in-f32, at every layer.

Mutation-proven: dropping layer_scalar → 34 assertions RED; capturing the raw last layer instead of the final-normed one → 3 RED. Tree restored byte-for-byte after each.

Measured end to end on the real 12B checkpoint

329 torchao-NVFP4 modules dequantized; 48 layers, 8 full-attention
prompt "a red fox running through deep snow at sunrise" -> 10 tokens
  -> video [1024, 4096]  max|v| = 34.0715  mean|v| = 0.0401384
  -> audio [1024, 2048]  max|v| = 27.1555
a DIFFERENT prompt differs by max|diff| = 33.7318
6,292,805 assertions, 0 failed

Tokenization is token-exact against HuggingFace on the shipped 262144-entry vocab: four prompts x 1024 positions, first mismatching index -1 on all four.

What is deliberately NOT done

has_encoder() is still false, and that is the honest answer rather than an omission. Upstream routes each stream through an Embeddings1DConnector before cross-attention. Its math is ported (Ltx2ConnectorForward); its weights are not loaded — they ship inside the DiT file as video_embeddings_connector.* / audio_embeddings_connector.* and remain among the modules Ltx2LoadDitFromSafetensors refuses. So the conditioning has nowhere to go, and true would promise a render that cannot complete.

The refusal therefore moves rather than lifts: encoder_path is still refused, with a message that now names the connector weights instead of the tower, because the old message is no longer true. Flipping the flag is one change away, in ltx2_loader.* / ltx2_video.cpp, which this row does not own.

Baselines — all matching, none moved

test_ltx2 29/1615 · test_ltx2_vae 16/1816 · test_ltx2_pipeline 35/2358 · test_ltx2_loader 20/2363 · test_ltx2_video 17/170 · test_ops_attention_cross 9/32 · test_minimax_h3 79/57395 · test_minimax_h3_video_fold 6/137 · test_video_engine 11/254 · test_capi 55/505

Gemma-4, behaviour-unchanged: test_gemma4_honesty 2/6 · test_gemma4_rocm_fp8_seams 2/10 · test_gemma3_forward 3/503 · test_tool_parser_gemma4 72/161 · test_gemma4_{vision_tower,registry_e2e,audio_tower,paged_engine} 1 case / 0 assertions each

test_ltx2_text_encoder 17/3350 → 23/3583 → 26/4115 (review round 1 added the rope table, the CI-reachable loader with six refusal subcases, and the prompt→conditioning value gate); 4131 with CHECKPOINT_ROOT, and the e2e opt-in on top.

Review round 1 — eight findings, at 9fbc68256

Six close as written. Two do not, and the oracle is why; both are argued in the commit message rather than worked around.

verdict
F2 conditioning value gate + CI-reachable loader CLOSED, and the M6 premise disproved
F3 partial_rotary_factor unreachable CLOSED by a different instrument — the prescribed fix is refuted
F4 generator's lossily-coupled legs CLOSED
F5 337 → 329, pinned exactly CLOSED
F7 inverted tokenizer rationale CLOSED
F8 unannotated f32 KV CLOSED by narrowing to bf16
F9 dropped docs/FEATURES.md edit CLOSED, and docs/USAGE.md had a real contradiction too
F1, F6 out of scope by instruction — another implementer owns them; none of their files touched

F3's prescription does not work. Measured against the oracle, signal = max|bf16 @0.25 − bf16 @1.00| over floor = max|f32 − bf16|, worst over 13 states:

committed        head_dim  8/16  seq  8    1.095
wider full head  head_dim 16/32  seq  8    0.258
longer seq       head_dim  8/16  seq 24    0.764
wider + longer   head_dim 16/32  seq 24    0.523
wider + longer   head_dim 16/32  seq 32    0.650

Enlarging the fixture makes it worse — bf16 accumulation noise grows at least as fast as the rope contribution. The hidden states are the wrong instrument, not a badly sized one. So BuildProportionalRopeCache's host computation is exported as Gemma4ProportionalRopeCosSin and compared in f32 to the oracle's own Gemma4UnifiedTextRotaryEmbedding (new golden section 6): agreement 1.267e-07 against a 1e-6 bound, plus an exact structural check on the zero-padded pairs.

M6 is not a defect. RoPE is relative and the pads are masked, so a uniform position shift cancels. In the oracle, the same 8 tokens told 12..19 and told 0..7:

f32   max|diff| 5.11e-05 over max|value| 14.35   3.6e-06 relative
bf16  max|diff| 1.09                             0.65-1.70x the dtype floor

The comments claiming renumbering "rotates every query by the wrong angle" — in ltx2_text_encoder.cpp and in the left-pad test — were overclaims and now carry that measurement. The numbering stays mirrored for fidelity to transformers, not for arithmetic.

Mutations, all reproduced

mutation result
rope_partial_full = 1.0 (M3) RED 202 assertions, worst 0.56533 vs 1e-6, exit 1
positions[i] = i (M6) RED 1 assertion, 1.10x the audio floor, exit 1
loader concat q,k,vq,v,k RED 14 assertions, exit 1
PLE refusal disabled RED exit 1
missing-v_proj refusal disabled RED exit 1
module dtype F16 read as bf16 RED exit 1
norm vector F16 read as bf16 RED exit 1
KV cache back to f32 (F8) GREEN, output byte-identical — which is the point

Gate

cmake --build bld -j 12                BUILD_EXIT=0, no ENOSPC/BFD, df 88%
ctest -N                               409 tests
ctest -j 8 --output-on-failure         409/409 passed, exit 0
./bld/tests/test_ltx2_text_encoder     26 cases / 4115 assertions, exit 0
                                       (baseline 23 / 3583)

test_cpu_x86_llamacpp_floor failed once under load 46 with waiting for quiet: busy=107%, then passed 10/10 serially at load 29. Contention, not this change.

Still owed, for the operator

.agents/specs/ltx-2-5.md is operator-owned and was not edited. It needs two things. §4.2's open question stands and nothing here narrows it: the shipped NVFP4 tower's output is still never compared to an independent oracle, so "all 49 hidden states within the oracle's bf16 noise floor" is proven for the synthetic reduced tower with bf16 weights, not for the quantized one that ships. PR #571 built the independent-oracle correlation technique that would close it. Separately, the position-numbering claim appears in the spec in the same overclaiming form corrected here.

Review notes

  • .agents/specs/ltx-2-5.md has no L10 row — the spec is the operator's and was not edited. The phase brief stood in for it, which is a departure from "spec before code" that the operator should reconcile.
  • One commit, because the doc-checkpoint gate is per-commit and every part owes the same two documents. The four blocks in the commit message are how it should be read.
  • doc-checkpoint range still reports b0aa475a3 and d67f8125e — both pre-existing on the base branch, neither from this row.

Re-review repair — head ddfcb516a (#435, #604)

The re-review returned FAIL on one finding (F7), closed everything else, and confirmed both of the previous round's refutations. Comment accuracy only: the code is byte-identical, the only non-comment line in the diff being a trailing comment on an unchanged statement.

F7: CLOSED — and the previous round had the direction RIGHT

The header correction landed at ltx2_text_encoder.h:427 and left the source at ltx2_text_encoder.cpp:691 saying the opposite thing, twelve lines above the Encode call it annotates. Same premise, contradictory predictions.

The reviewer could not reach the upstream anchor, so which one was correct was taken on trust. Verified here at /home/mudler/_git/LTX-2, pin fd4ded7f:

packages/ltx-core/src/ltx_core/text_encoders/gemma/tokenizer.py:37-43

encoded = self.tokenizer(
    text, padding=False, truncation=True,
    max_length=self.max_length, return_tensors="pt",
)

That is __call__ with its default add_special_tokens=True, so upstream does run the post_processor. The header was right; the source was the inverted one, and the source is what changed.

A third claim in that same comment was also false: "Doing both would double the BOS". Upstream's own guard at :45 (not input_ids or input_ids[0] != bos_id), mirrored at ltx2_text_encoder.cpp:712, absorbs a leading BOS from either call. The real exposure is a post_processor that adds anything else.

Sibling sweep

Built from the subject (post_processor, BOS, add_special_tokens, Encode), not from the sentences being fixed. Two further hits, both corrected:

site defect
scripts/gen-ltx2-prompt-tokens-goldens.py:86 annotated its add_special_tokens=False as transcribing :38-43 — the same conflation, in the generator that produces the committed goldens. Anchor corrected to :37-43; the deliberate flag flip is now stated
ltx2_text_encoder.h:421 called the BOS prepend "unconditional", contradicting test_ltx2_text_encoder.cpp:2398 ("i.e. conditional") and :45 itself

The two overclaims — wording fixed, tests untouched

Replacing Ltx2ComputeRightPadOrder's stable partition with the identity permutation reds two other cases (22 assertions) and leaves the new conditioning case entirely green. out.conditioning and want_f32 both reach sort_index and additive_mask through the same function over the same out.mask, so a defect there cancels and those ~40 assertions cannot fire. "Held EXACTLY" was an assertion true by construction wearing a gate's label.

Not a coverage hole, and no test changed. The comment now states what the assertions do establish — the mask is threaded through unaltered, and the mask itself is held to the golden above — and names the two cases that actually gate the ordering: "ltx2 text: additive mask, right-pad ordering and the binary mask" and "ltx2 text: the encoder -> conditioning hand-off", both against committed upstream goldens rather than a second call of the code under test.

Coverage limits recorded (not fixed)

  • The loader case's "q|k|v concat ORDER" claim holds for the LTX path only. Ltx2LoadGemmaTowerFromSafetensors uses its own TowerConcat (ltx2_text_encoder.cpp:943-945); gemma4_weights.cpp:281-295 is a separate implementation this suite never loads, and mutating it leaves every case here green. That path owes its own gate.
  • The rope-table instrument covers the full-attention arm only. The sliding layers' default rope at theta 1e4 has no equivalent f32 instrument and is reached only through the hidden states, where the same bf16 noise-floor argument works against resolving a config-carried angle defect.

Docs

Comment-only in src//include//tests/ owes nothing under AGENTS.md, but check-doc-checkpoint is path-derived and demands FEATURES.md (from src/vllm/model_executor/models/) and USAGE.md (from include/vllm/); 9fbc68256 paid it the same way. Both edits are genuine rather than gate-appeasement — they record the tokenization divergence this commit just verified, which is a real user-facing caveat. docs/USAGE.md:454 and :473 were left alone: inherited from the base branch, PR #600's lane.

The FEATURES.md note is folded into an existing paragraph because check-public-doc-tables budgets prose paragraphs (21) as well as cells (220 chars) and rows (600) — the LTX row's cell is already at 217.

Gate — CPU, Release, VLLM_CPP_CUDA=OFF

cmake --build build-f7 -j 16           BUILD_EXIT=0, no ENOSPC / BFD assertion, df 86%
ctest -N                               414
ctest -j 8                             414/414 passed, 0 failed, exit 0
./build-f7/tests/test_ltx2_text_encoder
                                       26 cases / 4115 assertions
                                       Status: SUCCESS, exit 0  (baseline matched exactly)
binary md5 unchanged across the post-docs rebuild (8b48f39b...)

First pass under -j 8; no flaky suite needed a serial re-run. ctest -N enumerates 414, not the 409 in the brief — that count comes from this configure at the unmodified head, and a comment-only diff cannot register a test.

doc-checkpoint range still reports b0aa475a3, d67f8125e and aa6aa0ecd. All three are ancestors of 9fbc68256, inherited, and not repairable from here without editing #600's lane.

🤖 Generated with Claude Code


F10 and the anchor sweep — head 131328e86 (#435, #604)

The third review returned one finding, F10: test_ltx2_text_encoder.cpp:635 cited ltx2_text_encoder.cpp:943-945 for "the LTX tower loader's own TowerConcat". Lines 943-945 are the q_proj TowerModule load. F10 is CLOSED, and it was not alone.

Fixing one number and stopping is what let this class survive three rounds, so every file:line this branch adds over row/MODEL-DIFFUSION-LTX25 was resolved against the tree it points into — extracted mechanically by SHAPE off the added lines of the branch diff (name.ext:NN, name.ext:NN-MM, bare :NN/:NN-MM, "line(s) NN"), never by grepping sentences already known to be wrong.

19 citations added, in 6 files. 19 resolved. 8 wrong, at 6 comment sites.

site cited what is actually there corrected to
test_ltx2_text_encoder.cpp:635 ltx2_text_encoder.cpp:943-945 the q_proj TowerModule load :951-953 (+ helper at :835)
test_ltx2_text_encoder.cpp:1959 modeling_rope_utils.py:187-245 fn is :187-254; the pad is :246 :187-254
test_ltx2_text_encoder.cpp:1992 modeling_rope_utils.py:233 blank line; expr is :234 :234
gemma4.h:260 modeling_gemma4_unified.py:257-274 opens on the previous fn's return :259-275 (cat at :271)
gemma4.h:260 modeling_rope_utils.py:187-245 same anchor, 2nd site :187-254
gen-ltx2-gemma-tower-goldens.py:378 modeling_gemma4_unified.py:214-218 the call, not the routing lookup :206-218 (lookup at :207)
gen-ltx2-gemma-tower-goldens.py:378 modeling_rope_utils.py:187-245 same anchor, 3rd site :187-254
ltx2_text_encoder.cpp:1055 gemma4.cpp:300-312 excludes the CastF32 pair it names :306-315

Three of the eight are one anchor repeated. _compute_proportional_rope_parameters is :187-254, and all three sentences name its zero paddingtorch.zeros(nope_angles, ...) at :246, one line past the cited end. The anchor stops inside the torch.cat( argument list, before the argument that is the whole claim.

gemma4.cpp:300-312 is the same shape in our own tree: it opens on the weight-less V RmsNorm and closes on the } else {, so the vt::CastF32 pair at :313-314 that the sentence charges is outside it. The kv.dtype != adt arm is :306-315.

modeling_gemma4_unified.py:214-218 shows rope_init_fn(...) but not ROPE_INIT_FUNCTIONS[rope_type] at :207, so a reader cannot confirm the word "routes".

F10's anchor is :951-953, not :835. The sentence is the LTX half of a contrast whose other half is gemma4_weights.cpp:281-295 — assembly sites, not a helper definition — and the qkv concat ORDER is what the case gates and what round 1's q,k,vq,v,k mutation (14 assertions RED) perturbed, at :951. :835 is named alongside so the helper stays one hop away.

The eleven that resolve as written, checked individually: tokenizer.py:37-43 (3 sites), :44-46, :45, :12-15, gemma_assets.py:335-386, base_encoder.py:41, pipeline_ltx2.py:339, gemma4_weights.cpp:281-295. Plus two non-line anchors from the F7 commit — the two test-case NAMES it cites — present verbatim at :1115 and :1166.

Pins. LTX-2 fd4ded7f (clean), diffusers 3a2f35d4, transformers 5.12.1 at /home/mudler/recon-cpu/venv — both files md5-identical to /home/mudler/_git/transformers @ 7d06b1a5, so the numbering is not an artifact of which install was read.

Docs

Code is byte-identical; this is comments plus two documents. check-doc-checkpoint is path-derived and bills FEATURES.md + USAGE.md, the same bill 9fbc68256 and ddfcb516a paid. Both edits are genuine, and one is a real defect:

  • USAGE.md still opened its LTX-2.5 section with "There is no LTX-2.5 render path yet … no text encoder, no VAE, no pipeline and no /v1/videos route for it — asking the video engine for LTX-2.5 will not work." Every clause is false and the same document contradicts all of them (ltx2-gen renders e2e at :412, --video-family ltx-2.5 at :435, and the text tower is this row). Rewritten to state what the section is for. The inherited refusal list that follows is left byte-for-byte alone.
  • FEATURES.md pointed at "ltx2_text_encoder.cpp names the line that would have to change" — an unresolvable pointer, in a commit about unresolvable pointers. It now names Ltx2TokenizeGemmaPrompt, by symbol, because a line number in a public doc has nothing to keep it true. Folded into the existing paragraph: the 700-char prose cap and 220-char cell cap make an addition an eviction, and the LTX row's cells are at 217 and 215.

docs/USAGE.md:454 / :473 untouched — #600's lane.

Gate — CPU, Release, VLLM_CPP_CUDA=OFF

cmake --build build-f10 -j 16          BUILD_EXIT=0, 0 hits for 'No space left|BFD assertion', df 85%
ctest -N                               414
ctest -j 8 --output-on-failure         414/414 passed, 0 failed, exit 0
./build-f10/tests/test_ltx2_text_encoder
                                       26 cases | 4115 assertions | 0 failed
                                       Status: SUCCESS, exit 0  (baseline matched exactly)

First pass under -j 8; no suite needed a serial re-run. Build dir deleted. doc-checkpoint --staged and now-current --staged both ok; doc-checkpoint range still reports b0aa475a3, d67f8125e, aa6aa0ecd — the identical three this worktree recorded before any edit, inherited, not repairable from here.

mudler added 2 commits August 12, 2026 23:36
…le L3 said did not exist (#435)

LTX-2.5 advertises text-to-video and could not be typed at. `has_encoder()` was
false and a prompt-carrying request was refused by name. This makes the prompt
path RUN: a real string tokenizes with the tokenizer the checkpoint ships as a
TENSOR, the torchao-NVFP4 Gemma-4 tower is materialized and forwarded, all 49
hidden states feed the existing aggregation, and both caption projections emit
the two conditioning streams. One commit because the doc-checkpoint gate is
per-commit and every part owes the same two documents; the four blocks below are
how a reviewer should read it.

────────────────────────────────────────────────────────────────────────────────
1. THE ORACLE L3 RECORDED AS IMPOSSIBLE WAS A `transformers` VERSION

Phase L3 recorded that the tower could not be gated: no `gemma4_unified` in
`CONFIG_MAPPING`, so it could not be built at reduced dims and there was nothing
to compare against. That was never a property of the tower. MEASURED both ways on
this box:

  /usr/bin/python3             transformers 5.3.0  -> KeyError 'gemma4_unified'
  /home/mudler/recon-cpu/venv  transformers 5.12.1 -> builds and RUNS the tower

So `Gemma4Model::ForwardHiddenStates` is now held to a RUNNING upstream instead
of to invariants derived from its own output. gemma4.h has said "grounded +
compiles" since it landed; compiling is not running.

THE TOLERANCE IS A MEASUREMENT. Our stream carries bf16 and widens only on the
way out; bf16 is also upstream's own resolved model dtype (base_encoder.py:41).
So "is a difference a defect?" has a measurable answer: how far does UPSTREAM's
answer move when the SAME upstream code runs at bf16 instead of f32? The
generator measures exactly that, per state, and emits it as the bound. Nobody
picked it, and it cannot be loosened to rescue a failure — loosening it means
regenerating it, which means the oracle moved.

  state   ours vs bf16 oracle   the floor   ratio
      0             0           0.000923    0.00x
      1             0.03125     0.072665    0.43x
      3             0.191406    0.305009    0.63x
      6             0.34375     0.559325    0.61x
      9             0.46875     0.683670    0.69x
     11             0.650391    1.098824    0.59x
     12             0.149658    0.210947    0.71x

Worst 0.71x over all 13 states. We are closer to upstream-in-bf16 than
upstream-in-bf16 is to upstream-in-f32, at every layer; state 0 is bit-identical.
The f32 arm is gated too, at the triangle bound, because a bf16 store is exactly
what absorbs a reduction-order defect.

THE FIXTURE DISCRIMINATES (§7.0(c)). The shipped 12B is not one repeated layer:
(sliding x 5, full) eight times, and the two kinds differ — 16 q heads x 256 with
8 kv heads and a `v_proj`, against 16 x 512 with ONE kv head and no `v_proj` at
all (`attention_k_eq_v` aliases V onto K), plus two rope types at two thetas and
partial rotary on the full arm only. The reduced arch keeps every one and repeats
the pattern twice, so a full layer is neither first nor last and a sliding layer
FOLLOWS a full one.

MUTATION-PROVEN: dropping `layer_scalar` -> 34 assertions RED; capturing the RAW
last layer instead of the final-normed one -> 3 RED. Tree restored byte-for-byte
(md5 040b97bf6b753f04be1855eaefc76ea1) and green re-confirmed after each.

THE CONFIG IS NOT GUESSED. The shipped vonkaiser build has NO `__metadata__`, so
its Gemma config cannot come from the file — which is also why upstream's own
`GemmaAssets.from_single_file` raises on it. The committed config is the
`__metadata__["gemma_config"]` of the OFFICIAL bf16 text encoder, read with an
84 KB HTTP RANGE request over the safetensors header: no large download, and the
authoritative source rather than a reconstruction. It independently confirms
§1.4's corrected width — `video_aggregate_embed.weight` is BF16 [4096, 188160]
there, unpacked, where the NVFP4 build stores [4096, 94080].

`ParseHfConfig(json, source)` exists for that: a config does not always arrive as
a sibling config.json, and writing one to a temp file to read it back would put
the temp directory on a model path.

────────────────────────────────────────────────────────────────────────────────
2. THE PROMPT PATH, AND A TOKENIZER THAT SHIPS AS A TENSOR

LTX-2.5's text encoder embeds its tokenizer as `tokenizer_json` U8 [32169626]
inside the one safetensors file (gemma_assets.py:34-36). Every loader in this
tree assumed a sibling `tokenizer.json`, so none could read this checkpoint;
hence `Tokenizer::FromHfJsonBytes`.

GATE 1 — the WRAPPER, inline fixture, no asset. Strip; BOS prepended
CONDITIONALLY (prepending unconditionally doubles it and drops the last token);
LEFT padding so the valid run is the TAIL; truncate-then-prepend-then-re-truncate
so a maximal prompt loses its LAST token rather than its BOS; an empty prompt
still carrying exactly one BOS; and a refusal with no bos id, where upstream also
raises (tokenizer.py:34-36).

GATE 2 — REAL prompts, the REAL 262144-entry vocab, TOKEN-EXACT. Four prompts x
1024 positions, ids and mask, vs HuggingFace `tokenizers` 0.22.2:

  prompt 0 (10 valid tokens): first mismatching index = -1  (-1 = EXACT)
  prompt 1 (11 valid tokens): -1
  prompt 2 ( 1 valid token ): -1
  prompt 3 (43 valid tokens): -1

No tolerance appears in that gate. The prompts discriminate: one with leading and
trailing whitespace, one EMPTY, one 43-token cinematic prompt with punctuation
and digits.

THE DISAGREEMENT, followed to the model author and recorded. `ltx_core` prepends
BOS explicitly and says why — "Gemma 3 already emits it via post_processor;
Gemma 4 does not, so we prepend" (tokenizer.py:12-15). diffusers instead passes
`add_special_tokens=True` (pipeline_ltx2.py:339). MEASURED on the shipped
tokenizer: its post_processor is a TemplateProcessing with ZERO special_tokens,
so it adds nothing and diffusers would produce every prompt without a BOS. That
measurement is emitted into the goldens header, so the reason is attached to the
artifact.

────────────────────────────────────────────────────────────────────────────────
3. THE TOWER ITSELF, CLOSING L6's RECORDED DEBT

L6 recorded verbatim: "the torchao arm of the Gemma TOWER itself
(`Gemma4Weights`) is not wired ... L6 loads the two caption projections, the
asset pack and the geometry, and VALIDATES every tower module without
materializing it." `Ltx2LoadGemmaTowerFromSafetensors` closes it, reusing L6's
own `Ltx2DequantTorchaoNvfp4ToBf16` unchanged.

WHY NOT `gemma4_weights.cpp`. The LTX file ships FLAT names
(`model.layers.{i}.self_attn.q_proj.weight`); that loader reads the
multimodal-wrapper form `model.language_model.layers.{i}....`. And every U8 width
is HALF its logical one, so each layer's geometry is resolved from the CONFIG and
the stored shapes are checked against it, never the reverse — a loader that
trusted stored widths would build a tower of exactly half the right width whose
matmuls all still conform among themselves.

REFUSALS, each naming a specific wrong model: a PLE config against a checkpoint
with no PLE tensors; `num_kv_shared_layers` != 0, where a shared layer reads a
cache nobody wrote; a `v_proj` present or absent against what `attention_k_eq_v`
declares, which is 16 kv heads of difference that every shape check passes; and a
module in neither the BF16 nor the torchao-NVFP4 form.

THE COST THE PORT DOES NOT PAY. Upstream pads every prompt to 1024 and runs all
1024 rows through a 12B tower; this runs the prompt's OWN length at the original
absolute positions. Both halves of that equivalence are MEASURED, not argued:
upstream's own padded-vs-short f32 spread is 3.2e-05 over a max|value| of 14.35,
and our short run reproduces the padded run's valid rows at 0.5x the dtype bound.
Writing that gate the obvious wrong way is kept in the test rather than quietly
fixed: declaring seq_len = P + T while writing only T keys has attention read P
slots of zeroes and call them keys — max|diff| 17.97 against max|value| 14.35.

MEASURED END TO END on the real checkpoint (opt-in; ~33 GB host, ~26 min CPU):

  329 torchao-NVFP4 modules dequantized; 48 layers, 8 of them full-attention at
  head_dim 512 / 1 kv head / no v_proj, 40 sliding at 256 / 8 kv heads
  prompt "a red fox running through deep snow at sunrise" -> 10 tokens
    -> video [1024, 4096]  max|v| = 34.0715  mean|v| = 0.0401384
    -> audio [1024, 2048]  max|v| = 27.1555
  a DIFFERENT prompt differs by max|diff| = 33.7318
  6,292,805 assertions, 0 failed

VALUES, not `isfinite`: a conditioning tensor of zeros would satisfy every shape
and finiteness check and render an unconditioned clip, and a tower that ignored
its input entirely would pass everything except the two-prompt arm.

────────────────────────────────────────────────────────────────────────────────
4. WHY `has_encoder()` IS STILL FALSE

Upstream does not hand the caption projections to cross-attention. Each stream
goes through an `Embeddings1DConnector` first (embeddings_processor.py:70-117).
Its MATH is ported — `Ltx2ConnectorForward` — but its WEIGHTS are not loaded:
they ship inside the DiT file as `video_embeddings_connector.*` /
`audio_embeddings_connector.*` and remain among the modules
`Ltx2LoadDitFromSafetensors` REFUSES (ltx2_loader.h:96-99). MEASURED in the
shipped FP8 DiT header: 372 such tensors, `learnable_registers [128, 2048]` on
the audio arm, and `attn2.to_k.weight [4096, 4096]` confirming the video stream's
4096 is exactly the width the projections emit.

So the conditioning this change computes has nowhere to go, and a `true` here
would promise a render that cannot complete. The refusal therefore MOVES rather
than lifts: `encoder_path` is still refused, but the message now names the
connector weights instead of the tower, because the old message — "the Gemma-4
TOWER itself is owed" — is no longer true, and a stale refusal is worse than a
blunt one. Flipping the flag is one change away, in files this row does not own.

────────────────────────────────────────────────────────────────────────────────
BASELINES, all matching, none moved

  test_ltx2                 29/1615    test_ltx2_vae            16/1816
  test_ltx2_pipeline        35/2358    test_ltx2_loader         20/2363
  test_ltx2_video           17/170     test_ops_attention_cross  9/32
  test_minimax_h3           79/57395   test_minimax_h3_video_fold 6/137
  test_video_engine         11/254     test_capi                55/505
  test_gemma4_honesty        2/6       test_gemma4_rocm_fp8_seams 2/10
  test_gemma3_forward        3/503     test_tool_parser_gemma4  72/161
  test_gemma4_vision_tower / registry_e2e / audio_tower / paged_engine  1/0 each

  test_ltx2_text_encoder    17/3350 -> 23/3583  (+6 cases; +16 assertions with
                            CHECKPOINT_ROOT, +6,289,222 with the e2e opt-in)

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
mudler added 4 commits August 13, 2026 08:02
…e false

Docs conflict was docs/FEATURES.md for the FOURTH time on the same keyed row
(see #595). Took the campaign side wholesale: it is strictly newer and a
superset, since L10's own text still said "STRUCTURAL e2e only, no
render-quality claim", which stopped being true when L9C wired the connector.

The substantive finding is in the merge, not the docs.

ltx2_video.cpp:672 refuses a supplied encoder_path, and its reason is stated
precisely: the Embeddings1DConnector's MATH is ported but its WEIGHTS are not
loaded, they ship inside the DiT file, and Ltx2LoadDitFromSafetensors "still
lists them among the modules it REFUSES". That was accurate when L10 was
written against its base.

It is NOT accurate on this merged tree. L9c landed exactly that loader:
Ltx2LoadConnectorWeights is called at ltx2_video.cpp:792,794 and
ltx2_loader.cpp:417 says in terms "NOT unported: phase L9c materializes them".

So the two halves of prompt conditioning have met for the first time. L10 makes
the tower RUN -- tokenizer token-exact on the shipped 262144-entry vocab, all 49
hidden states within the oracle's bf16 noise floor, both caption projections
producing the 4096 and 2048 streams. L9c makes the connector those streams must
pass through real, with the checkpoint's own weights. Neither branch alone could
see that, which is exactly the class of thing a merge is supposed to surface and
usually does not.

Deliberately NOT lifting the refusal in this merge commit. Whether the last hop
is complete is a question for a fresh implementer with a gate, not for the
coordinating session to assert. The stale tensor-list comment at
ltx2_loader.h:96-99 is owed the same correction.

This is the third refusal in this campaign whose REASON went false while the
refusal itself stayed correct. A refusal that names its cause is worth far more
than one that does not -- but only if something re-reads the cause when the
tree moves.

Issue: #435

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
…survive measurement (#435)

Eight findings from the L10 fresh review. Six close as written; two close
differently, because the thing they prescribed does not work and the oracle says
so. Both are recorded here rather than quietly worked around, since an
implementer who accepts a prescription that fails is the failure this protocol
exists to catch.

────────────────────────────────────────────────────────────────────────────────
F3 — THE PRESCRIBED FIX IS REFUTED; THE ROPE TABLE IS THE INSTRUMENT

The finding is real: `g.rope_partial_full = 1.0` (full rotary on the full-
attention layers instead of the config's 0.25) left the suite green at 23 cases
/ 3583 assertions. Reproduced here before touching anything.

The prescription was "a fixture whose global_head_dim/seq keep the rope
contribution above the floor". MEASURED against the oracle, per state, as
signal = max|bf16 @0.25 - bf16 @1.00| over floor = max|f32 - bf16|:

    committed        head_dim  8/16  seq  8    worst signal/floor  1.095
    wider full head  head_dim 16/32  seq  8                        0.258
    longer seq       head_dim  8/16  seq 24                        0.764
    wider + longer   head_dim 16/32  seq 24                        0.523
    wider + longer   head_dim 16/32  seq 32                        0.650

Enlarging the fixture makes it WORSE. bf16 accumulation noise grows at least as
fast as the rope contribution, so no reachable fixture size separates them: the
hidden states are the wrong instrument, not a badly sized one.

The right instrument is the table. `BuildProportionalRopeCache`'s host
computation is split out and exported as `Gemma4ProportionalRopeCosSin`, the
generator emits the oracle's own `Gemma4UnifiedTextRotaryEmbedding` cos|sin for
the full-attention layer type (section 6), and the new case compares them in
f32 with nothing accumulating. Measured agreement 1.267e-07 against a 1e-6
bound, plus an EXACT structural check that pairs at and beyond
`int(partial*head_dim//2)` are cos=1, sin=0 and that the pairs below it really
rotate.

    RED  under the mutation: 202 assertions fail, worst 0.56533 vs 1e-6, exit 1
    GREEN restored:          26 cases / 4115 assertions, exit 0

────────────────────────────────────────────────────────────────────────────────
F2 — THE VALUE GATE AND THE LOADER, IN CI; AND M6 IS NOT WHAT IT LOOKED LIKE

`Ltx2EncodePromptToConditioning`, `Ltx2LoadGemmaTowerFromSafetensors` and all
four documented refusals were reachable from exactly ONE place — the opt-in
24 GB case — so none had CI coverage.

They do now. The reduced tower fixture, which is already held to a running
oracle, is written out as a real .safetensors under the CHECKPOINT's tensor
names, loaded back, and compared to the fixture byte for byte; then a tokenizer
whose added tokens spell the oracle's own token ids drives the whole prompt path
and its conditioning is held to the committed LEFT-PADDED oracle run at a floor
propagated through the identical projection.

    concat order q,k,v -> q,v,k          RED  14 assertions, exit 1
    PLE refusal disabled                 RED   1 assertion,  exit 1
    missing-v_proj refusal disabled      RED   1 assertion,  exit 1
    module dtype F16 read as bf16        RED   1 assertion,  exit 1
    norm vector F16 read as bf16         RED   1 assertion,  exit 1
    conditioning at 0.565x / 0.688x of its propagated floor

Now the part that did not survive. The review called `positions[i] = i` "the
exact defect the surrounding comment warns about". It is not a defect at all.
Rotary embedding is RELATIVE and the pads are masked out of attention, so
shifting every position by the pad count cancels. MEASURED in the oracle, same
8 tokens told 12..19 and told 0..7:

    f32   max|diff| 5.11e-05 over max|value| 14.35   3.6e-06 relative
    bf16  max|diff| 1.09                             0.65-1.70x the dtype floor

f32 round-off, and at bf16 the rounding of different absolute angles. So the
comment at ltx2_text_encoder.cpp — "a port that renumbers from zero rotates
every query by the wrong angle" — was an overclaim, and so was its twin in the
left-pad case. Both now carry the measurement. The numbering is still mirrored,
because transformers derives positions from `cache_position` and fidelity is the
reason; arithmetic is not. The new case does red under the renumbering, at 1.10x
the audio floor, and says in its own comment that the narrow margin is a
property of the defect rather than of the gate.

────────────────────────────────────────────────────────────────────────────────
F4 — THE GENERATOR'S LEGS WERE COUPLED, AND NOT WHERE IT LOOKED

`run_tower` did `inner.to(dtype)`, which converts IN PLACE, so every leg after
the bf16 one ran downstream of it. Each leg now runs on a `copy.deepcopy`.

The mechanism is narrower than "bf16-rounded weights": MEASURED on this
transformers build, the parameters round-trip unchanged, and it is the rotary
embedding's non-persistent `inv_freq` BUFFERS that do not — 9.36e-05 on the
full-attention table, 9.77e-05 on the sliding one. Regenerating moved 12 of the
13 padded f32 states (state 12 by 1.03e-02 on values of 2.705) and every entry
of the pad-equivalence vector, while sections 2, 3 and 3b are byte-identical —
which is exactly the partition the in-place conversion predicts. The header's
claims about sections 4 and 5 are corrected to match.

────────────────────────────────────────────────────────────────────────────────
F5, F7, F8, F9

F5  337 -> 329, and pinned exactly rather than `> 300`. The count is arithmetic
    and now says so: 40*7 + 8*6 + 1. A lower bound is satisfied by a loader that
    quietly took a third of the layers from a bf16 fallback.

F7  The rationale was inverted. Upstream calls `self.tokenizer(text, ...)` —
    `__call__` with its default `add_special_tokens=True` — so upstream DOES run
    the post-processor and we do not. Identical on this checkpoint because its
    `special_tokens` map is empty, measured. "THE TWO REFERENCES DISAGREE" also
    overstated it: both let the post-processor run, and only the explicit BOS
    prepend differs. Both corrected.

F8  `kv.dtype` on the production prompt path was f32 where upstream resolves
    bf16 and where the attention itself runs bf16. Narrowed, not annotated. It
    was never wrong — it was WIDER, which is why nothing could see it: with the
    cache back at f32 the new conditioning gate reproduces 0.0617859 / 0.0394362
    byte for byte. Half the cache bytes, and it stops taking `Gemma4AttnBlock`'s
    two-cast-buffers-plus-CastF32 arm on every layer.

F9  The dropped `docs/FEATURES.md` edit is reapplied onto the campaign's
    rewritten row, truthfully: "Text tower RUNS" alongside the `encoder_path`
    refusal, which another change owns and this one does not touch. Row 533
    chars, cells 60/28/217/215.

    `docs/USAGE.md` is owed too, and the debt turned out to be real rather than
    procedural: line 372 still said "the Gemma-4 12B text tower is not ported",
    which is false and which the paragraph 85 lines below it already
    contradicted. One document, two answers about one model. Corrected to state
    that the tower runs and to defer to that paragraph for what still blocks a
    prompt. The `encoder_path` passage itself is untouched — it belongs to the
    change that owns F1.

────────────────────────────────────────────────────────────────────────────────
NOT CLOSED HERE, AND OWED

F1 and F6 are out of scope by instruction — another implementer owns whether the
`encoder_path` refusal can be lifted now that L9c landed the connector weights.

The spec is operator-owned, so this reports rather than edits it. Two things it
needs. First, §4.2's open question stands and should not be narrowed by anything
above: the shipped NVFP4 tower's output is still never compared to an
independent oracle, so "all 49 hidden states within the oracle's bf16 noise
floor" is proven for the SYNTHETIC reduced tower with bf16 weights and not for
the quantized one that ships. PR #571 built the independent-oracle correlation
technique that would close it. Second, the position-numbering claim appears in
the spec in the same overclaiming form corrected here.

────────────────────────────────────────────────────────────────────────────────
GATE

    cmake -S . -B bld -G Ninja -DCMAKE_BUILD_TYPE=Release -DVLLM_CPP_BUILD_EXAMPLES=OFF
    cmake --build bld -j 12                    BUILD_EXIT=0, no ENOSPC/BFD, df 88%
    ctest -N                                   409 tests
    ctest -j 8 --output-on-failure             409/409 passed, exit 0
    ./bld/tests/test_ltx2_text_encoder         26 cases / 4115 assertions, exit 0
                                               (baseline was 23 / 3583)

`scripts/agent-preflight.sh` fails `doc-checkpoint range` on commits b0aa475,
d67f812 and aa6aa0e. All three are ancestors of the branch head this work
started from and none is touched here.

Issue: #435

FOLLOWING_AGENTS_PROTOCOL

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

The last round corrected the post_processor rationale in the HEADER and left the
SOURCE saying the opposite thing, twelve lines above the `Encode` call it
annotates. The two made contradictory predictions from the same premise: the
header said a future post_processor makes us WRONG, the source said it keeps us
RIGHT. F7's whole content was "the rationale was inverted", so half of it was
still open.

VERIFIED AGAINST UPSTREAM, not taken on trust. The reviewer could not reach the
anchor; this was read at /home/mudler/_git/LTX-2 pin fd4ded7f:

    packages/ltx-core/src/ltx_core/text_encoders/gemma/tokenizer.py:37-43

        encoded = self.tokenizer(
            text, padding=False, truncation=True,
            max_length=self.max_length, return_tensors="pt",
        )

That is `__call__` with its DEFAULT `add_special_tokens=True`, so upstream DOES
run the post_processor. The HEADER had the direction right; the SOURCE was the
wrong one, and it is the source that is corrected here.

A THIRD claim in the same comment was also false. "Doing both would double the
BOS" is not true: upstream's own guard at :45 (`not input_ids or input_ids[0] !=
bos_id`), mirrored at ltx2_text_encoder.cpp:712, absorbs a leading BOS from
either call. The real exposure is a post_processor that adds anything ELSE, and
the comment now says that instead.

SIBLING SWEEP, built from the SUBJECT (post_processor, BOS, add_special_tokens,
Encode) rather than from the sentences being fixed, since a grep built from the
new text only confirms itself. Two more hits, both corrected:

  * gen-ltx2-prompt-tokens-goldens.py:86 annotated its
    `add_special_tokens=False` as transcribing ":38-43" -- the same conflation,
    in the generator that produces the committed goldens. Anchor corrected to
    :37-43 and the deliberate flag flip is now stated.
  * ltx2_text_encoder.h:421 called the BOS prepend "unconditional", which
    contradicts test_ltx2_text_encoder.cpp:2398 ("i.e. conditional") and :45
    itself. It is conditional, and the guard is why.

────────────────────────────────────────────────────────────────────────────────
TWO COMMENT OVERCLAIMS, PROVED BY THE REVIEWER'S MUTATION

Replacing `Ltx2ComputeRightPadOrder`'s stable partition with the identity
permutation reds two OTHER cases (22 assertions) and leaves the new conditioning
case entirely green. `out.conditioning` and `want_f32` both reach `sort_index`
and `additive_mask` through the SAME function over the SAME `out.mask`, so a
defect there cancels and those ~40 assertions cannot fire. "Held EXACTLY" was
an assertion true by construction wearing a gate's label.

This is NOT a coverage hole and no test changes. Both are genuinely gated by
"ltx2 text: additive mask, right-pad ordering and the binary mask" and "ltx2
text: the encoder -> conditioning hand-off", which compare against committed
upstream goldens rather than against a second call of the code under test. The
comment now says what its assertions do establish (the mask is threaded through
unaltered, and the mask itself is held to the golden) and names the two cases
that actually gate the ordering.

────────────────────────────────────────────────────────────────────────────────
TWO COVERAGE LIMITS RECORDED, NOT FIXED

  * The loader case's "the q|k|v concat ORDER" claim holds for the LTX path
    ONLY. `Ltx2LoadGemmaTowerFromSafetensors` uses its own `TowerConcat`
    (ltx2_text_encoder.cpp:943-945); `gemma4_weights.cpp:281-295` is a SEPARATE
    implementation this suite never loads, and mutating it leaves every case
    here green. That path owes its own gate.
  * The rope-table instrument covers the FULL-ATTENTION arm only. The sliding
    layers' `default` rope at theta 1e4 has no equivalent f32 instrument and is
    reached only through the hidden states, where the same bf16 noise-floor
    argument works against resolving a config-carried angle defect.

────────────────────────────────────────────────────────────────────────────────
DOCS

Comment-only in src/include/tests, so AGENTS.md owes nothing here -- but
check-doc-checkpoint is path-derived and demands FEATURES.md (from
src/vllm/model_executor/models/) and USAGE.md (from include/vllm/). The prior
repair at 9fbc682 paid it the same way. Both edits are GENUINE rather than
gate-appeasement: they record the tokenization divergence this commit just
verified, which is a real user-facing caveat -- our prompt tokenization mirrors
upstream only while the checkpoint's post_processor stays empty. USAGE.md:454
and :473 are left alone; they are inherited from the base branch and belong to
PR #600's lane.

The FEATURES.md note is folded into an existing paragraph rather than added as a
new one, because check-public-doc-tables budgets prose paragraphs (21) as well
as table cells (220 chars) and rows (600), and the LTX row's cell is already at
217.

CODE IS BYTE-IDENTICAL. The only non-comment line in the diff is a trailing
comment on an unchanged statement.

GATE, CPU, Release, VLLM_CPP_CUDA=OFF:
  BUILD_EXIT=0, no "No space left" / "BFD assertion", df 86% used
  test_ltx2_text_encoder: 26 cases / 4115 assertions, Status SUCCESS, exit 0
    (matches the declared baseline exactly)
  ctest -N: 414 (the brief said 409; this configure enumerates 414 at the
    unmodified head, and a comment-only diff cannot register a test)
  ctest: 414/414 passed, 0 failed, exit 0, first pass under -j 8, no flaky
    re-runs needed
  binary md5 unchanged across the post-docs rebuild (8b48f39b...)

check-doc-checkpoint "range" still reports b0aa475, d67f812 and aa6aa0e.
All three are ancestors of 9fbc682, inherited, and not repairable from here
without editing #600's lane.

Issue: #435, #604

FOLLOWING_AGENTS_PROTOCOL

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

The re-review found `test_ltx2_text_encoder.cpp:635` citing
`ltx2_text_encoder.cpp:943-945` for "the LTX tower loader's own `TowerConcat`".
Lines 943-945 are the `q_proj` `TowerModule` load. The concat calls are :951 and
:953; the helper is defined at :835. The substantive claim was measured and is
true, and only the number was wrong -- which is the worst shape for this defect,
because a reader who follows the anchor concludes a TRUE claim is unsupported.

Fixing one number and stopping is what let the class survive three review
rounds, so every `file:line` this branch ADDS over `row/MODEL-DIFFUSION-LTX25`
was resolved against the tree it points into. Extracted mechanically by SHAPE
off the added lines of `git diff origin/row/MODEL-DIFFUSION-LTX25...HEAD`
(`name.ext:NN`, `name.ext:NN-MM`, bare `:NN` / `:NN-MM`, "line(s) NN", "SS N"),
not by grepping the sentences already known to be wrong -- a grep built from
text you just fixed only confirms itself.

19 citations added, in 6 files. 19 resolved. 8 wrong, at 6 comment sites.

  site                                    cited                     is
  ------------------------------------------------------------------------
  test_ltx2_text_encoder.cpp:635          ltx2_te.cpp:943-945       q_proj load
  test_ltx2_text_encoder.cpp:1959         rope_utils.py:187-245     ends at 254
  test_ltx2_text_encoder.cpp:1992         rope_utils.py:233         blank line
  gemma4.h:260                            gemma4_unified.py:257-274 prev fn tail
  gemma4.h:260                            rope_utils.py:187-245     ends at 254
  gen-ltx2-gemma-tower-goldens.py:378     gemma4_unified.py:214-218 call, no
                                                                    lookup
  gen-ltx2-gemma-tower-goldens.py:378     rope_utils.py:187-245     ends at 254
  ltx2_text_encoder.cpp:1055              gemma4.cpp:300-312        cuts the cast

The three `modeling_rope_utils.py:187-245` sites are one anchor repeated. The
function is `_compute_proportional_rope_parameters`, :187-254, and all three
sentences name its ZERO PADDING -- which is `torch.zeros(nope_angles, ...)` at
:246, ONE LINE past the cited end. Following the anchor is following it to the
`torch.cat(` that opens two lines earlier and stopping before the argument that
matters. Corrected to :187-254, with :246 named where the padding is the claim.

`modeling_rope_utils.py:233` is a blank line. `rope_angles = int(rope_proportion
* head_dim // 2)`, which the comment quotes, is :234.

`modeling_gemma4_unified.py:257-274` opens on the PREVIOUS function's `return`
(:257) and stops before `forward`'s own (:275). The `emb = torch.cat((freqs,
freqs), dim=-1)` the sentence mirrors is :271, so the anchor does contain it,
but both ends land in the wrong construct. Corrected to the whole method,
:259-275, with :271 named.

`modeling_gemma4_unified.py:214-218` is the CALL, `rope_init_fn(self.config,
**rope_init_fn_kwargs)`. The sentence claims the class "routes `rope_type:
proportional` to `_compute_proportional_rope_parameters`", and the routing is
`ROPE_INIT_FUNCTIONS[rope_type]` at :207, outside the range -- a reader at
:214-218 cannot see what `rope_init_fn` resolves to, so the claim reads
unsupported. Widened to :206-218.

`gemma4.cpp:300-312` opens on the weight-less V `RmsNorm` at :300 and closes on
the `} else {` at :312, which EXCLUDES the `vt::CastF32` pair at :313-314 that
the sentence is about ("run a CastF32 on every K and V it wrote"). The
`kv.dtype != adt` arm is :306-315. Corrected, with the two `DBuf`s and the casts
named separately, because those are the two costs the sentence charges.

The eleven that resolve as written, checked one by one, not assumed: LTX-2
`tokenizer.py:37-43` (three sites, the `self.tokenizer(...)` call), `:44-46`,
`:45`, `:12-15`, `gemma_assets.py:335-386`
(`build_text_encoder_tensors_from_gemma_root`, :335 to its `return` at :386),
`base_encoder.py:41`; diffusers `pipeline_ltx2.py:339` (literally
`add_special_tokens=True`); ours `gemma4_weights.cpp:281-295`, which does span
all three qkv arms as the previous round reported. Two non-line anchors added by
the F7 commit -- the two test-case NAMES it cites as the ones that gate the
ordering -- exist verbatim at :1115 and :1166.

PINS. LTX-2 at `fd4ded7f`, tree clean. diffusers at `3a2f35d4`. transformers
5.12.1 at `/home/mudler/recon-cpu/venv`, the interpreter the tower generator
actually ran under; both files are md5-IDENTICAL to `/home/mudler/_git/
transformers` @ `7d06b1a5`, so the numbering above is not an artifact of which
install was read.

F10's anchor. `:951-953` and not `:835`, because the sentence is the LTX half of
a contrast whose other half is `gemma4_weights.cpp:281-295` -- assembly SITES,
not a helper definition -- and because the qkv concat ORDER is what the case
gates and what the previous round's mutation (`q,k,v` -> `q,v,k`, 14 assertions
RED) perturbed at :951. `:835` is named alongside it so the helper is still one
hop away.

DOCS. The code is byte-identical; this is comments and two documents.
`check-doc-checkpoint` is path-derived and demands `FEATURES.md` (from
`src/vllm/model_executor/models/`) and `USAGE.md` (from `include/vllm/`), which
is the same bill `9fbc68256` and `ddfcb516a` paid. Both edits are genuine.

`USAGE.md` still opened its LTX-2.5 gate section with "**There is no LTX-2.5
render path yet** ... no text encoder, no VAE, no pipeline and no `/v1/videos`
route for it -- asking the video engine for LTX-2.5 will not work." Every clause
is false, and the SAME DOCUMENT contradicts all of them: `ltx2-gen` renders end
to end at :412, `--video-family ltx-2.5` pins the family on the server at :435,
and the text tower is this row. Rewritten to say what the section is for. The
refusal list that follows is left byte-for-byte alone: it is inherited text, it
is not part of this finding, and `ltx2.h:38` and `:47-49` do support the two
clauses of it I checked.

`FEATURES.md` pointed at "`ltx2_text_encoder.cpp` names the line that would have
to change" -- an unresolvable pointer, in a commit about unresolvable pointers.
It now names `Ltx2TokenizeGemmaPrompt`. A symbol, deliberately, because a line
number in a public document has nothing that can keep it true. Folded into the
existing paragraph: the 700-char prose cap and the 220-char cell cap make an
addition an EVICTION of someone else's content, and the LTX row's two cells are
already at 217 and 215.

`docs/USAGE.md:454` and `:473` untouched -- #600's lane.

GATE -- CPU, Release, `VLLM_CPP_CUDA=OFF`, `build-f10`, deleted after.

  cmake --build build-f10 -j 16     BUILD_EXIT=0; 0 hits for
                                    'No space left|BFD assertion'; df 85%
  ctest -N                          414
  ctest -j 8 --output-on-failure    414/414 passed, 0 failed, exit 0
  ./build-f10/tests/test_ltx2_text_encoder
                                    26 cases | 4115 assertions | 0 failed
                                    Status: SUCCESS, exit 0
                                    (baseline 26 / 4115 matched exactly)

First pass under `-j 8`; no suite needed a serial re-run. `doc-checkpoint
--staged` and `now-current --staged` both ok. `doc-checkpoint range` still
reports `b0aa475a3`, `d67f8125e` and `aa6aa0ecd` -- the identical three failures
this worktree recorded BEFORE any edit, all ancestors of `9fbc68256`, inherited
and not repairable from here.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [Claude Code]
@localai-bot
localai-bot merged commit c43b5fa into row/MODEL-DIFFUSION-LTX25 Aug 13, 2026
@localai-bot

Copy link
Copy Markdown
Collaborator Author

Landed on row/MODEL-DIFFUSION-LTX25 at c43b5fa94 — verified --is-ancestor. Operator gate on the merged tree: BUILD_EXIT=0, zero warnings, enospc 0, ctest 415/415 exit 0, first pass.

Reviewed PASS at 131328e86 after three FAIL rounds (9 → 1 → 1 findings; converging, each real).

The last round is the one worth reading. Told to fix ONE wrong file:line, the implementer swept every citation the branch adds — 19 of them — and found EIGHT wrong at six sites. Fixing the instance would have shipped seven.

Three of the eight are the same anchor repeated: modeling_rope_utils.py:187-245, cited three times as the authority for that function's zero padding, which sits at :246 — one line past the cited end. The range stops inside the torch.cat( argument list, immediately before the argument that is the entire claim. Our own gemma4.cpp:300-312 has the identical shape, closing on } else { and leaving the vt::CastF32 pair at :313-314 that the sentence charges outside it. A range that stops just short of its own subject is worse than a missing citation, because it looks checked.

The reviewer then re-extracted every citation independently, confirmed the sweep missed none, resolved all eight corrections itself at the pins, verified the two transformers files are md5-identical between the venv and the clone so the numbering is not install-specific, and proved code identity by comment-stripping and hashing rather than by eye.

Two refutations from earlier rounds stand, both measured, both against instructions I gave:

  • positions[i] = i is a MARGINAL defect, not a severe one — RoPE rotations are orthogonal and q_m·k_n depends only on m−n, so a uniform shift is exactly a no-op in exact arithmetic; the 5.11e-05 residual is round-off. The in-tree "rotates every query by the wrong angle" wording was an overclaim.
  • My prescription to make rope_partial_full reachable by enlarging the fixture was wrong: measured signal-over-floor falls from 1.095 to 0.258, because bf16 accumulation noise grows at least as fast as the signal. Closed instead by exporting the host rope computation and comparing in f32 against the oracle's own rotary embedding at 1.267e-07 against a 1e-6 bound.

Also closed by narrowing rather than widening: the f32 KV cache on the prompt path became bf16 and the gate reproduced byte for byte — never wrong, just wider, which is exactly why nothing could see it, and precisely the too-WIDE dtype trap a token gate structurally cannot catch.

Closing; it reaches main through #437.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants