fix(ruby): stream debuggee stdout mid-run via injected sync prelude (#317) - #319
Merged
Conversation
…317) Ruby block-buffers $stdout when it is a pipe, and rdbg -c hands the debuggee the adapter process's piped stdio — so puts output only reached the proxy's stdio scraper at process exit (the #254/#258 exit-flush drain), never mid-run. The comprehensive matrix's ruby get_output cell soft-failed on exactly this: the session sits paused at a re-armed loop breakpoint, the process never exits, nothing flushes. Fix: buildTargetCommand now materializes a two-line prelude ($stdout.sync = true; $stderr.sync = true) in the session log dir and injects it into the target ruby argv as a single -r<path> element (both plain and bundler branches). Argv insertion is space-safe, unlike RUBYOPT which splits on whitespace; the log dir is product-owned, avoiding a predictable require path in shared /tmp. On write failure the launch proceeds without the prelude (exit-only flushing, as before). Launch mode only — attach connects to a process we did not start. Verified: rdbg's stop-at-load still lands on the main script, not the prelude. Regression coverage: hard e2e test asserting the marker arrives while the session is still paused (cannot be satisfied by the exit flush), plus unit tests for prelude materialization and argv shape. The matrix cell flips FAIL->PASS (175/0/6, was 174/1/6). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
… integration smoke Review follow-ups on #317: the new e2e test added a third verbatim copy of pollUntil — promote it to smoke-test-utils.ts and point all three suites at it. The ruby integration smoke test's logDir was <cwd>/logs/tests, which buildAdapterCommand now really writes into (sync prelude); use a per-test temp dir so the test stays hermetic and the -r assertion never depends on repo-tree writability. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Collaborator
Author
|
Post-PR multi-angle self-review outcome (all angles verified against the real Ruby 3.4/debug-1.11 toolchain): Applied (second commit):
Dismissed with rationale:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #317.
Problem
Ruby block-buffers
$stdoutwhen it is a pipe ($stdout.sync == false;$stderrdefaults tosync == true— which is exactly why stderr lines arrived mid-run while stdout didn't).rdbg -chands the debuggee the adapter process's piped stdio, soputsoutput only reached the proxy's stdio scraper at process exit (the #254/#258 exit-flush drain) — never while the session sat paused at a breakpoint. The comprehensive matrix's rubyget_outputcell soft-failed on exactly this: the workflow pauses at a re-armed loop breakpoint, the process never exits, nothing flushes (entries=1).Fix
buildTargetCommandnow materializes a two-line prelude ($stdout.sync = true; $stderr.sync = true) in the session log dir and injects it into the target ruby argv as a single-r<path>element, in both the plain and bundler branches.Design notes:
-cexecs the command after--verbatim).RUBYOPTsplits on whitespace and would break for install paths containing spaces.os.tmpdir(): the file isrequired into the debuggee; a predictable name in shared world-writable/tmpwould be a local code-injection surface on POSIX. The log dir is product-owned. Runtime generation also means no npm-bundle asset plumbing.PYTHONUNBUFFERED=1. A script can set$stdout.sync = falseitself to opt out. Attach connects to a process the server did not start, so nothing is injected there (documented).Tests
mcp-server-smoke-ruby.test.ts): breakpoint in the fizzbuzz loop → continue → pollget_outputfor iteration 1's marker → assert it arrives while the session is still paused, so the exit-flush path cannot satisfy it. Red against the pre-fix build (poll timeout), green after (<2s).nullon write failure) and argv shape (plain, bundler, degraded).ruby-session-smoke.test.ts— runs in CI with the realbuildAdapterCommand).get_outputflips FAIL→PASS — 175 PASS / 0 FAIL / 6 SKIP (was 174/1/6); the matrix cell stays soft by design, the new e2e test is the hard guard.docs/ruby/README.mdProgram output).Follow-up filed: #318 (
launchConfig.envsilently ignored on the ruby launch path — pre-existing, out of scope here).🤖 Generated with Claude Code