Skip to content

orderfile: hand the interactive workloads their stdin under node - #33358

Merged
Jarred-Sumner merged 3 commits into
mainfrom
claude/orderfile-workload-stdin
Jul 6, 2026
Merged

Jarred-Sumner merged 3 commits into
mainfrom
claude/orderfile-workload-stdin

Conversation

@robobun

@robobun robobun commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to #33345, fixing the run() stdin wiring flagged in this review.

The bug

run() asks for input and for stdin to be "ignore" at the same time:

input: options.input,
stdio: ["ignore", "pipe", "pipe"],

Node only delivers input when stdin is a pipe. With "ignore" it drops it on the floor, no error. Bun's spawnSync delivers it either way, which is why bun run orderfile works locally and nobody noticed: CI builds with node --experimental-strip-types scripts/build.ts (.buildkite/ci.mjs:595), so on CI the two interactive workloads are the only ones that are typed anything and they get nothing.

Running the real cli-fixture.js through the real run() options, once under each runtime:

### under NODE (what CI uses):
stdin=ignore  pipe: {"status":0,"read":0,"ms":50}
stdin=ignore  tty : {"err":"ETIMEDOUT","ms":10007}
stdin=pipe    pipe: {"status":0,"read":3,"ms":50}
stdin=pipe    tty : {"status":0,"read":3,"ms":58}

### under BUN (what a dev uses locally):
stdin=ignore  pipe: {"status":0,"read":3,"ms":43}
stdin=ignore  tty : {"status":0,"read":3,"ms":52}

So on CI today the pipe workload reads 0 lines, exits 0, and never drives readline. The tty one is worse: ptyrun gets /dev/null for stdin, types its ^D before bun has even started, readline then waits for a line that never arrives, and the workload sits there until WORKLOAD_TIMEOUT_MS (120s) kills it. run() turns that into a throw, build.ts catches it and annotates, and the build ships unordered. Either way the ~2k tty and readline functions those two workloads exist to capture are missing, which is the whole reason they were added.

The fix

Pipe stdin when there is input to deliver. That is what the rest of the repo already does (scripts/utils.mjs:246, scripts/utils.mjs:358, scripts/build/fetch-cli.ts:250); generate.ts was the only site that got it wrong, and I grepped the others to be sure.

run() captured nothing from its closure, so it is hoisted to module scope as runCommand and exported. That is what lets the test drive it under node, which is the only runtime where any of this is observable.

Verification

test/js/bun/perf/linker-order.test.ts gains a case that spawns node --experimental-strip-types on a fixture importing runCommand, runs cli-fixture.js through it, and checks the fixture was actually typed its input. An in-process test would be useless here: it would run under bun and pass either way.

With stdin reverted to "ignore":

  {
    "exitCode": 0,
-   "greeted": true,
-   "read": "3",
+   "greeted": false,
+   "read": "0",
  }
(fail) interactive workload stdin > reaches the workload when the generator runs under node, as CI does

and with the fix, 17 pass, 1 skip, 0 fail for the file. Agents install node 26.3.0 (scripts/bootstrap.sh), so the case runs rather than skips.

The generator spawns its workloads with spawnSync(input) but stdin set to
ignore. Node only delivers input when stdin is a pipe and silently drops
it otherwise, so under node the pipe workload reads nothing and exits 0
without ever driving readline, and the tty one waits for a line that never
arrives until WORKLOAD_TIMEOUT_MS kills the whole trace. Bun's spawnSync
delivers input either way, so only CI notices: it builds with
node --experimental-strip-types scripts/build.ts.

Pipe stdin when there is input to deliver, which is what the rest of the
repo already does (scripts/utils.mjs, scripts/build/fetch-cli.ts).

run() captured nothing from its closure, so hoist it to module scope as
runCommand and export it: the test drives it under node, which is the only
runtime where this is observable.
@github-actions github-actions Bot added the claude label Jul 5, 2026
@robobun

robobun commented Jul 5, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 6:14 AM PT - Jul 5th, 2026

❌ @robobun, your commit 1e39eed has 1 failures in Build #68517 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 33358

That installs a local version of the PR into your bun-33358 executable, so you can run:

bun-33358 --bun

@coderabbitai

coderabbitai Bot commented Jul 5, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 10 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d2e0d558-31f1-4c01-9e1d-014e374d8279

📥 Commits

Reviewing files that changed from the base of the PR and between 76c976d and 1e39eed.

📒 Files selected for processing (1)
  • test/js/bun/perf/linker-order.test.ts

Walkthrough

Refactors scripts/orderfile/generate.ts by extracting command execution into an exported runCommand() helper with a RunOptions type, replacing all local run() call sites, and conditionally piping stdin only when input is provided. Adds a new interactive workload fixture and a Linux-only Node-driven test validating stdin consumption and exit behavior.

Changes

Orderfile Command Execution and Interactive Stdin Testing

Layer / File(s) Summary
runCommand helper extraction
scripts/orderfile/generate.ts
Introduces exported RunOptions type and runCommand() function using spawnSync, piping stdin only when options.input is set, replacing the previous local run().
Call site migration to runCommand
scripts/orderfile/generate.ts
Updates tracer/pty compilation, dependency packing (bun-profile pm pack), traced workload execution, and nm symbol collection to use runCommand() instead of the old local runner.
Interactive stdin fixture and test
test/js/bun/perf/orderfile-workload-fixture.ts, test/js/bun/perf/linker-order.test.ts
Adds a fixture script invoking runCommand with fixed stdin and a timeout, setting process.exitCode instead of calling process.exit; adds a Linux-only, Node-driven skipped test importing nodeExe that verifies greeting output, exactly 3 consumed input lines, and a successful exit code.

Related PRs: None identified.

Suggested labels: scripts, tests, orderfile

Suggested reviewers: None identified.

Poem
A rabbit hops through spawnSync's maze,
Piping stdin only when input plays,
Node greets the workload, counts to three,
runCommand hums where run() used to be,
Tests confirm the exit is clean and bright,
Orderfile scripts refactored just right. 🐇

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main fix: piping stdin to interactive orderfile workloads under Node.
Description check ✅ Passed The description covers the bug, the fix, and verification steps, matching the template’s intent.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/js/bun/perf/linker-order.test.ts`:
- Around line 179-189: The perf test currently drains proc.stderr in Promise.all
but drops it from the assertion output, so failures from the fixture or
runCommand are hard to diagnose. Update the assertion in linker-order.test.ts to
capture stderr alongside stdout and include it in the expected object or failure
diff, using the existing proc.stdout.text(), proc.stderr.text(), and proc.exited
flow so the diagnostic stays visible when the test fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 29d1950b-c0bb-4fe4-be89-1a11f8eac525

📥 Commits

Reviewing files that changed from the base of the PR and between fb50cce and 76c976d.

📒 Files selected for processing (3)
  • scripts/orderfile/generate.ts
  • test/js/bun/perf/linker-order.test.ts
  • test/js/bun/perf/orderfile-workload-fixture.ts

Comment thread test/js/bun/perf/linker-order.test.ts Outdated

@claude claude Bot 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.

LGTM — targeted one-line stdio fix in build tooling with a well-constructed node-runtime regression test.

Extended reasoning...

Overview

This PR touches only build/test tooling: scripts/orderfile/generate.ts (the linker order-file generator), test/js/bun/perf/linker-order.test.ts, and a new test fixture. The functional change is a single conditional — stdio[0] becomes options.input === undefined ? "ignore" : "pipe" so that Node's spawnSync actually delivers input to the interactive workloads. The rest is a mechanical hoist of the inner run() helper to an exported module-level runCommand() (it captured nothing from its closure) plus 6 call-site renames, done solely so the test can import and drive it under Node.

Security risks

None. This is an internal build script that spawns cc, nm, ptyrun, and the freshly-built bun-profile with fixed argument lists. No user-controlled input, no network, no auth/crypto/permissions surface. The change only adjusts which end of a child's stdin is a pipe vs /dev/null.

Level of scrutiny

Low. Nothing here ships in the runtime binary — it affects how the release-build order file is generated on Linux CI. The fix matches the established pattern the PR cites elsewhere in scripts/, and gating on === undefined (presence, not truthiness) is the correct choice. The hoist is behavior-preserving: same body, same option handling, just moved and exported.

Other factors

The added test is well-designed for this bug class: it spawns node --experimental-strip-types on a fixture that imports the real runCommand and runs the real cli-fixture.js, so it fails under Node with the old "ignore" wiring and passes with the fix — exactly the CI-only failure mode described. It's guarded with describe.skipIf(process.platform !== "linux" || !nodeExe()), drains stdout/stderr/exited concurrently, and asserts a combined object. generate.ts uses only strippable TS (interfaces, annotations, as) and node:-prefixed imports, so it loads cleanly under Node's type-stripping. No CODEOWNERS cover these paths, no bugs were flagged by the bug-hunting system, and there are no outstanding reviewer comments.

The stdin test drained stderr and dropped it, so a fixture that threw before
writing anything (a hung workload, a spawn error) failed with a bare
greeted/read mismatch and no reason.

node warns about the fixture's module type on every run, so stderr is never
empty and cannot be asserted whole. Pull out the uncaught error line instead
and assert there isn't one. That also catches generate.ts growing TypeScript
node cannot strip, which breaks the real build the same way.

@claude claude Bot 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.

LGTM — one-line stdin-wiring fix in build tooling, hoisted for testability, with a node-driven regression test that demonstrably fails without the fix.

Extended reasoning...

Overview

This PR touches three files: scripts/orderfile/generate.ts (build tooling for the linux release linker order file), test/js/bun/perf/linker-order.test.ts, and a new 18-line test fixture. The behavioral change is a single expression: stdio[0] becomes options.input === undefined ? "ignore" : "pipe" instead of unconditionally "ignore". The rest is hoisting the previously-local run() helper to module scope as an exported runCommand() (it captured nothing from its closure) so the test can drive it under Node, plus renaming the five call sites.

Security risks

None. This is a build-time script that runs spawnSync on fixed local commands (cc, nm, the built bun-profile binary) during release builds. No user-controlled input reaches the command array, and the change only affects whether a pipe is opened for stdin.

Level of scrutiny

Low. This is build tooling under scripts/, not shipped runtime code. The worst-case failure mode is that the orderfile generation misbehaves on CI, which build.ts already catches and annotates rather than failing the build. The fix follows the pattern the PR description confirms is already used at three other repo call sites (scripts/utils.mjs, scripts/build/fetch-cli.ts).

Other factors

The PR description includes empirical before/after output under both runtimes, and the new test spawns node --experimental-strip-types on the actual runCommand + cli-fixture.js to prove stdin reaches the workload — with a shown failing diff when the fix is reverted. The one CodeRabbit nit (surface stderr in the failure diff) was addressed in commit 432cb95 via the crash field, and the thread is resolved. No CODEOWNERS cover these paths, and the bug hunter found nothing.

darwin-26-aarch64 test-bun failed downloading the build artifact from the
agent (120s timeout, zero tests run). Its sibling shard on the same platform
and the same artifact passed, as did all 285 other jobs.

@claude claude Bot 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.

LGTM — one-line stdin wiring fix in build tooling with a well-designed Node-driven regression test.

Extended reasoning...

Overview

This PR touches three files: scripts/orderfile/generate.ts (build tooling for the linker symbol-ordering file), test/js/bun/perf/linker-order.test.ts (adds one test case), and a new 18-line test fixture. The functional change is a single conditional: stdio: [options.input === undefined ? "ignore" : "pipe", ...], fixing Node's spawnSync silently dropping input when stdin is "ignore". The rest is a mechanical hoist of the local run() helper to an exported runCommand() (5 call sites renamed, no other logic change) so the test can drive it under Node — the only runtime where the bug is observable.

Security risks

None. This is a build-time script that runs on CI/locally, spawning known toolchain commands (cc, nm, the built bun-profile) against files it created itself. No user input, no network, no auth, no crypto.

Level of scrutiny

Low. This is not shipped runtime code — it generates a linker input file during release builds. The worst-case failure mode is that orderfile generation continues to behave as it did before (interactive workloads still miss their ~2k tty/readline symbols), or the new test is flaky/skipped. Neither affects the correctness of the shipped binary. The fix follows the pattern already used elsewhere in the repo (scripts/utils.mjs per the PR description), and the condition correctly gates on presence (=== undefined) rather than truthiness.

Other factors

The PR description includes empirical before/after evidence under both runtimes and shows the new test fails with the fix reverted. The one CodeRabbit nit (surface stderr in the failure diff) was addressed in 432cb95 with a better solution than suggested — a falsifiable crash regex extraction rather than expect.any(String) — and the thread is resolved. The test correctly drains pipes concurrently, uses a combined-object assertion, and skips on non-Linux / when Node is unavailable. No outstanding review comments.

@robobun

robobun commented Jul 5, 2026 •

Copy link
Copy Markdown
Collaborator Author

CI is red on darwin only, and it is not this diff. I have already spent my one retrigger, so this needs a maintainer.

Two builds of identical code (the second is 432cb95 plus an empty commit) failed on three different darwin lanes, three different ways, and the failures move between builds:

build sha darwin 26 aarch64 darwin 14 x64 darwin 14 aarch64
68475 432cb95 artifact download timed out after 120s, zero tests ran pass pass
68517 1e39eed pass creates subprocess with terminal attached hung the full 90s step timed out (255)

Everything else is green in both, 285 and 284 jobs: every linux lane, every windows lane. Nothing outside darwin has failed once.

The hang is test/js/bun/terminal/terminal.test.ts:998. It awaits a gotMarker promise that only ever resolves from the pty data callback, with nothing wired to reject it, so a macOS pty slow to deliver leaves it waiting until the runner kills the file. Bun.Terminal is native code this PR does not touch.

Nothing here can reach darwin in the first place: generateOrderFile() throws off linux, orderFileEligible() is false on pull requests, and the new test along with the pty runner and page tracer blocks are all skipIf(process.platform !== "linux"). On darwin the file is 18 pure-logic assertions that spawn nothing.

Where the change is actually exercised it runs and passes. From the debian 13 x64 shard log:

--- [3/239] test/js/bun/perf/linker-order.test.ts
17 pass
1 skip
0 fail

The 1 skip is refuses to run off linux, which is supposed to skip there. So the new test is running in CI, not quietly skipping.

Ready to merge from my side, with a retry on the darwin jobs.

@Jarred-Sumner
Jarred-Sumner merged commit dfa2810 into main Jul 6, 2026
74 of 77 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the claude/orderfile-workload-stdin branch July 6, 2026 06:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants