feat(MODEL-MUSIC-MUSIC3): W2/W3 — the autoregressive half, and the token gate the spec promised does not exist (#672) - #744
Merged
Conversation
…ken gate the spec promised does not exist (#672) Phases W2 and W3 of .agents/specs/minimax-music3.md: the compute that consumes three of W1's six components. W3 is complete; W2 is partial and the split is exact. Nothing here generates a song — the DiT, scheduler, vocoder, ABI and server are W4-W6. THE FINDING THAT MATTERS, AND IT IS A CORRECTION Spec section 5 said the LLM half gets a token-exact gate: "Greedy decode of the code sequence is compared against the oracle token-for-token ... This is a real token gate and it binds." Measured against the artifact, it is not available, and the artifact wins. Upstream's autoregressive stage HAS NO GREEDY PATH. `_sample_top_k` (encoders.py:94-103) is the only sampler either stage uses, `_AR_SAMPLING_TOP_K` is a module constant of 50, there is no temperature and no argmax branch, and the last line is `torch.multinomial(probs, 1, generator=generator)`. The committed `rvq_codes.npy` is a SEEDED SAMPLE, so matching it token-for-token would be reproducing torch's CPU Mersenne-Twister, not this model. A second, independent reason the same conclusion holds and that would survive a bit-exact RNG: BOTH stages sample from a CFG mix of a conditional and an unconditional row (encoders.py:327-328, :134-135), and the goldens store the conditional row ONLY (:132, :343 both slice `[:1]`). The distribution the codes were drawn from is not reconstructible from what is committed. Section 5 is rewritten rather than quietly edited: the withdrawn paragraph is kept verbatim, because a withdrawn claim that leaves no trace is how the same wrong gate gets re-specified. WHAT LANDS * `ConditionMix` — the learned 8-layer mix. A SOFTMAX over the layer logits (not a normalize), ONE scalar `layer_scale` on the mixed result (not per layer), a k=3 p=1 Conv1d over TIME through the shared `vocoder1d` primitives (not a pointwise projection), then NEAREST interpolation onto the latent timeline at an INPUT-over-OUTPUT scale. Each of those four has a plausible wrong form that returns the right shape. * `DepthDecoderForward` + `DepthSequenceEmbeds` + `AudioHeadLogits` — the 4-layer RVQ depth decoder, its sequence assembly and its seven heads. Attention is causal with no RoPE and no KV cache, so ONE forward over the depth sequence equals upstream's incremental schedule step for step; that equivalence is verified bit-exactly against the committed goldens, not assumed. The 16-position window is exercised AT its boundary and one past it, where `pos_embedding` has no row and this port refuses by name. * `EmbedAudioFrame` / `FrameHiddenRow` — the frame feedback and the conditioning row. The two codebook offsets are written from different loop bases upstream (`(index-1)*` one-based in `_generate_depth_codes`, `j*` zero-based in `_embed_audio_frame`); reading one for the other shifts every codebook by 1024 rows and is called out where it bites. * W2's deterministic front half: `CleanCaption` / `NormalizeLyrics` / `AssembleArPrompt` (upstream's own comment is that whitespace-level changes change the generated audio, and the replacement ORDER inside `_normalize_lyrics` is load-bearing), `UnconditionalPromptIds`, `MaxArFrames` with both of upstream's refusals, `SemanticVocabMask`, `GuidedSemanticLogits` including the re-mask that keeps guidance on two -inf logits from producing a NaN candidate, and `TopKProbabilities` — `_sample_top_k` up to but not including its draw. DTYPE IS A PARAMETER, BECAUSE UPSTREAM'S IS The AR half runs bf16, and a bf16 torch module rounds at EVERY op boundary, so an fp32 host forward is a different computation rather than a more precise one. MEASURED: the fp32 forward left 448450 of 716800 values beyond one bf16 ULP at mean|d| 2.649e-03. `ArCompute` threads the store width through every function with an op boundary. This is AGENTS.md's too-wide rule in its awkward direction — nothing is numerically WRONG at fp32, no token gate could ever see it, and only a tensor gate against the real weights can. TWO GATES, AND A BOUND CALIBRATED AGAINST A MATCHED CONTROL `test_minimax_music3_ar` (25 cases / 338 assertions, no checkpoint) runs upstream's OWN classes at reduced dimensions in float32 via `scripts/gen-minimax-music3-ar-goldens.py`, so it separates an algebra defect from rounding. Worst deviation observed 4.8e-07 against a 1e-5 relative bound. `test_minimax_music3_ar_real` (4 cases / 894 assertions) drives the real bf16 weights on the oracle capture's own inputs: condition mix 175989 of 176128 values BIT-IDENTICAL, mean|d| 1.99e-07, ZERO beyond one bf16 ULP-or-2^-7 depth decoder 716800 values over 25 frames x 7 depth steps, 43.61% bit-identical, mean|d| 1.824e-03, max|d| 0.125 Zero violations is not achievable on the depth decoder and the reason is measured, not assumed. With the input layernorm bit-identical, torch's dispatched attention on identical bf16 q/k/v reproduces only 25736 of 32768 values (its bf16 Linear reproduces 32759 of 32768) — the CPU kernel runs a blocked online softmax, and four candidate rounding models were all worse than the plain form. So the bound is calibrated against torch AGAINST ITSELF: running upstream's own module under `sdpa_kernel(MATH)` on these exact inputs reproduces the goldens to 46.34% bit-identical at mean|d| 1.659e-03. Ours sits inside that spread. Chasing a particular kernel's rounding below the control is not "more correct". The bit-identical FRACTION is the discriminator with four orders of margin (an fp32 forward scores 0.0004%), and it is asserted ALONGSIDE absolute mean and max bounds rather than instead of them — a count alone bounds nothing, and Pearson would see none of it because it is scale-invariant. `npy.h` grows an OPT-IN `allow_fortran_order`: `condition_chunk0.npy` is stored column-major, and read as C-order it has the right value count and is the wrong tensor. The default still throws, so every existing caller is unchanged. RED BEFORE GREEN, AND MUTATIONS THAT MOVED Against a shape-correct stub: 25 cases, 2 passed, 23 failed; 336 assertions, 101 passed, 235 failed. With the implementation: 25/25 and 338/338. Mutations, each applied to the head, rebuilt, run, and reverted (tree restored byte-for-byte, sha256 verified): softmax -> plain normalize 3 cases / 3 assertions RED (reduced), 1 case / 3 RED (full scale) interpolate scale inverted 3 cases / 11 assertions RED codebook offset dropped 1 case / 1 assertion RED attention made non-causal 4 cases / 65 assertions RED frame row keeps depth position 0 1 case / 24 assertions RED One mutation is recorded because it did NOT move: mutating only the SCORE loop of the causal attention is a numerical no-op, since softmax is shift-invariant and the later loops still stop at i. That is a bad mutation, not a coverage hole, and it is noted in the mutation script so the next reviewer does not read it as one. STILL OWED ON W2 The 8.6B `Qwen3ForCausalLM` forward itself. `frame_hiddens[:, :4096]` is the language model's own hidden state; reproducing it means running that model teacher-forced on the golden codes through our landed Qwen3 dense path, which needs an `inputs_embeds` entry it does not have. Recorded in the spec's section 5 and its `## Now` rather than discovered later. Issue: #672 FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
Four record surfaces conflicted, each resolved by taking the target branch version WHOLESALE and reapplying this row's scoped edit, per AGENTS.md. The first attempt did not: a regex resolution of .agents/model-matrix.md silently dropped three unrelated dots3-note rows, and docs/FEATURES.md lost the Native Windows and Dots3Note rows the same way. Both were caught by diffing the unrelated keys against origin/main byte-for-byte, which is now recorded as the check that catches it. docs/USAGE.md is append-only in practice here — both lanes added a section — so main's is kept intact and this row's is appended after it. Gates re-run on the MERGED tree, because merge-clean is not merge-builds: test_minimax_music3_ar 25 cases / 338 assertions PASS test_minimax_music3_ar_real 4 cases / 894 assertions PASS (real checkpoint) test_minimax_music3_loader 21 cases / 1393 assertions PASS Row: `MODEL-MUSIC-minimax-music3-mini-max-music3-for-conditional-generation` Issue: #672 FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
# Conflicts: # docs/FEATURES.md # docs/USAGE.md
# Conflicts: # docs/FEATURES.md # docs/USAGE.md
# Conflicts: # docs/FEATURES.md
# Conflicts: # docs/FEATURES.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phases W2 and W3 of
.agents/specs/minimax-music3.md.W3 is complete; W2 is partial and the split is exact. Nothing here generates a
song — the DiT, scheduler, vocoder, ABI and server are W4–W6.
Issue: #672 · Row:
MODEL-MUSIC-minimax-music3-mini-max-music3-for-conditional-generationThe finding that matters, and it is a correction
Spec §5 said the LLM half gets a token-exact gate: "Greedy decode of the code
sequence is compared against the oracle token-for-token … This is a real token
gate and it binds." Measured against the artifact, it is not available.
Upstream's autoregressive stage has no greedy path.
_sample_top_k(
encoders.py:94-103) is the only sampler either stage uses,_AR_SAMPLING_TOP_Kis a module constant of 50, there is no temperature and no argmax branch, and the
last line is
torch.multinomial(probs, 1, generator=generator). The committedrvq_codes.npyis a seeded sample, so matching it token-for-token would bereproducing torch's CPU Mersenne-Twister, not this model.
A second, independent reason that would survive even a bit-exact RNG: both
stages sample from a CFG mix of a conditional and an unconditional row
(
encoders.py:327-328,:134-135), and the goldens store the conditional rowonly (
:132,:343, both slice[:1]). The distribution the codes weredrawn from is not reconstructible from what is committed.
§5 is rewritten rather than quietly edited — the withdrawn paragraph is kept
verbatim, because a withdrawn claim that leaves no trace is how the same wrong
gate gets re-specified. The codes are consumed as inputs, and the AR half is
gated on tensors.
What lands
ConditionMix— the learned 8-layer mix: a softmax over the layer logits(not a normalize), one scalar
layer_scaleon the mixed result (not perlayer), a k=3 p=1 Conv1d over time through the shared
vocoder1dprimitives(not a pointwise projection), then nearest interpolation at an
input-over-output scale. Each has a plausible wrong form that returns the
right shape.
DepthDecoderForward/DepthSequenceEmbeds/AudioHeadLogits— the4-layer RVQ depth decoder. Attention is causal with no RoPE and no KV cache, so
one forward over the depth sequence equals upstream's incremental schedule step
for step; that equivalence is verified bit-exactly, not assumed. The
16-position window is exercised at its boundary and one past it.
EmbedAudioFrame/FrameHiddenRow— the frame feedback and theconditioning row, with the two differently-based codebook offsets called out
where they bite.
passes, string for string; the replacement order inside
_normalize_lyricsis load-bearing), the unconditional CFG row, the frame budget and its two
refusals, the semantic vocabulary mask, the guided-logit pipeline including the
re-mask that keeps a NaN from becoming a candidate, and
_sample_top_kup tobut not including its draw.
Dtype is a parameter, because upstream's is
The AR half runs bf16, and a bf16 torch module rounds at every op boundary,
so an fp32 host forward is a different computation, not a more precise one.
Measured: the fp32 forward left 448 450 of 716 800 values beyond one bf16 ULP at
mean|d| 2.649e-03.
ArComputethreads the store width through every functionwith an op boundary. This is AGENTS.md's too-wide rule in its awkward direction —
nothing is numerically wrong at fp32, no token gate could ever see it, and only
a tensor gate against the real weights can.
Two gates
test_minimax_music3_ar— 25 cases / 338 assertions, no checkpoint. Goldenscome from executing upstream's own classes at reduced dimensions in float32
(
scripts/gen-minimax-music3-ar-goldens.py), so it separates an algebra defectfrom rounding. Worst deviation observed 4.8e-07 against a 1e-5 relative bound.
test_minimax_music3_ar_real— 4 cases / 894 assertions on the real bf16checkpoint, skipping loudly without it:
condition_chunk0.npyframe_hiddens[:, 4096:]The depth-decoder bound is calibrated against a matched control, not guessed.
With the input layernorm bit-identical, torch's dispatched attention on identical
bf16 q/k/v reproduces only 25 736 of 32 768 values (its bf16 Linear reproduces
32 759) — the CPU kernel runs a blocked online softmax, and four candidate
rounding models were all worse than the plain form. So the control is torch
against itself: upstream's own module under
sdpa_kernel(MATH)on these exactinputs reproduces the goldens to 46.34% bit-identical at mean|d| 1.659e-03.
Ours sits inside that spread.
The bit-identical fraction is the discriminator with four orders of margin
(an fp32 forward scores 0.0004%), and it is asserted alongside absolute mean and
max bounds rather than instead of them — a count alone bounds nothing, and Pearson
would see none of it because it is scale-invariant.
npy.hgrows an opt-inallow_fortran_order:condition_chunk0.npyisstored column-major, and read as C-order it has the right value count and is the
wrong tensor. The default still throws, so every existing caller is unchanged.
RED before GREEN, and mutations that moved
Against a shape-correct stub: 25 cases, 2 passed, 23 failed; 336 assertions,
101 passed, 235 failed. With the implementation: 25/25 and 338/338.
Each mutation was applied to the head, rebuilt, run, and reverted; the tree was
restored byte-for-byte and verified by sha256.
The last three are the point of having two gates: they are invisible at reduced
dimensions in fp32 and caught at full scale in bf16.
One mutation is recorded because it did not move: mutating only the score
loop of the causal attention is a numerical no-op, since softmax is
shift-invariant and the later loops still stop at
i. That is a bad mutation,not a coverage hole, and it is noted in the mutation script so the next reviewer
does not read it as one.
Still owed on W2
The 8.6B
Qwen3ForCausalLMforward itself.frame_hiddens[:, :4096]is thelanguage model's own hidden state; reproducing it means running that model
teacher-forced on the golden codes through our landed Qwen3 dense path, which
needs an
inputs_embedsentry it does not have. Recorded in §5 and in the spec's## Nowrather than discovered later.Gate + environment
scripts/agent-preflight.sh --no-require-roleis green excepttest_cpu_x86_llamacpp_floor, which fails withNO_QUIET_WINDOW after 30s (busy=109% load=98.35)— the environmental signature, on a box loaded by thislane's own builds, and unrelated to any path this PR touches
(#631).
audit-live-rows.py --checkalso exits 1, verified RED onorigin/mainin a clean worktree with the identical command, flagging thissame row as abandoned-ACTIVE because no commit on main named the full row ID;
this PR's commits now do.
Every measurement here was taken on CPU. Nothing in this PR touches the speed
axis, and SGLang-Omni remains
gateable = no.Note:
windows-msvc-cpu,windows-msvc-vulkanandsanitize-cpu (address,undefined)are red on main from other lanes (#645, #674) and will showhere; they are not this change.
🤖 Generated with Claude Code