Join the conversation

Join the community of Machine Learners and AI enthusiasts.

Sign Up
SeaWolf-AIΒ 
posted an update 2 days ago
Post
2640
πŸ” The attention mask stopped being an audit.

An autoregressive model must not let position t depend on anything after t. Everyone checks this by inspecting the causal mask β€” but hybrid stacks now mix attention with state-space scans, and a scan has no mask. Every mask can be correct while information leaks through scans, aggregations, or normalization.

βš™οΈ So we test the property directly. Two inputs identical except at the last position, two forward passes, compare each layer's prefix, report the first layer that moves. No training, no gradients, no accelerator β€” seconds on CPU.

πŸ“Š Across 192 injected faults on eight checkpoints, mask inspection detected 0. The per-layer audit localized 192/192 to the exact layer.

🎯 Then we read the source before running anything. In transformers 5.7.0, the reference chunked scan reduces the inter-chunk recurrence over the input chunk axis; zamba2 and nemotron_h reduce over the output chunk axis. One axis. The dynamic audit confirmed the prediction exactly: Zamba2-1.2B leaks from length 256, its declared chunk size, and Nemotron-H-8B from 128, its declared chunk size. Bamba, Falcon-H1, Granite-4.0-H, Mamba2 and RecurrentGemma came back clean.

⚠️ Scope: the defect is on the PyTorch chunked-scan path, which runs whenever the fused kernels are absent β€” CPU, CI, stock installs. We could not build those kernels, so the fast path is untested and open. That caveat cuts both ways: a model can pass every fused-kernel test and still leak the moment it runs without them.

πŸ§ͺ AX-RAY now carries this as its own axis. 39 models scored across causal, white-box and behavioral axes: 21 A, 3 B, 1 C, 14 F β€” with exactly 2 Causal-LEAK verdicts, the two the paper predicted. Badges separate a weights-level audit from an API-only one, so the two never get read as the same claim.

πŸ“„ https://arxiv.org/abs/2608.22876
πŸ”¬ FINAL-Bench/AX-RAY
πŸ€— The Mask Is Not the Model: Auditing Prefix Invariance in Attention, State-Space, and Hybrid Sequence Models (2608.22876)

Your source read reproduces exactly. The window it opens has already closed upstream.

I pulled the sdists and diffed the inter-chunk recurrence rather than trusting the
description. In 5.7.0 the four models you scored clean all do this:

decay_chunk = torch.exp(segment_sum(pad(A_cumsum[:, :, :, -1], (1, 0))))
decay_chunk = decay_chunk.transpose(1, 3)
new_states = (decay_chunk[..., None, None] * states[:, :, None, ...]).sum(dim=1)

zamba2 and nemotron_h skip the transpose and permute the states instead:

states_permuted = states.permute(0, 2, 1, 3, 4)
result = (decay_chunk[..., None, None] * states_permuted[:, :, None, ...]).sum(dim=2)

Same triangular decay matrix, the other index contracted. That is your one axis, and
you called it off the source before running anything.

It is version-bound, and the boundary is sharp

Every 5.x sdist from 5.7.0 up, grepped for states_permuted:

5.7.0   2026-04-28   divergent
5.8.0                divergent
5.10.0               divergent
5.12.0               divergent
5.13.0 / 5.13.1      divergent
5.14.0 / 5.14.1      divergent    2026-07-16
5.15.0  2026-08-10   reference
5.15.1  2026-08-19   reference

In 5.15.0 both files carry transpose(1, 3) and sum(dim=1). I normalized the 23
lines around the recurrence in all six models and diffed them pairwise: zero
difference. zamba2 and nemotron_h now run the identical code that mamba2, bamba,
falcon_h1 and granitemoehybrid ran when your audit returned them clean.

Repaired 16 days before your post.

Which is a problem for the board, not for the paper

results.jsonl is 39 rows and has no version field. Keys are model_id, dhs, grade,
causal_verdict, leaked, causal_score, xray, behavior, badges, categories, arch,
params_M, ts, created. Zyphra/Zamba2-1.2B and nvidia/Nemotron-H-8B-Base-8K both
sit at grade F, dhs 28.6, causal_score 15, μΈκ³Όμ•ˆμ „ flagged μœ„ν—˜.

So a reader on 5.15.1 sees an F on a model whose leak they cannot reproduce, and a
reader on 5.14.1 sees A grades that were measured under a library where four of the
six were never the ones at risk anyway.

Your own caveat says this from the other direction: a model can pass every
fused-kernel test and still leak without them. If that is true, the verdict is not a
property of the checkpoint. It is a property of (checkpoint, library version, kernel
availability), and only the first of the three is in the row.

What I did not do

Source diff only. No GPU here, so I did not re-run your dynamic audit against 5.15.1
and I could not build the fused kernels either. Your fast path stays open from my
side too.

Does the audit capture the transformers version it ran under? One field in
results.jsonl turns two F grades from a claim about Zyphra and NVIDIA into a claim
about a dependency range, which is the thing you actually measured.

Β·

Thanks for taking the time to check this. Let me put the facts and the measurements side by side.

The verdict you're looking at was measured on 2026-07. The timestamp is in the record itself (ts=1785031072 for Zamba2, 1785033374 for Nemotron-H). The fix you're referring to shipped in v5.15.0 on August 10, so our measurement predates it by 15 days. Every release available at that time carried the defect, so the verdict was accurate as of measurement. Publication slipped to August for IP reasons on our side; the measurement itself was not taken in August.

I also checked how far back it goes. The defect was present from v4.49.0 (2025-02-17), when Zamba2 support landed, through v5.14.1 (2026-07-16) β€” 88 releases across 539 days. The fix is 16 days old.

And yes, it is resolved now. We verified that ourselves rather than taking it on faith. Same checkpoint, same seeds, CPU fp32, no mamba_ssm / causal_conv1d (pure PyTorch path). Only the library changed:

T transformers 5.14.1 transformers 5.15.0
128 0.0 0.0
256 0.0 0.0
320 7.549e-03 LEAK 0.0 CLEAN
512 2.574e-03 LEAK 0.0 CLEAN
768 6.917e-03 LEAK 0.0 CLEAN

three seeds @ T=320
7.5e-3 / 1.2e-2 / 5.4e-2 0 / 0 / 0

Leakage opens exactly at the chunk boundary (256 β†’ 320), and on 5.15.0 all three seeds are exactly zero. We diffed the source as well: the offending permute form appears twice in 5.14.1 and zero times in 5.15.0, replaced by transpose(1, 3) + sum(dim=1). Nemotron-H-8B is likewise clean across the full range on 5.15.0.

For context, we independently identified this defect in July and filed a report and a patch upstream. It wasn't picked up, and the actual fix landed through a separate refactor. No claim to credit here β€” just noting this wasn't news to us.

How to present a verdict like this is something we're still refining. Our current thinking is that the answer isn't to retract the verdict but to scope it:

Zamba2-1.2B β€” LEAK on transformers ≀ 5.14.1 / CLEAN on β‰₯ 5.15.0

"Fixed upstream" and "safe in the stack you are actually running" are different statements. Pinned requirements and container images don't move on their own. Our own diagnostic server was still on 5.14.1 as of today, which is why the re-run reproduced the same leakage. Drop the verdict entirely and anyone still on an older pin is left with nothing.

Which is, I think, the more interesting part of this. Not a single weight changed, yet swapping one library flipped the verdict. A defect like this doesn't show up in a model card or in the weights β€” it's only visible when you measure the stack as it executes. And because this path is the one used in training, prefill and teacher-forced evaluation, it fails quietly.

We agree that causality is a property of (checkpoint, library version, kernel path) rather than of the checkpoint alone. We'll take your input into account as we refine version and kernel metadata in our records and the cadence for re-measuring against upstream changes.

Good discussion. Thanks.

Version scoping is the right call, and the interval needs a floor as well as a ceiling, per model.

LEAK on transformers <= 5.14.1 is open downward. For Nemotron-H that claims a leak on releases where the model does not exist. I checked the introduction points on the tags rather than inferring them:

model        first release carrying the file   defect there   window to 5.14.1
zamba2       4.49.0   2025-02-17               yes            63 releases / 513 days
nemotron_h   5.3.0    2026-03-04               yes            25 releases / 133 days

modeling_nemotron_h.py is 404 at v5.2.0 and 200 at v5.3.0. Both files ship the divergent form at their own first release, states_permuted present and sum(dim=2) reducing over the output axis, so the defect was never introduced into either model. It was inherited from the reference implementation on day one and carried forward by the copy.

Which also means 88 releases / 539 days is one global window applied to two models with very different exposure. Counting final releases on PyPI, which is what a pinned requirement actually resolves against, 4.49.0 through 5.14.1 is 63 releases across 513 days. Nemotron-H's real window is a quarter of that. Same defect, same fix, different blast radius, and the reader who needs to act on this is the one asking which of the two they are running.

The other edge is the one that will age. CLEAN on >= 5.15.0 is a forward claim, and the release train moved under it while we were talking:

5.15.0   2026-08-10 10:27Z
5.15.1   2026-08-19 11:28Z
5.16.0   2026-08-26 12:32Z
5.16.1   2026-08-26 14:48Z

I pulled the 5.16.0 and 5.16.1 sdists and checked all six hybrid mamba files in each. states_permuted 0, .transpose(1, 3) 1, the states[:, :, None, ...]).sum(dim=1) reduction 1, in zamba2, nemotron_h, mamba2, bamba, falcon_h1 and granitemoehybrid. The fix holds through 5.16.1, which was under an hour old when I ran it.

But that line only stays true because someone ran it. A row that reads CLEAN on >= 5.15.0 is asserting something about releases that do not exist yet, and the refactor that fixed this one is proof that this file moves for reasons nobody involved is tracking.

So the metadata field I would want is not the version the audit ran under. It is the version range the verdict has actually been tested across, with the right edge naming a specific release rather than an inequality.

What would it take to make the board re-run on a transformers release rather than on a calendar?

Β·

All three points hold. Let me correct our own numbers first.

The 88 releases / 539 days I quoted earlier came from counting GitHub tags, which isn't the right denominator β€” a pinned requirement resolves against PyPI final releases. Recounted on that basis it matches yours, and it splits per model:

model first release w/ file defect window exposure
zamba2 4.49.0 2025-02-17 4.49.0 – 5.14.1 63 releases / 513 days
nemotron_h 5.3.0 2026-03-04 5.3.0 – 5.14.1 25 releases / 133 days

Your point about the missing floor is right: without one, the row claims a leak on releases where Nemotron-H has no file at all. modeling_nemotron_h.py is 404 through v5.2.1 and appears at v5.3.0 already carrying the divergent form. Intervals should be per model, with both edges named.

Same view on the ceiling. >= 5.15.0 is a claim about releases that don't exist yet, and 5.16.0 and 5.16.1 landing today makes that concrete. We pulled both as well β€” six files, states_permuted 0, transpose(1, 3) 1, states[:, :, None, ...]).sum(dim=1) 1, in every one. So the field should be the range a verdict has actually been tested across, with a release number on the right edge rather than an inequality:

Zamba2-1.2B LEAK 4.49.0 – 5.14.1
CLEAN 5.15.0 – 5.16.1 (verified 2026-08-26)

On your last question β€” the unlock is separating the causality probe from the full audit. A complete diagnostic run is hours per model, which is why it can only ever be calendar-driven. The causality check on the pure-PyTorch path is minutes on CPU. Once it stands alone, the trigger can be the release itself: watch PyPI for a new final, install it into a probe environment, run the affected families, and either extend the right edge of verified_clean to that release number or flip the verdict and flag a regression. No calendar involved.

The six-family framing was useful. bamba and falcon_h1 aren't on our board yet, and the granite entries are there with the causality field empty. We're filling those in now, on 5.16.1.

Good discussion.

Don't re-run the granite rows. They are already measured, and your board is hiding it from you.

You said the granite entries are on the board with the causality field empty. That is what /api/leaderboard says. It is not what reports/ says.

I pulled all 39 report files and diffed each one against its leaderboard row. 39 rows, 39 reports, one to one.

reports/ibm-granite__granite-4.1-8b.json  .causal
  score 100 Β· verdict 인과 μ•ˆμ „ (CAUSAL-SAFE) Β· leaked false
  deterministic true Β· positive_control 3/3
  tol 1e-6 Β· noise_floor 0 Β· max_leak_delta 0
  T_profile  128 / 256 / 320 / 512 / 768
             first_leak_layer null at every T

granite-4.1-3b is the same: 3/3, leaked false, full T-profile. The run happened. It just does not reach the row.

Sixteen of the 39 rows drop eight fields together:

field            present on 16   present on 23
causal_verdict        0/16           23/23
leaked                0/16           23/23
causal_score          0/16           23/23
xray                  0/16           22/23
behavior              0/16           23/23
arch                  0/16           23/23
params_M              0/16           23/23
created               0/16           23/23

The arch-null set and the causal-null set are the same 16 model ids, exactly.

The one cell that is not 23/23 is a different thing, and worth separating. upstage/Solar-Open2-250B carries arch, causal_verdict and causal_score and has xray: null, with rank_status: api_audited_whitebox_pending. That null is correct. There is no white-box x-ray for an API-only audit. It is the contrast that makes the other sixteen legible: one row says "this was not measurable" in the schema, sixteen say nothing at all. categories, dhs, grade and badges survive on all 39, which is why nothing renders wrong: isCausalLeak falls back to the Causal-LEAK badge, and 0 of the 16 dropped rows is a leak. The display is fine. The API is not, and the API is what you read when you decided to re-run granite.

Measured at Space sha dbf37598, results.jsonl 33,892 B, sha256 33bba5702e1a. /api/leaderboard returns baked_overlay: true, count 39, so this is the live merged surface and not the fallback snapshot.

I can't see your bake path, so I'll ask instead of guess. Do those 16 rows come from a different writer than the other 23? Because if the drop is in the bake rather than in the run, re-running granite produces a second correct report and a third empty row.

Second thing, and this one is a correction to me rather than to you.

My six-family framing was wrong. Three of the six were never defective at any release.

I went back and checked the whole timeline instead of the two endpoints. 361 probes, every PyPI final from each family's first appearance through 5.16.1, counting states_permuted and the transpose(1, 3) / sum(dim=1) replacement in torch_forward.

defective, and when it ended
mamba2       4.44.0  2024-08-06  ..  4.47.1  2024-12-17    12 releases / 133 days
zamba2       4.49.0  2025-02-17  ..  5.14.1  2026-07-16    63 releases / 514 days
nemotron_h   5.3.0   2026-03-04  ..  5.14.1  2026-07-16    25 releases / 134 days

never defective, at any release
bamba              4.48.0  2025-01-10 .. 5.16.1    71 releases / 593 days clean
granitemoehybrid   4.52.0  2025-05-20 .. 5.16.1    58 releases / 463 days clean
falcon_h1          4.53.0  2025-06-26 .. 5.16.1    53 releases / 426 days clean

mamba2 is the row that reorders the story. It was fixed at 4.48.0, on 2025-01-10. Not at 5.15.0. Nineteen months earlier, 67 releases earlier, same torch_forward chunk-recurrence block, identical replacement:

4.47.1   states_permuted = states.permute(0, 2, 1, 3, 4)
         result = (decay_chunk[..., None, None] * states_permuted[:, :, None, ...]).sum(dim=2)

4.48.0   decay_chunk = decay_chunk.transpose(1, 3)
         new_states = (decay_chunk[..., None, None] * states[:, :, None, ...]).sum(dim=1)

bamba was introduced in that same release, 4.48.0, already carrying the corrected form.

Then zamba2 lands 4 releases later, on 2025-02-17, with the old form. And nemotron_h lands 42 releases and 418 days after the fix, still with the old form.

Which kills the sentence I wrote last round. I said the defect was "inherited from the reference implementation on day one and carried forward by the copy." The inheritance part is wrong. On zamba2's day one the reference implementation was already correct. This was not a bad upstream propagating. It was a copy taken from a snapshot that had already been superseded, twice, fourteen months apart.

Two method notes so you can check me rather than take it. The denominator is PyPI final releases, since that is what a pin resolves against. Two of them, 4.54.1 and 5.10.4, have no matching git tag and did not probe; both sit inside runs where the release on each side is identical, so neither can hide a transition.

And it lands on your trigger design. Watching PyPI for a new final and re-running the probe would not have caught any of this. Nothing about 4.49.0 or 5.3.0 is a library release in the sense your trigger means. The defect entered on a model add, twice, from a stale copy of a file that was already fixed in-tree.

So the event worth watching may not be the release at all. It may be the first commit of any new modeling_*.py containing a chunked state recurrence. Is a probe that fires on model-add cheaper for you than one that fires on release-add, or is that the harder one to wire?

Β·

Answering your question first: yes, and the drop is in the bake, not the run.

I opened all sixteen reports. Every one of them had already been measured β€” causal_score 100, leaked false, positive control 3/3, with the full T-profile. The one exception is Falcon3-10B-Instruct at 2/3, which we're looking at separately. The row writer was dropping eight fields on the way out.

The trap you flagged was real, and there were two faults rather than one. The Space overlay was replacing a backend row wholesale instead of merging it field by field, so a thin baked row could erase what the backend supplied. Fixing the writer alone would have been undone on the next bake. Both are now fixed: rows rebuilt from reports/, and the overlay changed so a null can no longer overwrite a value.

Measured after deploy:

              before     after

causal_verdict 16/39 0/39
leaked 16/39 0/39
causal_score 16/39 0/39
behavior 16/39 0/39
arch 16/39 0/39
params_M 16/39 0/39
created 16/39 0/39
xray 17/39 1/39 upstage/Solar-Open2-250B

The remaining cell stays as it is, for the reason you gave β€” there is no white-box x-ray for an API-only audit, and that null is the schema saying so. Preserved fields (dhs, grade, badges, categories, ts) are unchanged across all 39, and no row lost a value it previously had. Space sha ffed591171.

On the mamba2 timeline, we reached the same conclusion independently. We only checked two points per family β€” first release and 5.14.1 β€” and got 12/133, 63/514, 25/134. Walking every release is the better method, and the numbers agreeing is a useful cross-check for both of us. We also probed seven models behaviorally on 5.16.1; max_delta 0.000e+00 across the board, Nemotron-H included.

On the trigger: model-add is the cheaper one, but it isn't the same kind of check.

When a new modeling_*.py first lands there are usually no weights yet, so what's available at that moment is a structural read of the chunk-recurrence block β€” text-level, effectively free, and it fires on exactly the two events you named, 4.49.0 and 5.3.0. A behavioral probe needs a checkpoint, which puts it on the release side. So it isn't one or the other. Entry gets caught at model-add; proof happens at release. Two tiers, and the expensive one only ever confirms what the cheap one flagged.

Good catch. It saved us a run that would have produced nothing.

The bake is clean. I checked it rather than taking it, and then I went one field deeper and found the same bug still there.

Your eight columns verify exactly as you reported them, against my own pre-fix pull at 08:28Z:

field            before   after
causal_verdict    16/39    0/39
leaked            16/39    0/39
causal_score      16/39    0/39
behavior          16/39    0/39
arch              16/39    0/39
params_M          16/39    0/39
created           16/39    0/39
xray              17/39    1/39   upstage/Solar-Open2-250B

Preserved fields held too. dhs, grade, badges, categories, ts: zero rows lost a value, and zero rows changed one. And I diffed all 39 rebuilt rows against their own reports/ file rather than trusting the counts. 39 reports, 39 rows, one to one, and verdict/leaked/score/arch/params_M match on every one. The overlay guard reads right as well, if v is not None, so a thin row can no longer erase a fat one.

The ninth field is still being dropped, and it is the one that qualifies the other eight

causal.positive_control is in every report. It is in neither results.jsonl (16 keys) nor an API row (15 keys). Censused across all 39 reports:

positive_control    n
3/3                34
2/3                 3
0/3                 1
absent              1    upstage/Solar-Open2-250B, API-only

Four weakened controls, not one. You named Falcon3-10B-Instruct. These are the others:

model                                        pc     score  verdict          badges
nvidia/Nemotron-H-8B-Base-8K                 2/3     15.0  LEAK             Causal-LEAK
tiiuae/Falcon3-10B-Base-1.58bit-prequantized 2/3    100.0  CAUSAL-SAFE      Causal-Safe
tiiuae/Falcon3-10B-Instruct                  2/3    100.0  CAUSAL-SAFE      Causal-Safe, Instruction-Faithful
tiiuae/Falcon3-10B-Instruct-1.58bit          0/3     70.0  클린 (미검증 β€” μ–‘μ„±λŒ€μ‘° 약함)

Your schema already knows how to say this, and the threshold only trips at zero

The 0/3 row is handled honestly. 클린 (미검증 β€” μ–‘μ„±λŒ€μ‘° 약함) and a score of 70, which is the record saying out loud that the instrument was not proven on that model.

The two 2/3 clean rows get 100.0, 인과 μ•ˆμ „, and a Causal-Safe badge, with nothing marking them. So a control that missed one injected leak in three reads on the board exactly like a control that caught all three. The caveat exists and fires only on total failure.

The asymmetry is what makes this worth fixing rather than noting

A weak positive control cannot threaten a positive finding. Nemotron-H-8B-Base-8K is 2/3 and says LEAK. The instrument found a leak while running below full sensitivity, so the leak is real and 2/3 costs that row nothing.

It only threatens negatives. A detector with a measured 1-in-3 miss rate returning leaked: false has not shown the model is clean, it has shown the model is clean-or-missed. That is precisely the two Falcon3 rows carrying Causal-Safe, and they are the two rows with no caveat on them.

Same bug class you just closed, one field further down: the qualifier lives in reports/ and dies at the row boundary. Nobody reading the board can see that a Causal-Safe badge rests on a 2/3 instrument.

One thing I could not fault, and one I could not rule out

/api/model_report and /api/leaderboard have opposite precedence. The board is backend-first with baked overlaid on top, so a non-null baked value wins. The report is backend-first with baked only as a fallback, so the backend wins outright. I probed all three Falcon and Nemotron rows across both endpoints plus the git report and they agree on every field today, so this is not currently biting anything. But the two surfaces resolve a disagreement in opposite directions, and only one of them can be right when they ever do disagree.

Would you carry positive_control onto the row, or fold it into the verdict and let a 2/3 clean read as unverified the way 0/3 already does?