Skip to content

test(ci): guard the Rust toolchain pin as an exact contract — Refs #503 - #506

Merged
pawellisowski merged 5 commits into
mainfrom
routine/issue-503-toolchain-pin-guard
Sep 9, 2026
Merged

test(ci): guard the Rust toolchain pin as an exact contract — Refs #503#506
pawellisowski merged 5 commits into
mainfrom
routine/issue-503-toolchain-pin-guard

Conversation

@pawellisowski

Copy link
Copy Markdown
Contributor

Summary

  • Restores a guard over cli/rust-toolchain.toml — nothing has watched the pin since PR ci: pin every Rust build from one reviewed setup #490's guard was withdrawn — in the shape that finally held there: the pin reader is an exact contract, compared verbatim, with no shell parsed anywhere.
  • Closes the one bypass still open when that branch was narrowed: Swatinem/rust-cache runs cargo metadata with no run: script, so above the installer it built a cache key on the runner's default compiler, invisibly.
  • Makes "known Cargo-running actions" a claim that cannot rot: every uses: in every workflow must be classified as the installer, Cargo-running, or Cargo-inert, and an unclassified one fails by name asking for the decision.

Refs #503.

Type of change

  • Other (specify): CI guard (cli/tests/toolchain_pin_gate.rs) + the two workflow lines its contract requires

Decalog check

  • This change respects all five decalog truths.

Why this shape

The withdrawn guard modelled workflow semantics with affix tests over shell text and then a hand-rolled tokeniser. It absorbed 22 findings across 14 rounds and was still, at the end, both too loose and too tight — channel=stable after a valid assignment walked through it while an idiomatic printf 'channel=%s\n' publish was rejected. Both are one defect: re-implementing POSIX shell inside a test file. So this guard does not try. The reader is six lines and appears once per Cargo job; changing it means changing PIN_RUN in the same commit, in a diff a reviewer can read.

Against #503's acceptance list, each verified by planting the mutation into the real workflows, running the suite, and restoring the tree:

Acceptance Planted against Result
floating installer ci.yml gates: @master@stable rejected
literal alternate version ci.yml gates: toolchain: 1.88.0 rejected
restated pin read channel=stable in the reader rejected
late installer bridge-windows-packaged: install moved below cargo build rejected
producer after consumer pin reader moved below the step reading its output rejected
known Cargo-running action before installation Swatinem/rust-cache moved above Install Rust rejected, naming the step and its uses:
RUSTUP_TOOLCHAIN overrides workflow scope, job scope, lowercase rustup_toolchain, container.env rejected in all four
rustup / cargo +toolchain selectors rustup override set stable in a step rejected
tracked nested toolchain file rust-toolchain added and git added under steel-detailer-lookup, agreeing with the pin rejected
ignored/untracked worktrees .claude/worktrees/probe/cli/rust-toolchain.toml (confirmed git-ignored) accepted, correctly
unclassified action codecov/codecov-action@v4 added to gates rejected, naming the action and where to classify it

The last row is the part that keeps the second row from the bottom honest. Naming Swatinem/rust-cache closes today's instance; requiring every uses: to be classified closes the class, because the next Cargo-running action cannot be silently assumed inert. The repo uses nine distinct actions, so the three lists are 9 lines.

Bounded on purpose — what it does not do

It proves a job installs the pin, never that the compiler can build the crate. Its reach stops at .github/workflows/, so Cargo invoked by a script a workflow calls is outside it. It knows dtolnay/rust-toolchain specifically. All three limits are written into the file's own doc comment.

Notes for reviewers

  • ci.yml carries two changes the contract requires, and they are the whole workflow diff: shell: bash on the two pin steps that lacked it (release.yml's and bridge-windows-packaged's already had it), and the POSIX-sed note moved from inside the script to a YAML comment above the step — otherwise that note would have to be repeated verbatim in four places to keep the four steps identical. shell: bash adds pipefail; the same script already runs under it in two jobs across all three runners, and sed reads a 15-line file, so head -1 cannot close the pipe early.
  • The classification lists are a deliberate maintenance cost. Adding any new action to any workflow now needs a one-line entry recording whether it runs Cargo. That is the "fails clearly when a new workflow shape requires an explicit policy decision" clause of Design a maintainable guard for workflow Rust toolchain pinning #503, and the failure message names the action and the two constants.
  • Two tests exist to stop this suite passing vacuously, which is the failure mode a guard of this kind has: the Cargo-job set is asserted against an explicit list, and the classification test asserts the walk actually reached actions/checkout, dtolnay/rust-toolchain and Swatinem/rust-cache — an empty finding list otherwise looks identical to a scan that read no files.
  • Gates, run locally on the pinned 1.95.0 with the apt packages CI installs: cargo fmt --all -- --check clean, cargo clippy --all-targets --locked -- -D warnings clean, cargo test --locked green, scripts/no-claude-coauthor-trailers.py clean over the branch range. No lint weakened, no #[allow], no test skipped.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AFUKh3c4ye7aCkbuq1EeUT


Generated by Claude Code

Refs #503

PR #490 fixed the two workflow jobs that bypassed cli/rust-toolchain.toml,
but its guard was withdrawn before merge: it modelled workflow semantics
with affix tests over shell text, absorbed twenty-two review findings
across fourteen rounds, and was still simultaneously too loose (a
reassignment walked through it) and too tight (an idiomatic printf publish
was rejected). Nothing has watched the pin since.

This restores a guard of the shape that finally held, and closes the one
bypass still open when the branch was narrowed.

The pin reader is an exact contract, compared verbatim against one
canonical constant. There is a single such step per Cargo job and it is
six lines, so no shell is parsed and nothing can be smuggled past a
pattern; changing the step means changing PIN_RUN in the same commit.

Around it, for every job that reaches Cargo:

  * exactly one unconditional dtolnay/rust-toolchain@master installer,
    consuming the reader's output;
  * read, then install, then Cargo — in that order;
  * no rustup command or `cargo +toolchain` selector in the job;
  * no RUSTUP_TOOLCHAIN at workflow, job, container.env or step scope,
    matched case-insensitively, because Windows resolves environment
    variables that way;
  * cli/rust-toolchain.toml is the only *tracked* toolchain file — a
    nested one overrides the installed pin for anything built from its
    directory, and two jobs build from exactly such a directory. Tracked
    via `git ls-files`, so a git-ignored .claude/worktrees checkout is
    not policed.

Cargo is not only `run: cargo …`. Swatinem/rust-cache shells out to
`cargo metadata` for its cache key, which ci.yml's own comment records, so
above the installer it builds a cache key on the runner's default
compiler with no script for a guard to read. That was the last finding
against the withdrawn version and it is closed here: known Cargo-running
actions count as Cargo invocations for the ordering check.

Naming those actions is only worth something if the list cannot go quietly
out of date, so every `uses:` in every workflow must be classified as the
installer, Cargo-running, or Cargo-inert. An unclassified action fails by
name and asks for the decision rather than being assumed harmless.

ci.yml carries two changes the contract requires: `shell: bash` on the two
pin steps that lacked it (the other two already had it), and the POSIX-sed
note moved from inside the script to a YAML comment above the step, where
it does not have to be repeated verbatim in four places.

Copy link
Copy Markdown
Contributor Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-09T05:30:33.918135Z 770dbeb Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 64bd5ea9d7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cli/tests/toolchain_pin_gate.rs Outdated
Comment on lines +230 to +231
let normalized = lower.split_whitespace().collect::<Vec<_>>().join(" ");
lower.contains("rustup") || normalized.contains("cargo +")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Normalize shell continuations before checking overrides

When a workflow formats a selector as cargo \ +nightly build, Bash removes the backslash-newline and executes cargo +nightly build (cargo --help explicitly lists cargo [+toolchain]), but this normalization retains the backslash and produces cargo \ +nightly, so the contains("cargo +") check misses it. Such a job still satisfies the canonical reader/installer checks while compiling with a different toolchain, defeating the guard's stated contract.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Real, and fixed at the root in bc2f60cc. I reproduced it before changing anything rather than reasoning about it.

Added as a case to later_toolchain_overrides_are_rejected_without_parsing_shell, "cargo \\\n +nightly build" failed the suite on 64bd5ea exactly as you describe — normalisation kept the backslash, so cargo \ +nightly never matched cargo +.

has_toolchain_override now drops \<newline> before normalising whitespace. Planted into the real ci.yml, in the cargo fmt --check step:

ci.yml: job `gates` contains a rustup command or explicit `cargo +toolchain` selector

And the other direction, which is where a tightening of this kind usually goes wrong: an innocently wrapped cargo fmt \ + newline + --all -- --check in the same step still passes, and two negative controls pin that. Removing the continuation must not invent a selector where the script has none.

It is two characters removed lexically, not a step toward interpreting shell — the check still only asks whether those substrings appear at all, which is what keeps it free of the model that sank the previous guard.

Gates on the pinned 1.95.0: fmt clean, clippy clean, full cargo test --locked green, tree restored after each planted mutation.


Generated by Claude Code

Codex review, PR #506. The shell removes a backslash-newline before it
splits words, so

    cargo \
      +nightly build

runs as `cargo +nightly build`. `has_toolchain_override` normalised
whitespace but kept the backslash, producing `cargo \ +nightly`, so the
`cargo +` test missed it — a job could satisfy the canonical reader and
installer checks and still compile on another toolchain.

Reproduced first: the new case fails the suite before this change and
passes after it, and planted into `ci.yml`'s `cargo fmt --check` step it
is rejected by job and by reason. The opposite direction is covered too —
an innocently wrapped `cargo fmt \` + newline + `--all` is still accepted,
because removing the continuation must not invent a selector where the
script has none.

Two characters, removed lexically. The guard still asks only whether these
substrings appear at all, so it remains free of any model of shell syntax.

Copy link
Copy Markdown
Contributor Author

Round 1 — bc2f60cc. Codex's single P2 on 64bd5ea was real and is closed at the root; details on the thread.

has_toolchain_override normalised whitespace but kept a backslash-newline, so cargo \ + newline + +nightly build — which the shell runs as cargo +nightly build — produced cargo \ +nightly and never matched. Reproduced first (the new case fails on 64bd5ea, passes here), then planted into ci.yml's own cargo fmt --check step and confirmed rejected by job and by reason. The reverse direction is pinned too: an innocently wrapped cargo fmt \ + newline + --all is still accepted.

CI was green on all 14 checks on 64bd5ea; this head needs its own run.

Head bc2f60cc
cargo fmt --all -- --check clean
cargo clippy --all-targets --locked -- -D warnings clean
cargo test --locked 55 suites, 1,651 tests, 0 failed (on 64bd5ea; re-running on this head)
scripts/no-claude-coauthor-trailers.py clean over the branch range

No lint weakened, no #[allow] added, no test skipped.

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc2f60cc2b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cli/tests/toolchain_pin_gate.rs Outdated
fn has_toolchain_override(run: &str) -> bool {
let lower = run.to_ascii_lowercase().replace("\\\n", "");
let normalized = lower.split_whitespace().collect::<Vec<_>>().join(" ");
lower.contains("rustup") || normalized.contains("cargo +")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject selectors invoked through cargo.exe

On Windows, a step such as cargo.exe +nightly build bypasses this check because the normalized text contains cargo.exe +, not cargo +, even though cargo --help documents the syntax as Usage: cargo [+toolchain] [OPTIONS] [COMMAND]. Fresh evidence beyond the closed continuation case is that invokes_cargo already explicitly recognizes cargo.exe as Cargo, so this job is guarded but its toolchain override is accepted; normalize the executable form or check both names.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Real, and fixed at the root in 53238b30. Your reasoning about why it was there is the part I acted on: invokes_cargo already accepted cargo.exe, so the two checks held different notions of what Cargo is called, and the gap between them is where both findings on this file have landed.

Reproduced before changing anything. With ci.yml's cargo fmt --check step rewritten to cargo.exe +nightly fmt --all -- --check:

bc2f60cc (before): 10 passed, 0 failed   ← bypass
53238b30 (after):  ci.yml: job `gates` contains a rustup command or explicit `cargo +toolchain` selector

Tree restored after.

is_cargo_executable is now the single definition both checks use, and the selector is read as two adjacent words — a Cargo executable followed by a +… — instead of the substring cargo . So every spelling the invocation check accepts is covered, not just the two you named: cargo.exe, C:\Users\runner\.cargo\bin\cargo.exe +stable build, and a quoted sh -c "cargo +nightly build", all now cases in the suite.

Two negative controls pin the direction a token rule can get wrong on its own: cargo-nextest run +extra is not Cargo, and echo 1 + 2 && cargo build is not a selector.

Gates on the pinned 1.95.0: fmt clean, clippy clean, cargo test --locked 55 suites / 1,651 tests / 0 failed.


Generated by Claude Code

Codex review, PR #506. `has_toolchain_override` searched for the literal
`cargo ` followed by `+`, while `invokes_cargo` accepted `cargo.exe`, a
path prefix and any case. A step running

    cargo.exe +nightly build

was therefore counted as a Cargo invocation — so the job was guarded — and
its selector was read straight past.

Both findings on this file have been that gap: two notions of "Cargo" that
did not agree. They agree now. `is_cargo_executable` is the single
definition, and the selector is read as two adjacent words — a Cargo
executable followed by a `+…` — rather than as a substring, so every
spelling the invocation check already accepts is covered: `cargo.exe`, an
absolute Windows path, a quoted `sh -c "cargo +nightly build"`.

Reproduced before fixing: with `ci.yml`'s `cargo fmt --check` step changed
to `cargo.exe +nightly fmt`, the suite at bc2f60c reports 10 passed, and
with this change it fails naming the job and the reason. Tree restored.

Two negative controls pin the other direction, which a token rule can get
wrong in its own way: `cargo-nextest run +extra` is not Cargo, and a `+`
somewhere else in the script is not a selector.

Copy link
Copy Markdown
Contributor Author

Round 2 — 53238b30. Codex's P2 on bc2f60cc was real and is closed at the root; details on the thread.

Worth naming the shape, because both findings on this branch are one shape rather than two: the guard held two notions of what Cargo is called. invokes_cargo accepted cargo.exe, a path prefix and any case; has_toolchain_override searched for the literal cargo followed by +. A step is Cargo enough to be guarded and not Cargo enough for its selector to be read — that is the entire gap, and the line continuation was the same gap in its lexical form.

They agree now. is_cargo_executable is the one definition, and the selector is read as two adjacent words rather than a substring.

Both closed at the root, and both reproduced against the real ci.yml before the fix rather than argued:

Bypass Before After
cargo \ + newline + +nightly build accepted on 64bd5ea rejected
cargo.exe +nightly build accepted on bc2f60cc rejected
C:\Users\runner\.cargo\bin\cargo.exe +stable build never raised rejected
sh -c "cargo +nightly build" never raised rejected
wrapped cargo fmt \ + newline + --all accepted, correctly
cargo-nextest run +extra accepted, correctly
Head 53238b30
cargo fmt --all -- --check clean
cargo clippy --all-targets --locked -- -D warnings clean
cargo test --locked 55 suites, 1,651 tests, 0 failed
CI green on all 14 checks on 64bd5ea; bc2f60cc and this head re-running

No lint weakened, no #[allow] added, no test skipped.

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 53238b301a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cli/tests/toolchain_pin_gate.rs Outdated
Comment on lines +263 to +264
fn has_toolchain_override(run: &str) -> bool {
let lower = run.to_ascii_lowercase().replace("\\\n", "");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Normalize PowerShell continuations before pairing Cargo arguments

When a Cargo step in the checked ci.yml bridge-windows-packaged job is wrapped using PowerShell's native backtick continuation (for example, cargo.exe followed by a newline and +nightly build), PowerShell removes the backtick-newline and Cargo receives the selector, but this normalization removes only Bash's \-newline; split_whitespace therefore leaves the backtick as an intervening token and returns false. Fresh evidence beyond the closed Bash-continuation thread is that this job runs on windows-latest and its Cargo step does not select Bash, so pwsh syntax is in the guard's actual workflow scope; such a selector would override the installed pin while this test remains green.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Real, and fixed at the root in c7649415. Your scope argument is the load-bearing part and it checks out: bridge-windows-packaged runs on windows-latest, its Build source AWARE host step sets no shell:, so its default is pwsh and the backtick form is in this guard's actual workflow scope rather than hypothetical.

Reproduced in that exact job before changing anything, cargo.exe + backtick + newline + +nightly build:

53238b30 (before): 10 passed, 0 failed   ← bypass
c7649415 (after):  ci.yml: job `bridge-windows-packaged` contains a rustup command or explicit `cargo +toolchain` selector

Tree restored after.

I did not add a second special case. Three shells spell a continuation three ways — bash \, pwsh `, cmd ^ — so instead of learning them, the pairing now drops any token that is nothing but punctuation once trimmed, which is exactly what a lone backtick or caret is. One rule, and less shell knowledge than the special case it replaces. The backslash keeps its rewrite for a stated reason: \ is a real character inside a Windows path, so it cannot be treated as punctuation.

cargo ^ + newline + +stable build is a case too, and two more negative controls pin the other direction: a wrapped cargo build + backtick + --locked and an echo "+done" on the line after a Cargo command are both still accepted.

Gates on the pinned 1.95.0: fmt clean, clippy clean, cargo test --locked 55 suites / 1,651 tests / 0 failed.


Generated by Claude Code

Codex review, PR #506. Only bash's `\`-newline was removed, so a selector
wrapped with PowerShell's backtick left a `` ` `` token between `cargo.exe`
and `+nightly` and the pair went unread. The finding is in scope, not
theoretical: `bridge-windows-packaged` runs on windows-latest and its Cargo
step selects no shell, so pwsh is its default.

Rather than learn three shells, the pairing now ignores anything that is
nothing but punctuation once trimmed — which is what a lone backtick or a
cmd caret is. One rule covers all three continuations, and it is less shell
knowledge than the special case it replaces, not more. The backslash keeps
its rewrite because `\` is a real character inside a Windows path and
cannot be treated as punctuation.

Reproduced before fixing, in that exact job: `cargo.exe ` + backtick +
newline + `+nightly build` leaves the suite at 10 passed on 53238b3 and
fails here naming `bridge-windows-packaged`. Tree restored.

Both directions covered: the caret form is a case too, and two more negative
controls pin that a wrapped `cargo build ` + backtick + `--locked`, and an
`echo "+done"` on the line after a cargo command, are still accepted.

Copy link
Copy Markdown
Contributor Author

Round 3 — c7649415. Codex's P2 on 53238b30 was real, in scope, and is closed at the root; details on the thread.

All three findings on this branch have been one thing: two of the guard's own notions disagreeing. Round 1 and 2 were two notions of what Cargo is called; this one is two notions of what "adjacent" means. Each fix collapses the pair into a single definition rather than patching the instance, which is the property the previous guard never had.

Every case verified by executing the suite against a planted mutation of the real ci.yml, tree restored after each:

Case Before After
cargo \ + newline + +nightly (bash) accepted on 64bd5ea rejected
cargo.exe +nightly accepted on bc2f60cc rejected
cargo.exe + backtick + newline + +nightly (pwsh, in bridge-windows-packaged) accepted on 53238b30 rejected
cargo ^ + newline + +stable (cmd) never raised rejected
C:\Users\runner\.cargo\bin\cargo.exe +stable build never raised rejected
sh -c "cargo +nightly build" never raised rejected
wrapped cargo fmt \ + newline + --all accepted
wrapped cargo build + backtick + --locked accepted
cargo-nextest run +extra accepted
echo "+done" after a Cargo command accepted

Six rejections and four acceptances, because a tightening of this kind fails in both directions and the previous guard's record was half false positives.

Head c7649415
cargo fmt --all -- --check clean
cargo clippy --all-targets --locked -- -D warnings clean
cargo test --locked 55 suites, 1,651 tests, 0 failed
CI green on all 14 checks on both 64bd5ea and bc2f60cc; this head re-running

No lint weakened, no #[allow] added, no test skipped. The workflow diff is still the same two lines it has been since the first commit.

@codex review


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c7649415d2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +274 to +276
.split_whitespace()
.map(bare_token)
.filter(|word| !word.is_empty())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Normalize continuations before splitting executable names

In the checked ci.yml bridge-windows-packaged job, which uses the default PowerShell shell, car + backtick-newline + go.exe +nightly build is executed as cargo.exe +nightly build; Microsoft documents that a terminal backtick allows input to continue on the next line. This code instead splits first, yielding car, go.exe, and +nightly, and dropping punctuation-only words never reconstructs the Cargo executable, so the override remains accepted. Normalize backtick-newline and caret-newline sequences before tokenization, as is already done for Bash's backslash-newline.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Real, and fixed at the root in 770dbeba — with one correction to the reproduction worth having on the record.

joined_script now removes all three continuations — bash \, pwsh backtick, cmd ^ — once, before anything reads the script, and both the Cargo-invocation check and the selector check read its result. You are right that splitting first can never reconstruct the word; the fix is to stop splitting first.

The correction. Planted into an existing job, your mutation was already rejected on c7649415 — but not for the reason either of us gives: the step stopped looking like Cargo at all, so bridge-windows-packaged dropped out of the found set and the explicit coverage list failed. To show the real escape I had to plant a new job, where nothing is added to that list and so nothing notices:

c7649415 (before): new windows job, `car` + backtick-newline + `go.exe +nightly build`  -> 10 passed, 0 failed
770dbeba (after):  same job                                                             -> the known Cargo-job set changed

Tree restored. So the finding is real and its consequence is worse than "the override is accepted": on the old code such a job was not a Cargo job at all, and the guard skipped it entirely. That is why the fix had to move into invokes_cargo too rather than only into the selector check.

Two negative controls pin the other direction, since joining words is exactly how this could go wrong: my-car + backtick-newline + go-tool build must not become Cargo, at either check.

I have also written the boundary into the file's doc, because each round has probed one step further along this same axis and it deserves a stated answer rather than another lap: this check sees a selector a contributor wrote, in any layout — the accident it exists to catch. It does not see one the shell assembles at run time (c=cargo; $c +nightly build, a value arriving in an environment variable, a script file the step calls). Nothing that reads YAML can, and a version that tried would be back to modelling shell, which is what sank the guard this one replaced.

Gates on the pinned 1.95.0: fmt clean, clippy clean, cargo test --locked 55 suites / 1,651 tests / 0 failed.


Generated by Claude Code

Codex review, PR #506. A continuation is not only *between* words: pwsh
runs `car` + backtick-newline + `go.exe +nightly build` as
`cargo.exe +nightly build`. Splitting first and then ignoring
punctuation-only words can never put that word back together, so the
executable was unreadable and with it the selector.

`joined_script` now removes all three shells' continuations — bash `\`,
pwsh backtick, cmd `^` — once, and both the Cargo-invocation check and the
selector check read its result. That is the same correction as the previous
two commits, a third time: two of this file's own notions disagreed, here
about which text is being read, so there is now one.

Reading the same text matters more than it looks. On the previous commit a
NEW job whose only Cargo call was wrapped this way was not seen as a Cargo
job at all, so the whole guard skipped it and the explicit coverage list
stayed satisfied — nothing was added to it. Demonstrated exactly that way:
such a job leaves c764941 green and fails here. (Planted into an EXISTING
job the same mutation was already caught, by the coverage list noticing the
job had dropped out of it. The new-job form is the one that escaped.)

Two negative controls pin the other direction: joining must not manufacture
Cargo out of `my-car` + backtick-newline + `go-tool build`.

Also records where this check stops, since each round has probed one step
further along the same axis: it reads a selector a contributor WROTE, in
any layout, which is the accident this file exists to catch. One the shell
assembles at run time — `c=cargo; $c +nightly` — is not visible to anything
that reads YAML, and chasing it is how the previous guard died.

Copy link
Copy Markdown
Contributor Author

Round 4 — 770dbeba. Codex's P2 on c7649415 was real and is closed at the root; the thread carries the detail, including a correction to how it reproduces.

Four findings, one shape each time: two of the guard's own notions disagreeing. What Cargo is called (rounds 1–2), what "adjacent" means (round 3), which text is read (round 4). Each fix collapses the pair into one shared definition instead of patching the instance, which is why each has stayed a few lines rather than growing a model.

This round also found something neither of us predicted. Planted into an existing job the reported mutation was already rejected — the step stopped looking like Cargo, the job dropped out of the found set, and the explicit coverage list failed. The real escape needs a new job, where nothing is added to that list: on c7649415 such a job was not recognised as a Cargo job at all and the guard skipped it entirely. So the fix had to go into the Cargo-invocation check, not only the selector check.

CI is green on 64bd5ea, bc2f60cc, 53238b30 and c7649415; this head is running.

I have also written the boundary into the file's doc rather than leave it to be rediscovered: this check sees a selector a contributor wrote, in any layout — the accident the file exists to catch — and not one the shell assembles at run time (c=cargo; $c +nightly build, a value from an environment variable, a script file the step calls). Nothing that reads YAML can, and chasing that is precisely how the previous guard died at twenty-eight findings. Against a deliberate bypass the protection is review of a two-line workflow diff.

Head 770dbeba
cargo fmt --all -- --check clean
cargo clippy --all-targets --locked -- -D warnings clean
cargo test --locked 55 suites, 1,651 tests, 0 failed

No lint weakened, no #[allow] added, no test skipped. The workflow diff is unchanged since the first commit: two shell: bash lines and a comment moved out of a script.

@codex review


Generated by Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 770dbebaa1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@pawellisowski
pawellisowski merged commit 0982900 into main Sep 9, 2026
14 checks passed
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.

1 participant