Skip to content

ci: run the Rust gates on stacked PRs, not only PRs targeting main - #319

Merged
coderdan merged 1 commit into
mainfrom
ci/run-rust-gates-on-stacked-prs
Sep 6, 2026
Merged

ci: run the Rust gates on stacked PRs, not only PRs targeting main#319
coderdan merged 1 commit into
mainfrom
ci/run-rust-gates-on-stacked-prs

Conversation

@coderdan

@coderdan coderdan commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

What

Drops the branches: [main] filter under pull_request in crap.yml, test.yml, mutants.yml and miri.yml, matching what go.yml already does. push: [main] and workflow_dispatch triggers are unchanged.

Why

Retargeting #318 onto non-empty-with to stack it on #314 turned every Rust gate off: only the Go bindings and wasm checks ran on the new commits, and the PR looked green while the CRAP gate had a failing function. The gates had to be triggered by hand with workflow_dispatch to see it.

mutants.yml diffs against github.base_ref, which on a stacked PR is the parent branch, so its scope stays this PR's changes rather than the whole stack. Cost is one extra run of each gate per stacked PR.

https://claude.ai/code/session_01QvrvBVdcecdr4LfywiGnDd

`crap`, `test`, `mutants` and `miri` filtered `pull_request` to
`branches: [main]`. Retargeting a PR onto its parent branch, the normal
move when stacking, silently switched every one of them off: #318 showed
only the Go bindings and wasm checks and looked green while the CRAP
gate had a failing function nobody could see. `go.yml` had already
dropped the filter for exactly this reason.

The filter goes on all four. `mutants` already diffs against
`github.base_ref`, which on a stacked PR is the parent branch, so its
scope stays "this PR's changes" rather than growing to the whole stack.
The `push: [main]` triggers are untouched.

Claude-Session: https://claude.ai/code/session_01QvrvBVdcecdr4LfywiGnDd
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

🧬 Mutation testing (cargo-mutants, --in-diff)

No mutants were generated for the changed lines.

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

✅ No CRAP threshold violations

572 function(s) analyzed · threshold 30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The workflow trigger changes are straightforward and correct, with only a minor follow-up doc/comment consistency issue noted.

Pull request overview

Updates GitHub Actions triggers so Rust “gate” workflows run on stacked PRs (where the PR base is a feature branch), not only when the PR targets main, preventing false-green PRs when retargeting during stacking.

Changes:

  • Removed the pull_request.branches: [main] filter from Rust gate workflows so they run for any PR base branch.
  • Added short inline rationale comments explaining the stacked-PR motivation and prior failure mode.
  • Left push (still main-only) and workflow_dispatch triggers unchanged.
File summaries
File Description
.github/workflows/test.yml Removes PR branch filter so the main Rust test/clippy/docs gate runs on stacked PRs; adds explanatory comment.
.github/workflows/crap.yml Removes PR branch filter so the CRAP gate runs on stacked PRs; adds explanatory comment.
.github/workflows/mutants.yml Removes PR branch filter so mutation testing gate runs on stacked PRs; adds explanatory comment.
.github/workflows/miri.yml Removes PR branch filter so the Miri (unsafe/UB) gate runs on stacked PRs; adds explanatory comment.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +6 to +8
# No branch filter: a stacked PR (a feature branch as base) must run this
# gate too. With `branches: [main]` here, retargeting a PR onto its
# parent branch silently switched every Rust gate off; see go.yml.
@coderdan
coderdan requested a review from auxesis September 6, 2026 14:56
@coderdan
coderdan merged commit a08ce94 into main Sep 6, 2026
7 checks passed
coderdan added a commit that referenced this pull request Sep 7, 2026
…P and mutants

First run of the Rust gates on this branch (it predates #319, so only
the Go workflow had ever run) found one CRAP violation and three
surviving mutants, all in `shuffle.rs`.

`schedule` scored 56: complexity 7 from one match arm per size, and 0%
coverage, because production only evaluates it inside `const` blocks
and llvm-cov cannot see compile-time execution. The bounds test now
calls it at runtime, and a `should_panic` test covers the unsupported
length arm. Same production code, 100% covered, CRAP 7.

`batcher_fill`'s inner guard `i + j + k < n` had a surviving `<=`
mutant: every shipped size is a power of two, where a partial final
merge block never occurs, so nothing exercised it. A zero-one-principle
test now builds the schedule at runtime for every n from 2 to 16 and
checks sorting and gate bounds; odd n reach the guard and the mutant
dies with an out-of-bounds gate.

The outer merge loop's bound has an equivalent mutant: one extra
iteration whose every inner step the guard rejects, unobservable by
construction. It is rewritten as `n > j + k` so its mutant name differs
from the inner guard's, and that one name is excluded in
.cargo/mutants.toml with the reasoning. The inner guard stays mutated.

Packing `(random << 8) | index` had an equivalent `^` mutant since the
low byte is zero after the shift. `+` produces the same word and has no
equivalent mutant.

Claude-Session: https://claude.ai/code/session_01QvrvBVdcecdr4LfywiGnDd
coderdan added a commit that referenced this pull request Sep 8, 2026
…P and mutants

First run of the Rust gates on this branch (it predates #319, so only
the Go workflow had ever run) found one CRAP violation and three
surviving mutants, all in `shuffle.rs`.

`schedule` scored 56: complexity 7 from one match arm per size, and 0%
coverage, because production only evaluates it inside `const` blocks
and llvm-cov cannot see compile-time execution. The bounds test now
calls it at runtime, and a `should_panic` test covers the unsupported
length arm. Same production code, 100% covered, CRAP 7.

`batcher_fill`'s inner guard `i + j + k < n` had a surviving `<=`
mutant: every shipped size is a power of two, where a partial final
merge block never occurs, so nothing exercised it. A zero-one-principle
test now builds the schedule at runtime for every n from 2 to 16 and
checks sorting and gate bounds; odd n reach the guard and the mutant
dies with an out-of-bounds gate.

The outer merge loop's bound has an equivalent mutant: one extra
iteration whose every inner step the guard rejects, unobservable by
construction. It is rewritten as `n > j + k` so its mutant name differs
from the inner guard's, and that one name is excluded in
.cargo/mutants.toml with the reasoning. The inner guard stays mutated.

Packing `(random << 8) | index` had an equivalent `^` mutant since the
low byte is zero after the shift. `+` produces the same word and has no
equivalent mutant.

Claude-Session: https://claude.ai/code/session_01QvrvBVdcecdr4LfywiGnDd
coderdan added a commit that referenced this pull request Sep 9, 2026
…P and mutants

First run of the Rust gates on this branch (it predates #319, so only
the Go workflow had ever run) found one CRAP violation and three
surviving mutants, all in `shuffle.rs`.

`schedule` scored 56: complexity 7 from one match arm per size, and 0%
coverage, because production only evaluates it inside `const` blocks
and llvm-cov cannot see compile-time execution. The bounds test now
calls it at runtime, and a `should_panic` test covers the unsupported
length arm. Same production code, 100% covered, CRAP 7.

`batcher_fill`'s inner guard `i + j + k < n` had a surviving `<=`
mutant: every shipped size is a power of two, where a partial final
merge block never occurs, so nothing exercised it. A zero-one-principle
test now builds the schedule at runtime for every n from 2 to 16 and
checks sorting and gate bounds; odd n reach the guard and the mutant
dies with an out-of-bounds gate.

The outer merge loop's bound has an equivalent mutant: one extra
iteration whose every inner step the guard rejects, unobservable by
construction. It is rewritten as `n > j + k` so its mutant name differs
from the inner guard's, and that one name is excluded in
.cargo/mutants.toml with the reasoning. The inner guard stays mutated.

Packing `(random << 8) | index` had an equivalent `^` mutant since the
low byte is zero after the shift. `+` produces the same word and has no
equivalent mutant.

Claude-Session: https://claude.ai/code/session_01QvrvBVdcecdr4LfywiGnDd
coderdan added a commit that referenced this pull request Sep 9, 2026
…P and mutants

First run of the Rust gates on this branch (it predates #319, so only
the Go workflow had ever run) found one CRAP violation and three
surviving mutants, all in `shuffle.rs`.

`schedule` scored 56: complexity 7 from one match arm per size, and 0%
coverage, because production only evaluates it inside `const` blocks
and llvm-cov cannot see compile-time execution. The bounds test now
calls it at runtime, and a `should_panic` test covers the unsupported
length arm. Same production code, 100% covered, CRAP 7.

`batcher_fill`'s inner guard `i + j + k < n` had a surviving `<=`
mutant: every shipped size is a power of two, where a partial final
merge block never occurs, so nothing exercised it. A zero-one-principle
test now builds the schedule at runtime for every n from 2 to 16 and
checks sorting and gate bounds; odd n reach the guard and the mutant
dies with an out-of-bounds gate.

The outer merge loop's bound has an equivalent mutant: one extra
iteration whose every inner step the guard rejects, unobservable by
construction. It is rewritten as `n > j + k` so its mutant name differs
from the inner guard's, and that one name is excluded in
.cargo/mutants.toml with the reasoning. The inner guard stays mutated.

Packing `(random << 8) | index` had an equivalent `^` mutant since the
low byte is zero after the shift. `+` produces the same word and has no
equivalent mutant.

Claude-Session: https://claude.ai/code/session_01QvrvBVdcecdr4LfywiGnDd
coderdan added a commit that referenced this pull request Sep 9, 2026
…P and mutants

First run of the Rust gates on this branch (it predates #319, so only
the Go workflow had ever run) found one CRAP violation and three
surviving mutants, all in `shuffle.rs`.

`schedule` scored 56: complexity 7 from one match arm per size, and 0%
coverage, because production only evaluates it inside `const` blocks
and llvm-cov cannot see compile-time execution. The bounds test now
calls it at runtime, and a `should_panic` test covers the unsupported
length arm. Same production code, 100% covered, CRAP 7.

`batcher_fill`'s inner guard `i + j + k < n` had a surviving `<=`
mutant: every shipped size is a power of two, where a partial final
merge block never occurs, so nothing exercised it. A zero-one-principle
test now builds the schedule at runtime for every n from 2 to 16 and
checks sorting and gate bounds; odd n reach the guard and the mutant
dies with an out-of-bounds gate.

The outer merge loop's bound has an equivalent mutant: one extra
iteration whose every inner step the guard rejects, unobservable by
construction. It is rewritten as `n > j + k` so its mutant name differs
from the inner guard's, and that one name is excluded in
.cargo/mutants.toml with the reasoning. The inner guard stays mutated.

Packing `(random << 8) | index` had an equivalent `^` mutant since the
low byte is zero after the shift. `+` produces the same word and has no
equivalent mutant.

Claude-Session: https://claude.ai/code/session_01QvrvBVdcecdr4LfywiGnDd
coderdan added a commit that referenced this pull request Sep 9, 2026
…P and mutants

First run of the Rust gates on this branch (it predates #319, so only
the Go workflow had ever run) found one CRAP violation and three
surviving mutants, all in `shuffle.rs`.

`schedule` scored 56: complexity 7 from one match arm per size, and 0%
coverage, because production only evaluates it inside `const` blocks
and llvm-cov cannot see compile-time execution. The bounds test now
calls it at runtime, and a `should_panic` test covers the unsupported
length arm. Same production code, 100% covered, CRAP 7.

`batcher_fill`'s inner guard `i + j + k < n` had a surviving `<=`
mutant: every shipped size is a power of two, where a partial final
merge block never occurs, so nothing exercised it. A zero-one-principle
test now builds the schedule at runtime for every n from 2 to 16 and
checks sorting and gate bounds; odd n reach the guard and the mutant
dies with an out-of-bounds gate.

The outer merge loop's bound has an equivalent mutant: one extra
iteration whose every inner step the guard rejects, unobservable by
construction. It is rewritten as `n > j + k` so its mutant name differs
from the inner guard's, and that one name is excluded in
.cargo/mutants.toml with the reasoning. The inner guard stays mutated.

Packing `(random << 8) | index` had an equivalent `^` mutant since the
low byte is zero after the shift. `+` produces the same word and has no
equivalent mutant.

Claude-Session: https://claude.ai/code/session_01QvrvBVdcecdr4LfywiGnDd
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.

3 participants