Repository navigation
fix(core): forward stderr from stdout-only filters on every exit (adopt upstream #3772) - #159
Conversation
`RunOptions::stdout_only()` is documented as "stdout-only to filter, stderr passthrough", but the passthrough half was never implemented: stderr was captured and dropped on every path except `skip_filter_on_failure`. A tool that reports on stderr and leaves stdout empty therefore produced nothing at all. golangci-lint is the visible case -- a config or build error goes to stderr, the filter is handed an empty stdout, and the never-worse guard replaces its "JSON parse failed" fallback with the empty string, so both streams end up silent. The same shape applies to every other stdout-only filter: ruff, pytest, rspec, rubocop, gh, glab, prettier, tree, wc. Savings are also measured against running the command directly, so the stderr that is forwarded has to be counted as emitted. Comparing stdout to stdout while printing stderr as well booked a passed-through stream as if it had been filtered away. The guard keeps comparing against stdout. stderr is forwarded verbatim on both sides, so it cancels: never-worse reduces to `filtered <= stdout`, and widening the baseline to the combined output would let rtk emit more than the command it replaces. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Six cases over a fake tool on PATH, covering both halves of the contract: the
tool's own stderr has to reach the user, and rtk must not invent a stdout
message in its place.
The prettier case is the one that is easy to get wrong. It writes its report to
stderr and nothing to stdout even on a successful run, so a filter's empty-input
placeholder ("Error: prettier produced no output") would be printed over a run
that worked. An empty stdout from a command that printed nothing on stdout is
the correct answer.
Also pinned: stderr is not replayed onto stdout on top of being forwarded, a
genuinely silent command stays silent, forwarding is not golangci-specific, and
exit codes propagate -- including golangci-lint's exit 1, which means issues
were found and is reported without failing the build.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…i#3772 The fork adopted upstream rtk-ai#3029 (georgyia, c27bbd0) to surface a failing tool's stderr under stdout-only filtering, scoped to non-zero exits. rtk-ai#3772 forwards stderr on every exit and counts it, so the earlier block now prints the same diagnostics twice on failure. Remove it; its test file keeps the failure case and its success case now pins the new contract: a success exit with stderr-only output is no longer collapsed to silence. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ract Upstream's prettier is a stdout-only filter, so rtk-ai#3772's test expects the stderr report forwarded verbatim and an empty stdout. This fork's prettier (PR #117, upstream rtk-ai#2878) runs on the combined stream and reads the "[warn] <file>" report itself, printing a summary on stdout. Keep the intent of the test — the report reaches the user and no clean run is invented — without pinning which stream carries it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The six tests adopted from upstream rtk-ai#3772 spawn rtk without RTK_DB_PATH, so every run booked fake-tool savings into the developer's history.db. Point them at a per-test file in the tempdir, as tests/stderr_passthrough_test.rs already does. Also names rtk-ai#2878 as the upstream issue it is. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review roundIndependent read-only reviewer pass over the branch, applying Verified
Said plainly: the re-pinned prettier test ( Nits taken ( Nits declined: no Windows-runnable success-exit stderr test (both files are Gate re-run after the amendment: fmt clean, clippy 0 warnings, all tests green on the x64 host toolchain. |
Closes #135. Adoption of upstream rtk-ai#3772 (Nicolas Le Cam / KuSh): every
RunOptions::stdout_only()filter dropped the tool's stderr, and with stdout empty the never-worse guard collapsed the run to silence on both streams. Around 30 call sites (ruff,pytest,rspec,rubocop,gh,glab,go test,golangci-lint,tree,wc,psql, …).Commits
12db983fix(core): forward stderr from stdout-only filters and count it2eee910. One deviation, folded into the pick: the import hunk conflicted with the fork'suse regex::Regex;(from upstream's bun/deno runner work); both imports kept. Body otherwise byte-identical.4d3b417test(core): pin stdout-only stderr handling7b89028, byte-identical (tests/stderr_only_failure_test.rs, unix-only).9ed2e49fix(runner): drop the failure-only stderr forward superseded by rtk-ai#3772c27bbd0, fork PR #17) that forwards stderr on non-zero exits only. rtk-ai#3772 subsumes it; left in place the two blocks print the diagnostics twice on failure. Its test filetests/stderr_passthrough_test.rskeeps the failure case; the success case flips from "stays suppressed" to "reaches the user" — the old assertion encoded rtk-ai#3029's scoping, which is exactly what rtk-ai#3772 changes.90008eetest(core): pin the prettier stderr case to this fork's prettier contract[warn]report on stdout, so that test would fail on fork CI. Re-pinned to the intent: the report reaches the user (either stream) and no clean run is invented; exit code still checked.Why the rtk-ai#3029 adoption goes
Upstream rtk-ai#3029 is still open, so this is not a heal. It is a partial supersession inside the fork: rtk-ai#3772 does everything rtk-ai#3029 did plus the success-exit case, and the two cannot coexist without a double print.
git show upstream/develop:src/core/runner.rs | grep 3029is blank — the block was fork-only.Repro, real binaries (before = installed
developbuild, after = this branch), fakeruff.cmd/prettier.cmdfirst on PATHQuality gate
x64 host toolchain:
cargo fmt --all --checkclean ·cargo clippy --all-targets0 warnings ·cargo test --all3233 unit tests passed, 0 failed, 8 ignored; all integration suites green. The two stderr integration test files are#![cfg(unix)]and compile away on Windows; fork CI (ubuntu, macos) is their verification.git diff --checkclean.Not done
REVIEW_REQUIRED; Adoption, no upstream obligation. When it merges, the next sync's conflict is confined to the import line and the removed fix(runner): surface a failing tool's stderr under stdout-only filtering rtk-ai/rtk#3029 block.rtk gainfor stdout-only filters will report slightly lower savings than before. Intended by the PR; not adjusted here.🤖 Generated with Claude Code