Skip to content

fix(output): pluralize the count nouns in collect/run output - #82

Merged
max-sixty merged 3 commits into
mainfrom
fix/collect-binary-plural
Sep 16, 2026
Merged

max-sixty merged 3 commits into
mainfrom
fix/collect-binary-plural

Conversation

@cargo-affected-bot

@cargo-affected-bot cargo-affected-bot commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

cargo affected collect printed exporting coverage maps for 1 binaryy... on any single-binary project — the plural was interpolated as a suffix (binary + y/ies), but the binary/binaries split lands mid-word, so the singular branch appended a stray y.

Tonight's rolling survey of tests/functional/diff_collect.rs turned up the same bug class in six more messages, so this PR now covers all of them rather than leaving the output half-fixed.

The wider defect

format_summary hard-coded tests, so the tool's single most-printed line read 1 tests to run whenever a selection came down to one test — which is the ordinary outcome of the workflow this tool exists for. It shows up verbatim in the repro transcript on #94:

cargo-affected: cache=hit-with-divergence selection=1/2 (50%) max_commits_ahead=1
1 tests to run (1 affected + 0 config + 0 new + 0 stranded, 1 skipped of 2 reachable-known):

Five more sites had it, including the found N tests across M binaries line whose binaries half this PR already fixed:

Site Before at n=1
selection.rs format_summary 1 tests to run / 1 tests would run / 1 tests to recollect
collect.rs listing summary found 1 tests across 1 binary
collect.rs update path updating coverage for 1 tests (1 ranges)...
collect.rs store path storing coverage for 1 tests (1 ranges)...
collect.rs done line done. 1 tests, 1 ranges stored in ...
run.rs nextest handoff running 1 tests with nextest

The pruned N test{s} and evicted N fingerprint{s} messages already pluralized correctly, which is what made the rest read as an oversight rather than a house style.

Changes

collect::plural_s(n) -> &'static str is the s in {n} test{s}, and every suffix-pluralized count in the codebase now routes through it — the six sites above plus the seven that already carried their own inline copy of the same conditional (pruned, evicted, the skipped test{s} warning and the completed test{s} bail in collect.rs, the missing-sha notice in selection.rs, the stale rows line in status.rs, and the leftover-staging-dir line in main.rs). binaries_phrase stays a separate function because its split is mid-word; its doc comment now points at plural_s for the contrast. The parenthesized commit(s) in collect.rs/run.rs/status.rs is a third convention and is deliberately left alone — it's already grammatical, and converting it would churn output this PR has no other reason to touch.

The regression test is selection::tests::summary_singular_test_count. Per tests/CLAUDE.md it was seen failing against the pre-fix expression:

thread 'selection::tests::summary_singular_test_count' panicked at src/selection.rs:620:9:
expected a singular noun for a one-test selection, got:
1 tests to run (1 affected + 0 config + 0 new + 0 stranded, 4 skipped of 5 reachable-known) — pass -v to list

The two pre-existing format_summary unit tests assert n=3 and n=4, so they pin the plural branch unchanged. Four functional assertions that pinned the ungrammatical form (tests/functional/diff_collect.rs ×3, tests/functional/run.rs ×1) are updated to the corrected wording.

Notes

cargo test --bins — 114 passed. cargo clippy --all-targets and cargo fmt --check clean. The functional suite needs cargo-nextest and llvm-tools, neither of which is installed in the agent sandbox (that's what #96 addresses), so those four updated assertions are verified by CI here rather than locally.

This conflicts with #75 at one line — tests/functional/diff_collect.rs:302, where that PR tightens the same assertion to include the category breakdown. Whichever lands second needs 1 tests to run (1 affected1 test to run (1 affected. Nothing else overlaps; #49, #81, #92 and #41 touch disjoint hunks.

Folded into this PR rather than opened as a new one, since it's the same concern in the same output surface and the queue is long enough already.

The map-export line interpolated the plural suffix after the whole noun,
but binary/binaries splits mid-word — so 1 rendered "binaryy" and 2
rendered "binaryies". The adjacent listing line hardcoded "binaries"
and read "1 binaries". Both go through one helper that owns the split.
format_summary hard-coded "tests", so the tool's most-printed line read
"1 tests to run". Same shape in five more count messages, including the
"found N tests across M binaries" line this PR already half-fixed.

Adds collect::plural_s and routes every suffix-pluralized count through it,
replacing the two inline copies of the same conditional.
@cargo-affected-bot cargo-affected-bot changed the title fix(collect): say "1 binary", not "1 binaryy" fix(output): pluralize the count nouns in collect/run output Aug 31, 2026

@cargo-affected-bot cargo-affected-bot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two suffix-pluralization sites still carry their own inline if n == 1 { "" } else { "s" } and don't route through plural_s, so the sweep the description calls complete ("every suffix-pluralized count now routes through it") is two sites short:

  • src/collect.rs, in the block guarded by if skipped > 0let s = if skipped == 1 { "" } else { "s" }; eprintln!("{skipped} test{s} produced no coverage");. This is the same shape as the pruned and evicted copies the PR did convert, and it sits ~20 lines above the updating coverage for … hunk.
  • src/status.rs, in the reach.missing block — the stale rows: {stale_rows} (anchored at missing sha{}) line ends with if reach.missing.len() == 1 { "" } else { "s" } as its last format arg.

Both are already grammatically correct today, so this is consolidation rather than a bug — but leaving two copies of the predicate behind is exactly the drift that let binary + y ship in the first place. Happy to push a commit routing both through plural_s if you'd rather have the sweep land whole; both are outside this diff's hunks so there's no inline suggestion to apply.

Separately, note: {} commit(s) since collect (src/run.rs, src/status.rs, src/collect.rs) uses a third convention — the parenthesized (s) — for the same problem. Not worth folding into this PR, but it means "every count noun is consistent now" isn't quite true yet either.

The plural_s / binaries_phrase split itself is right, and the doc comments pointing at each other are what make the mid-word case hard to re-break. summary_singular_test_count pins the site that mattered, and the pre-existing n=3/n=4 assertions keep the plural branch honest. CI is green on f8ba8c1 across all three platforms, so the four updated functional assertions check out.

@cargo-affected-bot

Copy link
Copy Markdown
Collaborator Author

Pushed in b522b4e — and the sweep was three sites shorter than the review counted, not two. Beyond collect.rs's skipped test{s} warning and status.rs's stale rows line, the same inline predicate was still live in three more places:

  • src/collect.rs — the refusing to overwrite stored coverage bail, ~15 lines below the skipped one, spelling the conditional inline as a format arg rather than binding it to let s.
  • src/selection.rs missing_shas_noticelet plural = if missing.len() == 1 { … }, feeding the collect_sha{plural} in the divergence notice. This one is the upstream of the status.rs site the review found: status.rs prints the notice and then appends its own separately-computed sha{} suffix, so the two copies were computing the same predicate over the same reach.missing a line apart.
  • src/main.rs cleanremoved {swept} leftover staging dir{s}.

All five now call plural_s, so grep -rn 'else { "s" }' src/ is empty and the function is the only place the predicate is written. Rendered output is byte-identical at every site — plural_s is the same conditional — so no test expectations move; cargo test --bins is 114 passed, cargo clippy --all-targets and cargo fmt --check clean.

Agreed on leaving commit(s) alone. It's grammatical as written, and converting it would rewrite three more output lines for style in a PR whose reason to touch output is a bug. The PR description now says which convention lives where instead of claiming the codebase is uniform.

Why the three extra sites didn't match the review's grep shape

The review found the two that bind the predicate to a let s on its own line. The other three vary the surface: selection.rs binds to let plural, collect.rs's bail passes the conditional straight in as a positional arg to bail!, and main.rs sits in a subcommand body rather than the collect/status output paths the sweep walked. Same predicate, three spellings — which is the drift argument the review made, one layer down.

@max-sixty
max-sixty merged commit 53aefb8 into main Sep 16, 2026
6 checks passed
@max-sixty
max-sixty deleted the fix/collect-binary-plural branch September 16, 2026 17:39
@max-sixty max-sixty mentioned this pull request Sep 18, 2026
max-sixty added a commit that referenced this pull request Sep 18, 2026
Three fixes to `run` and to the output, with no new CLI surface, no report-JSON
schema change, and no change to the grep-able `cargo-affected:` summary line —
so a patch.

Deleting a test no longer makes `run` exit 4 (#81). The deletion's diff hunk
covers exactly the lines of the deleted test's own stored coverage range, so the
test selects itself, and nextest is then handed a filterset matching nothing.
`run` now restricts the filterset to tests still in the listing and exits 0 with
a note when that leaves nothing; `status` predicts the same.

A change committed since the last collect that nothing covers now reports as
uncovered (#92). The empty-selection message keyed off the working tree while
selection diffs against each reachable `collect_sha`, so the change read as "no
uncommitted changes ... nothing to run" directly under the "N commit(s) since
collect" notice.

Count nouns in `collect` and `run` output agree with their numbers (#82): "1
binary" rather than "1 binaryy", "1 test to run" rather than "1 tests to run".

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
max-sixty added a commit that referenced this pull request Sep 18, 2026
…sing (#113)

* chore: fix the typos false positive in collect.rs, and document releasing

Two maintenance items found while cutting 0.4.1.

`pre-commit run --all-files` rewrote collect.rs's `format!("{n} binar{}", …)`
to `binary{}`, reintroducing the "1 binaryy" output bug #82 had just fixed. The
binary/binaries plural splits mid-word, so the bare stem is deliberate, and
typos can't tell that from a misspelling. `.typos.toml` now carries it as an
extend-word. Nothing else would have caught the rewrite: no CI job runs typos,
though CLAUDE.md claimed one did. Corrected alongside.

CLAUDE.md also gains a Releasing section. The process — bump the version, open
a `chore: release X.Y.Z` PR, tag the squash commit, and let the tag push
trigger release.yaml's Trusted-Publishing `cargo publish` — lived only in git
history and that workflow's header comment, so cutting 0.4.1 meant
reconstructing it from the previous release commits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(output): build "binary"/"binaries" from whole words, not a split stem

Review feedback: the `.typos.toml` entry this branch added was allowlisting a
token the code had no reason to produce. `binaries_phrase` interpolated the
plural's tail after a bare `binar` stem, which is what typos flagged and what
`--write-changes` then rewrote into "1 binaryy". Selecting the whole noun
produces byte-identical output — the existing unit test pins all three cases —
and leaves nothing for typos to flag, so the allowlist entry goes.

The CLAUDE.md sentence drops its claim about CI parity rather than restating
it. #97 is open and adds `pre-commit run --all-files` to `ci.yaml`'s lint job,
which would make any claim about what CI does or doesn't cover stale on
whichever of the two merges second. Describing what the hook set runs is true
either way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants