CI quality gates — what landed, what it measured, what is still red
Date: 2026-10-01. Toolchain for every local reading below: rustc /
rustfmt / clippy 1.98.1, cargo-deny 0.20.2, cargo-machete 0.9.2,
actionlint 1.7.12, LLVM 22.1.8. Commits: 1f9f4b0..6725563 on
main. Every number here was produced by the command printed next to it, or
read out of a CI run named by its id; none is estimated.
This is the findings file for six jobs added to .github/workflows/ci.yml. It
exists because two of them are RED ON LANDING and a red gate with no number
attached is the failure mode AGENTS.md §1.1 calls a defect — so the numbers are
here, and the fixes are named.
CLOSED IN PART, 2026-10-02 (wt/ci-final, rebased onto 1717ac3 — the
baseline numbers below were measured before 26 commits of landed lanes, so the
fresh counts were smaller):
unused-deps: closed (machete 10 → 0: eight deletions, two reasoned facade entries;continue-on-errordropped). §4.lint(clippy): closed (green under-D warnings; the three named exceptions carry#[allow]+ reason). §2.lint(fmt): closed except two files —cargo fmtapplied to all 52 files of the four crates excepttrain/src/lib.rs(129 hunks) andcore/src/routing.rs(8), both held out because lanes hold uncommitted edits in them;continue-on-errorstays until those land. §1.fused-library / ndarray: closed — the three burn-spectral fixtures moved toAutodiff<NdArray>and now assert instead of panicking in setup (TEST-AUDIT.md finding 2, CLOSED line); vendor formatted (280 hunks) and green under clippy-D warnings; the job’s KNOWN RED note removed.
The scoreboard
Section titled “The scoreboard”| job | first-run result | what the number is |
|---|---|---|
lint (fmt) |
red, soft | 629 hunks across 54 files, per-crate below |
lint (clippy) |
red, soft | 114 diagnostics at 27 sites, 18 clippy lints + 4 rustc lints |
supply-chain |
green | 0 advisories, 0 license failures, 0 source failures; 2 advisories FIXED to get there |
unused-deps |
red, soft | 10 findings in crates/, 4 of them facade false positives |
coverage |
green | 7003 / 8158 lines = 85.84 %; no threshold yet, by design |
docs (rustdoc) |
green | 57 s, exit 0, 7 missing documentation warnings, nothing denied |
workflow-lint |
green after one red | red on run 1 because of pyflakes; see §6 |
Both “soft” jobs carry continue-on-error: true and the count in the job
name, so a reader of the Actions page sees the debt without opening a log.
That is the ADR-0019 COUNTED mark: the degradation happens and something says
so out loud.
Two things that were wrong in the first version, found by reading the runs
Section titled “Two things that were wrong in the first version, found by reading the runs”Neither was visible locally and both are the class of defect this repo keeps paying for, so they are recorded here rather than only in the commits.
1. continue-on-error on a STEP reports the step as success. Run
36866436388: both lint steps were red in the log — the rustfmt step’s log holds
629 Diff in hunks — and the API returned conclusion: success for both. A
green tick beside a red gate is worse than no gate, because it is read as a
pass. Moved to the job, which is where cuda-compile already had it: the
steps keep their own failure and the run is not blocked. Verified on run
36868907213.
2. A failing step SKIPS every step after it. Run 36868694110, after the fix above made the rustfmt failure visible for the first time:
5 rustfmt -> failure6 clippy -> skippedSo half the job reported nothing. That is fused-library.yml’s failure mode
arriving by another route — its ndarray check+test job dies at step 6 for
exactly this reason — and a gate that does not run is indistinguishable from a
gate that passes. Fixed with if: ${{ !cancelled() }} on the clippy step, which
keeps the “still refuse to run on a cancelled job” property that always()
would have traded away. Verified on run 36868907213, where both steps report
failure.
1. cargo fmt — 629 hunks across 54 files
Section titled “1. cargo fmt — 629 hunks across 54 files”cargo fmt -p dormouse-core -p dormouse-data -p dormouse-train -p dormouse-cli -- --check| crate | hunks |
|---|---|
dormouse-core |
332 |
dormouse-train |
231 |
dormouse-data |
43 |
dormouse-cli |
23 |
| total | 629 |
629 is what the job runs and what its comment and job name say. 651 is the same
measurement over all six workspace members: the extra 22 hunks are in
backend-parity and cublas-poc, which this workflow’s cpu job does not
build either. Both numbers are here because a gate that quotes a figure it did
not measure is the wrong kind of wrong, and the first version of the job comment
quoted 651.
This is not a formatting disagreement, it is an unformatted tree. Measured:
there is no rustfmt.toml or .rustfmt.toml anywhere in the repo, and the
files are hand-wrapped at ~100 columns by hand. Two configs were tried and
neither closes it: max_width = 120 gives 566 hunks, use_small_heuristics = "Max" gives 488, both together 563. So this is 629 hunks of genuinely
rustfmt-nonconforming code, not one config choice.
One thing deliberately NOT in this number. cargo fmt --all walks local
path-based dependencies, so it also reaches 279 hunks inside
vendor/dormouse-fused — a separate workspace, separately gated by
fused-library.yml, and .editorconfig deliberately withdraws rustfmt’s
newline/whitespace rules under vendor/** so a diff against upstream stays
reviewable. Hence -p <four crates> in the job, and hence the vendor tree is
out of scope here.
To close it: one cargo fmt -p … commit and delete continue-on-error
from that step. That is the whole fix; it is mechanical and it belongs in its
own commit, not folded into a CI change.
2. cargo clippy — 114 diagnostics at 27 sites
Section titled “2. cargo clippy — 114 diagnostics at 27 sites”cargo clippy -p dormouse-core -p dormouse-data -p dormouse-train -p dormouse-cli --all-targets -- -D warningsEnumerated with --cap-lints warn (so the run completes and every diagnostic
is printed) and counted by (file, line). 47 distinct messages, 119 emitted
occurrences, 114 unique (file, line) sites — the difference is
--all-targets compiling each crate as both lib and lib-test, which reports
the same site twice. The job name says 114; that is the deduplicated count.
By lint, deduplicated:
| count | lint |
|---|---|
| 18 | clippy::manual_is_multiple_of |
| 15 | clippy::doc_lazy_continuation |
| 10 | unused_mut |
| 9 | clippy::field_reassign_with_default |
| 8 | clippy::let_unit_value |
| 5 | clippy::chunks_exact_to_as_chunks |
| 4 | clippy::useless_conversion |
| 4 | clippy::cloned_ref_to_slice_refs |
| 3 each | clippy::manual_range_contains, clippy::too_many_arguments, unused_variables |
| 2 each | clippy::needless_borrow, clippy::unnecessary_cast, clippy::items_after_test_module, clippy::empty_line_after_doc_comments, clippy::mem_replace_with_default, unused_imports |
| 1 each | clippy::ptr_arg, clippy::needless_range_loop, clippy::implicit_saturating_sub, clippy::double_ended_iterator_last, clippy::missing_const_for_thread_local, clippy::mistyped_literal_suffixes, clippy::bool_comparison, clippy::manual_contains, clippy::map_clone, clippy::redundant_closure, clippy::extra_unused_type_parameters, unused_parens, unused_assignments, dead_code, non_snake_case |
The three that are worth reading before fixing
Section titled “The three that are worth reading before fixing”clippy::mistyped_literal_suffixes — crates/dormouse-train/src/offload.rs:36.
This is a clippy error under -D warnings and it is the reason the job
cannot be strict. The literal is const LAYOUT_MAGIC: u32 = 0x4D_47_4E_32; —
clippy reads the 32 at the end of a hex literal as a mistyped i32 suffix
and wants 0x004D_474E_i32. The code is correct; the lint is a false positive
on a magic constant, so this one needs an #[allow] with a reason, not a
rewrite.
unused_assignments / unused_variables on nan_reads, tsct_bef,
tsct_aft — crates/dormouse-train/src/lib.rs:1118-1328. These are in the
stress-monitor path, and §3.3 of AGENTS.md records that StressMonitor state
is lost on resume. A value assigned and never read next to counters named
nan_reads is worth a human glance before an autofix deletes the assignment:
it may be the same defect wearing a lint’s clothes. Do not clippy --fix
this file blind.
clippy::non_snake_case — crates/dormouse-core/src/dspark_oracle.rs:296,
the_confidence_head_IS_markov_conditioned. Deliberate: the name asserts the
ADR-0020 claim the function exists to prove. Needs #[allow(non_snake_case)]
with that reason, not a rename.
Full list
Section titled “Full list”Sites marked ? are a continuation of the diagnostic on the line above (clippy
reports several lines under one lint name and the note carries no second
#[warn(...)]).
114 sites, by file (click to expand)
114 diagnostics at 27 sites.
crates/dormouse-cli/src/bin/generate.rs
| 51 | clippy::manual_range_contains | manual !RangeInclusive::contains implementation |
crates/dormouse-cli/src/bin/serve.rs
| 119 | clippy::manual_range_contains | manual !RangeInclusive::contains implementation |
crates/dormouse-core/src/aux.rs
| 161 | clippy::needless_range_loop | the loop variable i is used to index out |
| 181 | clippy::implicit_saturating_sub | implicitly performing saturating subtraction |
crates/dormouse-core/src/config/override.rs
| 26 | clippy::double_ended_iterator_last | called Iterator::last on a DoubleEndedIterator; this will needlessly iterate the entire iterator |
crates/dormouse-core/src/config/schema.rs
| 159 | clippy::doc_lazy_continuation | doc list item without indentation |
| 160 | ? | doc list item without indentation |
| 161 | ? | doc list item without indentation |
| 162 | ? | doc list item without indentation |
| 300 | clippy::items_after_test_module | items after a test module |
crates/dormouse-core/src/config/validation.rs
| 3 | unused_imports | unused import: super::schema::ActQuant |
| 108 | clippy::manual_is_multiple_of | manual implementation of .is_multiple_of() |
| 114 | ? | manual implementation of .is_multiple_of() |
| 174 | ? | manual implementation of .is_multiple_of() |
crates/dormouse-core/src/dspark_oracle.rs
| 101 | clippy::chunks_exact_to_as_chunks | using chunks_exact with a constant chunk size |
| 178 | ? | using chunks_exact with a constant chunk size |
| 296 | non_snake_case | function the_confidence_head_IS_markov_conditioned should have a snake case name |
| 482 | ? | using chunks_exact with a constant chunk size |
crates/dormouse-core/src/gr.rs
| 105 | ? | the loop variable i is used to index normed |
| 141 | ? | the loop variable i is used to index branches |
crates/dormouse-core/src/loop_block.rs
| 299 | clippy::useless_conversion | useless conversion to the same type: burn::Tensor<2> |
| 300 | ? | useless conversion to the same type: burn::Tensor<2> |
| 826 | ? | the loop variable n is used to index step_outs |
| 966 | clippy::field_reassign_with_default | field assignment outside of initializer for an instance created with Default::default() |
| 976 | unused_mut | variable does not need to be mutable |
| 977 | ? | variable does not need to be mutable |
| 1002 | ? | field assignment outside of initializer for an instance created with Default::default() |
| 1039 | ? | field assignment outside of initializer for an instance created with Default::default() |
| 1062 | ? | field assignment outside of initializer for an instance created with Default::default() |
| 1090 | ? | field assignment outside of initializer for an instance created with Default::default() |
| 1112 | ? | field assignment outside of initializer for an instance created with Default::default() |
| 1126 | ? | variable does not need to be mutable |
| 1133 | ? | variable does not need to be mutable |
| 1153 | ? | field assignment outside of initializer for an instance created with Default::default() |
| 1181 | ? | field assignment outside of initializer for an instance created with Default::default() |
| 2011 | ? | variable does not need to be mutable |
| 2178 | ? | field assignment outside of initializer for an instance created with Default::default() |
crates/dormouse-core/src/mixture_probe.rs
| 333 | clippy::cloned_ref_to_slice_refs | unnecessary use of clone to create a slice from a reference |
| 336 | ? | unnecessary use of clone to create a slice from a reference |
crates/dormouse-core/src/model.rs
| 52 | ? | doc list item without indentation |
| 53 | ? | doc list item without indentation |
| 273 | clippy::too_many_arguments | this function has too many arguments (8/7) |
crates/dormouse-core/src/moe.rs
| 222 | unused_parens | unnecessary parentheses around closure body |
| 533 | clippy::redundant_closure | redundant closure |
| 551 | unused_variables | unused variable: off_spread |
crates/dormouse-core/src/probe.rs
| 112 | clippy::missing_const_for_thread_local | initializer for thread_local value can be made const |
crates/dormouse-core/src/routing.rs
| 485 | ? | variable does not need to be mutable |
crates/dormouse-core/tests/model_seam.rs
| 376 | unused_variables | unused variable: fwd |
| 519 | clippy::map_clone | you are using an explicit closure for copying elements |
| 778 | clippy::manual_contains | using contains() instead of iter().any() is more efficient |
crates/dormouse-core/tests/moe_grad_seam.rs
| 29 | clippy::empty_line_after_doc_comments | empty line after doc comment |
| 89 | unused_mut | variable does not need to be mutable |
crates/dormouse-core/tests/moe_step1_gate.rs
| 25 | clippy::empty_line_after_doc_comments | empty line after doc comment |
crates/dormouse-core/tests/preset_exec.rs
| 572 | clippy::bool_comparison | equality checks against false can be replaced by a negation |
crates/dormouse-data/src/bin/filter.rs
| 378 | clippy::unnecessary_cast | casting to the same type is unnecessary (i32 -> i32) |
| 895 | clippy::too_many_arguments | this function has too many arguments (9/7) |
| 1454 | clippy::needless_borrow | this expression creates a reference which is immediately dereferenced by the compiler |
| 1525 | clippy::let_unit_value | this let-binding has unit value |
| 1527 | ? | this let-binding has unit value |
| 1551 | ? | this let-binding has unit value |
| 1553 | ? | this let-binding has unit value |
| 1570 | ? | this let-binding has unit value |
| 1573 | ? | this let-binding has unit value |
| 1603 | ? | this let-binding has unit value |
| 1605 | ? | this let-binding has unit value |
crates/dormouse-data/src/bin/shard.rs
| 51 | unused_mut | variable does not need to be mutable |
| 55 | clippy::needless_borrow | this expression creates a reference which is immediately dereferenced by the compiler |
| 85 | clippy::manual_is_multiple_of | manual implementation of .is_multiple_of() |
crates/dormouse-data/src/lib.rs
| 360 | clippy::ptr_arg | writing &mut Vec instead of &mut [_] involves a new object where a slice will do |
| 770 | unused_mut | variable does not need to be mutable |
| 807 | clippy::manual_range_contains | manual Range::contains implementation |
crates/dormouse-train/examples/export_divergence.rs
| 149 | clippy::ptr_arg | writing &PathBuf instead of &Path involves a new object where a slice will do |
crates/dormouse-train/src/jepa_targets.rs
| 104 | dead_code | method len is never used |
| 130 | clippy::chunks_exact_to_as_chunks | using chunks_exact with a constant chunk size |
crates/dormouse-train/src/lib.rs
| 478 | clippy::extra_unused_type_parameters | type parameter B goes unused in function definition |
| 565 | clippy::too_many_arguments | this function has too many arguments (8/7) |
| 884 | clippy::unnecessary_cast | casting to the same type is unnecessary (u64 -> u64) |
| 1118 | unused_assignments | value assigned to tsct_bef is never read |
| 1119 | ? | value assigned to tsct_aft is never read |
| 1136 | unused_variables | variable nan_reads is assigned to, but never used |
| 1149 | ? | manual implementation of .is_multiple_of() |
| 1158 | clippy::mem_replace_with_default | replacing a value of type T with T::default() |
| 1237 | ? | manual implementation of .is_multiple_of() |
| 1241 | ? | manual implementation of .is_multiple_of() |
| 1256 | ? | manual implementation of .is_multiple_of() |
| 1257 | ? | manual implementation of .is_multiple_of() |
| 1309 | ? | value assigned to nan_reads is never read |
| 1328 | ? | value assigned to nan_reads is never read |
| 1355 | ? | manual implementation of .is_multiple_of() |
| 1375 | ? | manual implementation of .is_multiple_of() |
| 1408 | ? | manual implementation of .is_multiple_of() |
| 1452 | ? | manual implementation of .is_multiple_of() |
| 1474 | ? | manual implementation of .is_multiple_of() |
| 1746 | ? | manual implementation of .is_multiple_of() |
| 1759 | ? | manual implementation of .is_multiple_of() |
| 1951 | ? | doc list item without indentation |
| 1952 | ? | doc list item without indentation |
| 1953 | ? | doc list item without indentation |
| 1954 | ? | doc list item without indentation |
| 1955 | ? | doc list item without indentation |
| 1956 | ? | doc list item without indentation |
| 1957 | ? | doc list item without indentation |
| 2248 | unused_mut | variable does not need to be mutable |
| 2270 | ? | useless conversion to the same type: burn::Tensor<2> |
crates/dormouse-train/src/offload.rs
| 36 | clippy::mistyped_literal_suffixes | mistyped literal suffix |
| 145 | clippy::manual_is_multiple_of | manual implementation of .is_multiple_of() |
| 158 | ? | method to_bytes is never used |
| 200 | ? | using chunks_exact with a constant chunk size |
| 262 | clippy::items_after_test_module | items after a test module |
| 393 | clippy::useless_conversion | useless conversion to the same type: burn::Tensor<2> |
crates/dormouse-train/src/optim.rs
| 553 | unused_imports | unused import: burn::optim::Optimizer |
crates/dormouse-train/src/stress.rs
| 78 | ? | manual implementation of .is_multiple_of() |
| 142 | clippy::doc_lazy_continuation | doc list item without indentation |
| 143 | ? | doc list item without indentation |
To close it
Section titled “To close it”cargo clippy --fixfor the mechanical 90-ish (everything except the three above).- Three
#[allow]with reasons, per the three notes. - Delete
continue-on-error.
3. cargo deny — green, and it found two real advisories
Section titled “3. cargo deny — green, and it found two real advisories”cargo deny --log-level warn --manifest-path ./Cargo.toml --all-features check| check | first run | after the two bumps below |
|---|---|---|
| advisories | FAILED (2) | ok |
| bans | ok (24 warnings) | ok |
| licenses | ok | ok |
| sources | ok | ok |
The two advisories, and the four lines that fixed them
Section titled “The two advisories, and the four lines that fixed them”| id | crate | what | how it got here |
|---|---|---|---|
| RUSTSEC-2026-0285 | rustls 0.23.43 |
accepted TLS 1.3 handshake messages sent at the wrong encryption level when they followed a key-changing message in the same record. The transcript is still authenticated, so it is not a MITM — but a peer could send in plaintext what should have been encrypted, without rustls rejecting the connection. | ureq → tracel-llvm-bundler → build-dependency of cubecl-llvm. Build-time only. |
| yanked | chacha20 0.10.1 |
yanked version, no patched release at the time | rand 0.10.2 → burn-backend. Transitive. |
Both are cargo update --precise moves — rustls 0.23.43 → 0.23.45,
chacha20 0.10.1 → 0.10.2 — and the whole diff is four lines of Cargo.lock.
Neither is a decision about our code, which is why the fix was made rather
than an ignore entry: deny.toml’s [advisories] ignore is still [], and
every entry added to it must carry a reason.
One methodological note worth keeping. cargo update -p X --precise Y on
this tree also re-resolves windows-sys and getrandom and drags five
unrelated crates along. The lock was instead edited directly (the two
(name, version, checksum) blocks) and then proved with
cargo metadata --locked --offline, which resolves without touching the
network. A lockfile is a resolved graph, not an opinion, and this is the way to
change two entries in it without rewriting nine.
The 24 bans warnings, warn-only with a reason
Section titled “The 24 bans warnings, warn-only with a reason”multiple-versions: 43 crates carry more than one version in the lock (cargo metadatacount, not cargo-deny’s). The notable clusters:hashbrown0.13/0.15/0.16/0.17,rand0.8/0.9/0.10,thiserror1/2,toml0.8/1.1,syn2/3,windows-sys0.52/0.61. Most are burn/cubecl’s own fan-out and are not ours to collapse. Warn-only until someone triages which are.wildcards: 22 findings, and every one is apath =dependency with noversionkey. cargo-deny reads that as a wildcard, andallow-wildcard-paths = truedoes not rescue it in a crate that could be published (it only applies topublish = falsecrates). Five crates here could be published —dormouse-cli,dormouse-core,dormouse-train, andburn-kda/burn-spectralin the vendored library. The closing recipe ispublish = falsein five[package]blocks, which is a statement about this repo’s release policy and lands in files other lanes are editing, so it is the owner’s call. Warn-only until then, with the recipe indeny.toml.
The license set is measured, not remembered
Section titled “The license set is measured, not remembered”16 allowed identifiers, chosen from cargo metadata --all-features over all
756 packages as a count of distinct SPDX operands: MIT 659 · Apache-2.0 527 ·
Unicode-3.0 25 · Zlib 23 · BSD-3-Clause 15 · Unlicense 11 ·
Apache-2.0 WITH LLVM-exception 8 · ISC 6 · BSD-2-Clause 5 · MPL-2.0 3 ·
CC0-1.0 3 · LGPL-2.1-or-later 2 · BSL-1.0 2 · CDLA-Permissive-2.0 2 · 0BSD 1 ·
NCSA 1. MPL-2.0 is weak-copyleft (file-level) and arrives only through
colored / option-ext / buildid — build-time or CLI formatting, never in a
shipped artefact.
One package, cfg_block 0.1.1, carries no license field at all. It is
reachable only on a non-linux target, so the x86_64-unknown-linux-gnu graph
does not contain it and the check passes. If a target is ever added that pulls
it in, cargo-deny fails it loudly — which is the point of naming it here.
4. cargo machete — 10 findings, 4 of them the tool’s blind spot
Section titled “4. cargo machete — 10 findings, 4 of them the tool’s blind spot”cargo machete crates/| crate | reported | real? |
|---|---|---|
dormouse-cli |
burn, dormouse-data, serde_json |
yes, all three |
dormouse-core |
burn-nn, burn-tensor, rand |
2 false, 1 real (rand) |
dormouse-train |
burn-tensor, rand |
1 false, 1 real (rand) |
cublas-poc |
burn-cuda |
false |
backend-parity |
burn-tensor |
false |
The false positives are one bug, four times. burn-tensor, burn-nn and
burn-cuda are declared as direct dependencies but reached through the burn
facade re-export: the source says use burn::tensor::Tensor,
use burn::nn::{Linear, LinearConfig}, use burn::tensor::{Device, …}, and
burn_tensor appears zero times in any of these crates. cargo-machete
greps for the crate name and cannot see through a re-export. The right fix is
[package.metadata.cargo-machete] ignored = [...] with that reason, not a
deletion — deleting them would be a real breakage.
The real ones. rand is declared in both dormouse-core and
dormouse-train and neither has a single rand:: in its sources. Worth noting
why that is not a bug hiding behind the lint: the --rand-depth arm lives in
crates/dormouse-train/src/cfg.rs and samples the loop depth from the step
index, not from a generator (§3.7). So the dependency is genuinely dead, not
the feature. dormouse-cli’s three are one-line deletions each: serde_json
and dormouse-data appear nowhere in its sources (dormouse_data appears only
inside a doc comment in tests/decode_wiring.rs), and burn likewise.
To close it: delete the three in dormouse-cli, delete rand from
dormouse-core and dormouse-train, add the four facade entries to
ignored, drop continue-on-error.
5. Coverage — a measurement, deliberately without a threshold
Section titled “5. Coverage — a measurement, deliberately without a threshold”cargo llvm-cov --lib -p dormouse-core -p dormouse-data -p dormouse-train \ --summary-only --lcov --output-path lcov.info7003 / 8158 lines = 85.84 %, from CI run 36866436388, commit
e90d69a, 2026-10-01, ubuntu-latest,cargo-llvm-cov 0.9.1. The lcov artifact from that run is the evidence; this section was read out of it.
Per file, from the artifact (28 files; the vendored library is excluded by cargo-llvm-cov’s default ignore rules, so this number is our crates):
| file | lines | covered |
|---|---|---|
dormouse-core/src/attention.rs |
25 | 100.00 % |
dormouse-core/src/aux.rs |
445 | 100.00 % |
dormouse-core/src/gr.rs |
204 | 99.02 % |
dormouse-core/src/moe.rs |
295 | 98.98 % |
dormouse-core/src/dspark_oracle.rs |
259 | 98.46 % |
dormouse-core/src/mixture_probe.rs |
271 | 98.52 % |
dormouse-core/src/future_byte.rs |
341 | 98.83 % |
dormouse-core/src/routing.rs |
241 | 98.76 % |
dormouse-core/src/config/schema.rs |
91 | 96.70 % |
dormouse-core/src/config/loader.rs |
106 | 95.28 % |
dormouse-core/src/act_quant.rs |
158 | 91.77 % |
dormouse-core/src/loop_block.rs |
1065 | 91.64 % |
dormouse-core/src/param.rs |
133 | 80.45 % |
dormouse-core/src/model.rs |
299 | 73.91 % |
dormouse-core/src/probe.rs |
18 | 72.22 % |
dormouse-core/src/config/validation.rs |
110 | 69.09 % |
dormouse-core/src/config/override.rs |
89 | 65.17 % |
dormouse-data/src/lib.rs |
655 | 90.84 % |
dormouse-train/src/lib.rs |
1729 | 85.19 % |
dormouse-train/src/stress.rs |
88 | 81.82 % |
dormouse-train/src/cfg.rs |
195 | 97.44 % |
dormouse-train/src/offload.rs |
275 | 97.45 % |
dormouse-train/src/optim.rs |
361 | 95.01 % |
dormouse-train/src/jepa_targets.rs |
90 | 88.89 % |
dormouse-train/src/decode.rs |
53 | 0.00 % |
dormouse-train/src/export.rs |
426 | 0.00 % |
The number is 85.84 % and the honest reading is “two files are untested”
Section titled “The number is 85.84 % and the honest reading is “two files are untested””479 lines — 5.87 % of the total — are decode.rs and export.rs at zero,
and both have integration tests that exercise them:
crates/dormouse-train/tests/decode_seam.rs and tests/export_roundtrip.rs.
They read as untested here only because this job runs --lib and those
are separate binaries. Without them the same number is 91.20 %.
So the design choice below — --lib not --workspace — is worth 5.36
percentage points on this reading, and it is worth naming which way: the
narrower command makes the number lower, so it is not the flattering choice.
Widening to the workspace would answer the other question, and would need its
own column in the log rather than a footnote.
The rest of the distribution is the real signal: eight files above 98 %, and the
gap concentrated in model.rs (73.91 %), param.rs (80.45 %), probe.rs
(72.22 %, 18 lines), config/validation.rs (69.09 %) and config/override.rs
(65.17 %). config/override.rs is the config -C key=value parser and
validation.rs is the refusal path — both are exactly the code AGENTS.md §1.1
depends on being right, and both are among the least covered files in the model
crate. That is the argument for a coverage gate, and it is an argument about
which files rather than about a floor.
No threshold is set, and here is the number to set one from. A defensible first floor from this reading is 85 %: it is 0.84 points below today’s figure, so ordinary work cannot break it, and it fails the moment either zero-coverage file gets worse. Anything higher starts encoding “do not grow this crate” rather than “do not lose this coverage”.
Why no threshold. A floor invented before the first reading is a floor nobody can satisfy or defend. The job’s whole deliverable is that the number exists, is in the log, and is in an artifact — so the next run makes “coverage fell” a diff rather than an opinion. Choosing the floor is the owner’s call and it needs the number first.
Two decisions that shape what the number means:
--lib, not--workspace. The seam tests undercrates/*/tests/are separate binaries. Including them would answer “what do the integration tests cover”, which is a different and also interesting question; this job answers “how much of the library doescargo test --libreach”.- The instrumented build is its own target directory
(
target/llvm-cov-target), so it never invalidates the plaintarget/the other jobs share. The cost is a cold dependency build every run — which is whytimeout-minutes: 120.
The log line and the artifact cannot disagree: the awk sums lcov’s LF:
(lines found) and LH: (lines hit) across every record of the same file that is
uploaded. Verified against a hand-built two-file lcov fixture (1 hit / 3 found
→ 33.33%) before it went in.
Not measurable on this box
Section titled “Not measurable on this box”The local instrumented build was attempted and aborted: /home was at 97%
(15 G free) and target/ alone is 127 G, so a second instrumented copy of the
dependency tree is a disk-fill, which AGENTS.md §2.5 records as a real failure
mode. The number below is from the CI runner, which is where the job actually
runs.
6. actionlint — and the finding about actionlint
Section titled “6. actionlint — and the finding about actionlint”actionlint # no arguments: lints .github/workflows/*.y*ml itselfWhy this job exists: ae0505d is the record. An unquoted colon inside a
name: scalar (“the facade: generated files in sync + every feature
combination”) made fused-library.yml a YAML parse error, so the whole
workflow stopped running — five jobs, including the 1000-case f64 oracle gate,
contributed nothing, and nothing said so. The fix was one pair of quote
characters, found by reading a log after the fact.
It earned its place inside one commit. The first draft of this very job
contained two unquoted colons (one in a step name:, one in an awk
program inside a run:). actionlint caught both before either was pushed. The
gate caught the failure mode it was added for, in the file that adds it.
Then the first CI run went red anyway, on pyflakes:
E: Package 'pyflakes' has no installation candidate.pyflakes is a PyPI package, not an apt one, and the step installed
shellcheck pyflakes in one command, so one wrong name took down the whole
job. Fixed in e90d69a (pipx install pyflakes, which is preinstalled on
ubuntu-latest). Worth recording how it failed: loudly, naming the cause and
the escape, within 7 seconds of job start. That is a gate working.
The finding: actionlint skips a missing external checker SILENTLY. Measured
on 1.7.12, on a workflow containing run: echo $HOME:
shellcheck on PATH |
actionlint output | exit code |
|---|---|---|
| yes | SC2086:info:1:6: Double quote to prevent globbing… |
1 |
| no | nothing at all | 0 |
Not a note, not a warning, not a different exit code. So a green actionlint run
cannot distinguish “every run: body is shellchecked” from “no run: body was
ever checked” — the ADR-0019 SILENT shape, living inside a linter. Hence the
extra step that proves shellcheck and pyflakes are on PATH in the same
step that runs actionlint, which is exactly how actionlint resolves them.
pyflakes is installed for shell: python bodies, of which this repo has none;
it is there so that adding one does not silently add zero checking.
Pinning. actionlint 1.7.12, tarball verified against that release’s
published SHA256 for linux_amd64
(8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8, the runner
image’s platform). No path filter: the file this job protects is the file that
decides what runs, so “only when the workflows changed” can be wrong in the
same commit that breaks them.
7. rustdoc — green, and the gap in the job that already existed
Section titled “7. rustdoc — green, and the gap in the job that already existed”cargo doc --no-deps -p dormouse-core -p dormouse-data -p dormouse-cli57 s, exit 0, 7 missing documentation warnings, nothing denied. The
warnings are deliberately not a gate: missing_docs is a style lint, and this
repo’s documentation lives in AGENTS.md and docs/, not in per-item rustdoc.
The build is not the point — the point is that
broken_intra_doc_links = "deny" and invalid_html_tags = "deny" already sit
in the root [workspace.lints.rustdoc] table with every member opting in via
[lints] workspace = true, and this is the job where a doc link that does not
resolve stops being a warning.
dormouse-train is not in this job. It is the heavy crate and it has an
active lane. Naming the omission is the point: a gate covering three of four
crates reads as a gate covering four.
The docs-site gate was NOT duplicated — and it has a hole
Section titled “The docs-site gate was NOT duplicated — and it has a hole”The brief asked for cd docs-site && npm ci && npm run ingest && npm run build && npm run check in ci.yml. That job already exists: build in
docs-site.yml runs exactly those four commands and fails on any dead internal
href. Measured locally on this box, 2026-10-01:
internal links checked: 24322broken internal links: 0anchor mismatches (warning): 0check: okmermaid: 3 rendered, 0 fence(s) left as code151 pages, 29 152 href= attributes total. A second copy in ci.yml would
build the same site twice on every push to main. Not added — the brief said
extend, not duplicate.
The hole, reported rather than fixed: that workflow’s paths: filter names
docs-site/**, docs/**, research/**, README.md, AGENTS.md,
CONTEXT.md, .bulba/**. A PR that touches only a crate’s doc comments —
which is exactly what the docs job above exists to check — runs no docs
gate at all, because the filter never fires. The fix is adding crates/** to
that filter. Not done here because docs-site.yml is a different lane’s file
and a push to it deploys to GitHub Pages.
What each job is NOT, in one list
Section titled “What each job is NOT, in one list”Read this as the boundary of the work, because a gate that is not checked for its own blind spots is the failure mode this file keeps finding:
- fmt and clippy do not see
vendor/dormouse-fused(279 hunks there) orbackend-parity/cublas-poc(22 hunks). Those arefused-library.yml’s and nobody’s. - cargo-deny does not see non-linux targets.
cfg_block 0.1.1, the one package in 756 with no license field, is excluded bytargets = ["x86_64-unknown-linux-gnu"]. Add a target, get a red gate. - cargo-machete cannot see through the
burnfacade, which is 4 of its 10 findings and the reasonignoredentries are needed rather than deletions. - coverage excludes
vendor/,tests/,examples/and the registry by cargo-llvm-cov’s defaults, and this job additionally runs--libonly — 5.36 points of the number, named in §5. - actionlint silently skips a missing checker, so the separate PATH step is part of the gate, not decoration (§6).
cargo deny’swildcardscheck counts apath =dep as a wildcard, which is why 22 of its warnings are about something other than wildcards (§3).- No job here runs a CUDA feature.
cuda-compile(existing) compiles them; nothing executes them, perfused-library.yml’s long note on why no GPU runner is registered.
Follow-ups, cheapest first
Section titled “Follow-ups, cheapest first”cargo fmt -p dormouse-core -p dormouse-data -p dormouse-train -p dormouse-cliin one commit, then dropcontinue-on-errorfrom the lint job. Closes 629 hunks.- Delete
randfromdormouse-coreanddormouse-train, andburn/serde_json/dormouse-datafromdormouse-cli; add the four facade re-exports to[package.metadata.cargo-machete] ignoredwith the reason. Closes all 10 machete findings. cargo clippy --fix, then three#[allow]-with-reason (offload.rs:36, dspark_oracle.rs:296, and thenan_readscluster in train/src/lib.rs — read that one first).publish = falsein five manifests, then flipbans.wildcardstodeny.- Set the coverage floor at 85 % (§5 gives the argument and the margin). One line: a
--fail-under-style check on the awk’s own number. - Add
crates/**todocs-site.yml’spaths:filter — one line, different lane’s file, deploys to Pages. - Add
dormouse-trainto thedocsjob when its lane lands. - Give
model.rsandconfig/override.rsa test — 73.91 % and 65.17 % on the two files a reviewer is most likely to change.