build: pin CI to 1.98.0 and write down why the tree cannot lint below it #114

Merged
jlxq0 merged 2 commits from clippy-floor into main 2026-08-25 07:25:52 +00:00
Owner

Cross-engine review (Codex) and a re-measurement after Clark disputed two numbers. Both produced corrections; both are in 8984d5d.

The numbers in the first version were the post-fix tree's, sitting next to a sentence about the pre-fix one. Re-measured, both trees, four toolchains, counted by clippy rather than by grep:

clippy main (before) this branch (after)
1.93.0 13 unknown lint 12
1.96.0 2 1
1.97.1 1
1.98.0 0 0
  • Thirteen attributes, not twelve.
  • 1.96.0 gives 2 on main, not 1. Clark measured that independently, and the #109 worker's gate report had shown exactly those two before either of us went looking.
  • duration_suboptimal_units is at 11 sites, not the 10 a grep finds: rate_limit.rs has one #[allow] line carrying two lints. Clippy counts suppressions, grep counts lines.

Added 1.97.1, because Codex was right that "lints only on clippy >= 1.98" claimed a range from two measurements. 1.97.1 still fails, so 1.98.0 is the first clean toolchain — and AGENTS.md now says first-clean rather than implying later ones stay clean, which no measurement can show and which is exactly why ci.yml names a version.

Codex's other two findings, and what I did with them:

  • The as_chunks::<2>() loop is semantically identical for every input, and size & !1 is now redundant. Confirmed — as_chunks splits the remainder off itself. Left in place; the test comment already records that two mechanisms enforce the same property and that neither alone is observable.
  • The new test's fixture is not "well-formed" — a RIFF chunk with an odd body needs a trailing pad byte and this one has none. It parses because parse_wav_mono_16bit returns from the data arm before padding matters. Reworded the .expect rather than the fixture; the endianness and sample-count assertions are unaffected.

Gates re-run: fmt, clippy -D warnings (0 errors), 209 tests green on 1.98.0. cargo check --all-features --locked green on 1.93.0.


Closes #112

What the issue had, and what was actually there

The issue named two attributes. There are twelve. clippy::duration_suboptimal_units appears at eleven sites across session.rs, mas.rs, channel.rs, key_backup_gate.rs, rate_limit.rs (×2), session_store.rs, setup.rs (×3) and mcp.rs, and it is unknown to clippy 1.93 as well. Same tree, three toolchains:

toolchain cargo clippy --all-targets --all-features --locked -- -D warnings
1.93.0 12 unknown lint errors — 11 duration_suboptimal_units, 1 unused_async_trait_impl
1.96.0 1 unknown lint error — unused_async_trait_impl
1.98.0 clean

So the floor is 1.98 and duration_suboptimal_units was never the binding constraint, but eleven of the twelve sites were undocumented.

One attribute was removable, and its comment was wrong

chunks_exact_to_as_chunks at the WAV decoder carried "clippy 1.98 suggests as_chunks::<2>(), which is newer than our declared MSRV". It is not newer. as_chunks::<2>() compiles on rustc 1.93.0 — probed with a standalone --edition 2024 file before the attribute was deleted. Taking the suggestion is also the better code: the array type carries the width, so from_le_bytes takes *c with no slice indexing.

The other eleven stay, and so does the twelfth

Duration::from_mins and Duration::from_days are still unstable on 1.98 (E0658: use of unstable library feature 'duration_constructors'), so clippy is suggesting something that does not compile. unused_async_trait_impl fires on code #[tool_handler] generates. Both are now written down in AGENTS.md as suppressions not to remove in order to lower the floor.

Why rust-version stays 1.93

The issue proposed raising it. I did not, because it would be false. cargo check --all-features --locked passes on rustc 1.93.0 — the tree builds there, which is what rust-version means, and the digest-pinned rust:1.93-bookworm builder in the Dockerfile does it on every release. Raising it to 1.98 would have forced a builder-image bump with a new digest for a reason that is about linting, not building.

Two floors, and they are different numbers. AGENTS.md now says both, with the measurements and the date.

Why pin CI rather than leave it on stable

stable is not a safety margin here. With RUSTFLAGS: -Dwarnings, a clippy release firing a new lint on untouched code reds whichever pull request happens to be open, and every #[allow] added to quiet one ratchets the floor up again — invisibly, because CI is always on the new side of it. That is how twelve of them accumulated. 1.98.0 is stated in ci.yml with a comment saying to bump it deliberately.

The rewrite had no test, which the negative control found

Swapping from_le_bytes for from_be_bytes — corrupting every decoded sample — left all 206 tests green. transcoder_produces_ogg_stream asserts the Ogg container's shape and nothing about its contents.

wav_samples_are_read_little_endian_and_a_trailing_odd_byte_is_not_a_sample reads the samples: seven data bytes chosen so little-endian and big-endian differ on all three. Controls run:

  • byte order flipped → red
  • size & !1 changed to sizegreen, because as_chunks discards the remainder too. Either mechanism alone can be deleted invisibly. The test's own comment says so rather than claiming a coverage it does not have.

Gates

fmt, clippy -D warnings (0 errors) and 207 tests green on 1.98.0. cargo check --all-features --locked green on 1.93.0, which is the claim rust-version makes.

Merge order

Touches the end of AGENTS.md, as does #113. Whichever lands second needs a one-hunk rebase.

Cross-engine review (Codex) and a re-measurement after Clark disputed two numbers. Both produced corrections; both are in `8984d5d`. **The numbers in the first version were the post-fix tree's, sitting next to a sentence about the pre-fix one.** Re-measured, both trees, four toolchains, counted by clippy rather than by grep: | clippy | `main` (before) | this branch (after) | |---|---|---| | 1.93.0 | 13 `unknown lint` | 12 | | 1.96.0 | 2 | 1 | | 1.97.1 | — | 1 | | 1.98.0 | 0 | 0 | - Thirteen attributes, not twelve. - `1.96.0` gives 2 on `main`, not 1. Clark measured that independently, and the #109 worker's gate report had shown exactly those two before either of us went looking. - `duration_suboptimal_units` is at **11** sites, not the 10 a grep finds: `rate_limit.rs` has one `#[allow]` line carrying two lints. Clippy counts suppressions, grep counts lines. **Added 1.97.1**, because Codex was right that "lints only on clippy >= 1.98" claimed a range from two measurements. 1.97.1 still fails, so 1.98.0 is the *first* clean toolchain — and `AGENTS.md` now says first-clean rather than implying later ones stay clean, which no measurement can show and which is exactly why `ci.yml` names a version. **Codex's other two findings, and what I did with them:** - *The `as_chunks::<2>()` loop is semantically identical for every input, and `size & !1` is now redundant.* Confirmed — `as_chunks` splits the remainder off itself. Left in place; the test comment already records that two mechanisms enforce the same property and that neither alone is observable. - *The new test's fixture is not "well-formed"* — a RIFF chunk with an odd body needs a trailing pad byte and this one has none. It parses because `parse_wav_mono_16bit` returns from the `data` arm before padding matters. Reworded the `.expect` rather than the fixture; the endianness and sample-count assertions are unaffected. Gates re-run: fmt, clippy `-D warnings` (0 errors), 209 tests green on 1.98.0. `cargo check --all-features --locked` green on 1.93.0. --- Closes #112 ## What the issue had, and what was actually there The issue named two attributes. There are twelve. `clippy::duration_suboptimal_units` appears at eleven sites across `session.rs`, `mas.rs`, `channel.rs`, `key_backup_gate.rs`, `rate_limit.rs` (×2), `session_store.rs`, `setup.rs` (×3) and `mcp.rs`, and it is unknown to clippy 1.93 as well. Same tree, three toolchains: | toolchain | `cargo clippy --all-targets --all-features --locked -- -D warnings` | |---|---| | 1.93.0 | 12 `unknown lint` errors — 11 `duration_suboptimal_units`, 1 `unused_async_trait_impl` | | 1.96.0 | 1 `unknown lint` error — `unused_async_trait_impl` | | 1.98.0 | clean | So the floor is 1.98 and `duration_suboptimal_units` was never the binding constraint, but eleven of the twelve sites were undocumented. ## One attribute was removable, and its comment was wrong `chunks_exact_to_as_chunks` at the WAV decoder carried "clippy 1.98 suggests `as_chunks::<2>()`, which is newer than our declared MSRV". It is not newer. `as_chunks::<2>()` compiles on rustc 1.93.0 — probed with a standalone `--edition 2024` file before the attribute was deleted. Taking the suggestion is also the better code: the array type carries the width, so `from_le_bytes` takes `*c` with no slice indexing. ## The other eleven stay, and so does the twelfth `Duration::from_mins` and `Duration::from_days` are **still unstable on 1.98** (`E0658: use of unstable library feature 'duration_constructors'`), so clippy is suggesting something that does not compile. `unused_async_trait_impl` fires on code `#[tool_handler]` generates. Both are now written down in `AGENTS.md` as suppressions not to remove in order to lower the floor. ## Why `rust-version` stays 1.93 The issue proposed raising it. I did not, because it would be false. `cargo check --all-features --locked` passes on rustc 1.93.0 — the tree builds there, which is what `rust-version` means, and the digest-pinned `rust:1.93-bookworm` builder in the Dockerfile does it on every release. Raising it to 1.98 would have forced a builder-image bump with a new digest for a reason that is about linting, not building. Two floors, and they are different numbers. `AGENTS.md` now says both, with the measurements and the date. ## Why pin CI rather than leave it on `stable` `stable` is not a safety margin here. With `RUSTFLAGS: -Dwarnings`, a clippy release firing a new lint on untouched code reds whichever pull request happens to be open, and every `#[allow]` added to quiet one ratchets the floor up again — invisibly, because CI is always on the new side of it. That is how twelve of them accumulated. `1.98.0` is stated in `ci.yml` with a comment saying to bump it deliberately. ## The rewrite had no test, which the negative control found Swapping `from_le_bytes` for `from_be_bytes` — corrupting every decoded sample — left all 206 tests green. `transcoder_produces_ogg_stream` asserts the Ogg container's shape and nothing about its contents. `wav_samples_are_read_little_endian_and_a_trailing_odd_byte_is_not_a_sample` reads the samples: seven data bytes chosen so little-endian and big-endian differ on all three. Controls run: - byte order flipped → **red** - `size & !1` changed to `size` → **green**, because `as_chunks` discards the remainder too. Either mechanism alone can be deleted invisibly. The test's own comment says so rather than claiming a coverage it does not have. ## Gates fmt, clippy `-D warnings` (0 errors) and 207 tests green on 1.98.0. `cargo check --all-features --locked` green on 1.93.0, which is the claim `rust-version` makes. ## Merge order Touches the end of `AGENTS.md`, as does #113. Whichever lands second needs a one-hunk rebase.
build: pin CI to 1.98.0 and write down why the tree cannot lint below it
Some checks failed
CI / docker (pull_request) Has been cancelled
CI / cargo (pull_request) Has been cancelled
21ad8e5a23
Twelve `#[allow]` attributes named clippy lints that did not exist yet, and
an `#[allow]` naming a lint the running clippy does not have is itself an
error under `-D warnings`. So the tree had a lint floor nobody had recorded,
CI sat above it on a moving `stable`, and a developer three months behind got
`unknown lint` errors on code they had not touched.

Measured, not inferred — same tree, three toolchains:

  1.93.0  12 unknown-lint errors   (11 duration_suboptimal_units, 1 other)
  1.96.0   1 unknown-lint error    (unused_async_trait_impl)
  1.98.0   clean

One of the twelve was avoidable and is gone. `chunks_exact_to_as_chunks` was
suppressed with the comment "clippy 1.98 suggests as_chunks::<2>(), which is
newer than our declared MSRV". It is not: `as_chunks::<2>()` compiles on
rustc 1.93.0, which I checked before deleting the attribute. Taking the
suggestion is also better code — the array type carries the width, so
`from_le_bytes` needs no slice indexing.

The other eleven stay, and taking their advice would not compile:
`Duration::from_mins` and `from_days` are still unstable on 1.98
(E0658, `duration_constructors`). The twelfth fires on code `#[tool_handler]`
generates. Neither is a suppression anyone should remove to lower the floor,
so AGENTS.md now says so.

`rust-version` stays 1.93 and the digest-pinned rust:1.93-bookworm builder
stays as it is. That number is about building, and `cargo check
--all-features --locked` passes on 1.93.0. Raising it to match a *lint* floor
would have forced a builder-image bump for no build reason.

CI moves from `toolchain: stable` to `1.98.0`. With RUSTFLAGS: -Dwarnings a
moving stable is not a safety margin: the next clippy release fires a new
lint on untouched code and reds whichever pull request is open, and each
`#[allow]` added to quiet it ratchets the floor up again, invisibly, because
CI is always on the new side of it.

The WAV rewrite needed a test and did not have one. Flipping every sample to
big-endian left all 206 tests green: `transcoder_produces_ogg_stream` asserts
the container's shape and nothing about its contents. Added one that reads
the samples, and confirmed it goes red under that flip. It does not observe
`size & !1` — `as_chunks` discards the remainder too, so either alone can be
deleted invisibly — and the test says so rather than claiming otherwise.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176noPigX2cwByDtRcafzyi
jlxq0 force-pushed clippy-floor from 21ad8e5a23
Some checks failed
CI / docker (pull_request) Has been cancelled
CI / cargo (pull_request) Has been cancelled
to fe7e9443aa
All checks were successful
CI / cargo (pull_request) Successful in 1m33s
CI / docker (pull_request) Has been skipped
2026-08-25 07:12:55 +00:00
Compare
docs: the lint-floor numbers were the post-fix tree's, labelled as the pre-fix one
All checks were successful
CI / cargo (pull_request) Successful in 1m16s
CI / docker (pull_request) Has been skipped
8984d5d070
Clark disputed two of them and was half right, which is what re-measuring is
for. Both trees, four toolchains, counted by clippy rather than by grep:

                  before this change   after
    clippy 1.93.0   13 unknown lint     12
    clippy 1.96.0    2                   1
    clippy 1.97.1    —                   1
    clippy 1.98.0    0                   0

- The tree carried thirteen such attributes, not twelve. I measured after
  removing `chunks_exact_to_as_chunks` and wrote the result down next to a
  sentence describing the tree before it.
- "1.96.0 gives 1" was the post-fix count. On `main` it gives 2, which is
  what Clark measured and what the #109 worker's gate report showed before
  either of us went looking.
- `duration_suboptimal_units` is at 11 sites, not the 10 a grep finds:
  `rate_limit.rs` has one `#[allow]` line carrying two lints. Clippy counts
  suppressions; grep counts lines.

Added 1.97.1, because "lints only on >= 1.98" claimed a range from two
measurements. 1.97.1 still fails, so 1.98.0 is the first clean toolchain —
and the file now says first-clean rather than implying every later one is,
which no measurement can show and which is the reason ci.yml names a version.

Also stopped the new WAV test calling its fixture well-formed. A RIFF chunk
with an odd body needs a trailing pad byte and this one has none; it parses
because `parse_wav_mono_16bit` returns from the `data` arm before padding
matters. The endianness and sample-count assertions are unaffected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176noPigX2cwByDtRcafzyi
jlxq0 merged commit 7525066a52 into main 2026-08-25 07:25:52 +00:00
Sign in to join this conversation.
No description provided.