What happens
GitHub Actions echoes the source of every run: step — line by line, in cyan-bold — before executing it. PatchRail reads those lines as if they were the step's output, so a log carries the step's error-handling branches whether or not they were ever taken.
Found by running the documented one-liner over real failing logs from public repos:
gh run view <id> --repo astral-sh/ruff --log-failed | patchrail ci explain
astral-sh/ruff's benchmark job is a Rust panic (exit code: 101):
thread 'main' (2574) panicked at crates/ruff_benchmark/benches/ty_walltime.rs:238:13:
Expected between 1 and 3 diagnostics on project 'altair' but got 4
failed to execute the benchmark process, exit code: 101
PatchRail 0.6.1 calls it python_test_failure. The entire case is one line of the installer script — an error branch for a download that succeeded:
^[[36;1m echo "::error title=Failed to install CodSpeed CLI::Installation of CodSpeed CLI ..."^[[0m
FAILED .*:: (pytest's short-summary line, matched case-insensitively) reads Failed to install CodSpeed CLI:: off it.
Why it matters beyond this one log
No rule is safe from this. Seven of ten real failing logs sampled (ruff, airflow, pydantic, tokio, vite, prometheus, httpx) echo a step body, and those bodies say pnpm run lint, bail() {, exit 1, echo "'toolchain' is a required input". Any of them can carry a verdict on its own.
The colour is the only thing marking these lines as source rather than output — and _strip_ansi_escapes erases it before anything looks at the text, so by classification time the step's program text is indistinguishable from its output.
Expected
The runner echoing a step's source is a command echo like Run mypy . or + ruff check .: it may corroborate a verdict, it must not carry one. A tool that really failed also fails somewhere off the script listing.
What happens
GitHub Actions echoes the source of every
run:step — line by line, in cyan-bold — before executing it. PatchRail reads those lines as if they were the step's output, so a log carries the step's error-handling branches whether or not they were ever taken.Found by running the documented one-liner over real failing logs from public repos:
astral-sh/ruff's benchmark job is a Rust panic (
exit code: 101):PatchRail 0.6.1 calls it
python_test_failure. The entire case is one line of the installer script — an error branch for a download that succeeded:FAILED .*::(pytest's short-summary line, matched case-insensitively) readsFailed to install CodSpeed CLI::off it.Why it matters beyond this one log
No rule is safe from this. Seven of ten real failing logs sampled (ruff, airflow, pydantic, tokio, vite, prometheus, httpx) echo a step body, and those bodies say
pnpm run lint,bail() {,exit 1,echo "'toolchain' is a required input". Any of them can carry a verdict on its own.The colour is the only thing marking these lines as source rather than output — and
_strip_ansi_escapeserases it before anything looks at the text, so by classification time the step's program text is indistinguishable from its output.Expected
The runner echoing a step's source is a command echo like
Run mypy .or+ ruff check .: it may corroborate a verdict, it must not carry one. A tool that really failed also fails somewhere off the script listing.