Allow ordinary static stderr redirection on one streaming stage. Multiple redirected stages and descriptor aliases keep the existing buffered executor to preserve ordering.
Draft while #453 remains unmerged. This branch is rebased onto current main with the shared prerequisite fixes. Focused follow-up diff. The full GitHub diff includes the prerequisite; it should be rebased again once that prerequisite lands.
Validation: focused behavior tests, TypeScript, scoped Biome, knip and banned-pattern checks pass. No manual workflow rerun or review-bot request was made.
Activity
13🤖 auto-maintain review
Automated, advisory triage for @asafyish's PR. Facts below are read from the GitHub API.
| Check | Result |
|---|---|
| Author's merged PRs (this repo) | 1 |
| Account established | ✅ (age 5392d · 13 followers · 7 public repos) |
| Commits signed/verified | ⚠️ 0/12 |
| Changeset included | ✅ (.changeset/streaming-pipelines.md) |
Review panel: 🟡 medium highest severity
just-bash maintainer code review: 🟡 medium
Streaming mixed file/stdin operands in cat has an ordering and early-termination regression.
- packages/just-bash/src/commands/cat/cat.ts:124 — `cat file -` eagerly drains all piped stdin before reading `file`. In a streaming pipeline such as `seq 1000000 | cat /file - | head -1`, this can hit execution limits before emitting `/file`, whereas Bash emits the file first and terminates the producer early. Process operands in order and stream `-` when reached.
General code review: 🟢 low
No actionable correctness issues found in the complete base-to-head diff.
Adversarial security: 🟢 low
No actionable adversarial security issues found in the complete diff.
Adversarial security (second opinion): 🟡 medium
No backdoors, exfiltration, or sandbox-escape issues; the trusted-scope isolation fix for concurrent stages is correct. One actionable gap: the new fd-2 redirection gate admits /dev/stdout and /dev/full, which the redirection layer treats as fd duplication rather than ordinary file targets.
- packages/just-bash/src/interpreter/streaming-pipeline.ts:78 — The fd-2 streaming gate only filters on operator/fd/static-word, so pseudo-device targets reach it. `2>/dev/stdout` is handled by redirections.ts as fd duplication (`fd2Sink = fd1Sink`), not a file open — the stage's stderr lands in `result.stdout` and is pushed into the pipe after everything the stage already sent via `ctx.stdio.write()`, so the merged stream is reordered versus bash, and stderr crosses into the next stage's stdin even though the gate deliberately rejects the equivalent `2>&1`. `2>/dev/full` likewise slips through and synthesizes a bogus `bash: echo: write error` plus exitCode 1 on a streaming stage. Exclude `/dev/stdout`, `/dev/stderr`, and `/dev/full` from the allowed targets (as `/dev/null` is already special-cased downstream).
Standard Bash and host portability: 🟢 low
No actionable Standard Bash or host-portability issues found.
Posted by auto-maintain. This automated code review is advisory; a human maintainer makes the call.
Fixed the documentation mismatch in b102a46b: static stderr targets (2>, 2>>, 2>|) are explicitly documented as streaming-eligible. The shared concurrent-trust finding is also reproduced and fixed by dba7513b, using the existing untrusted-context wrapper; the foundation fix is described on #453. All 41 focused stderr/pipeline tests pass.
The mixed-input cat observation is real, but it belongs to the unchanged #453 foundation. With /first containing first\n, I executed seq 1000000000 | cat /first - | head -n 1 on both 262a17b2 and this PR. Both return empty stdout and the same seq iteration-limit error (exit 126). This fd-2 eligibility change does not introduce it. Adding ordered file streaming to cat would expand the PR beyond stderr redirection, so I have flagged that as separate foundation work pending scope approval.
Verified and fixed the stderr-overlap finding in 00cde91c. A deterministic reproduction made the second stage append first, then let the first stage complete: its deferred truncate erased the appended text. The streaming eligibility gate now falls back to the existing ordered executor when more than one stage redirects stderr. This also covers aliased paths without adding path-resolution or descriptor machinery. One regression exercises the failure; single-stage stderr redirection keeps streaming.
The browser export finding is also real. 17873a3c exports the existing byte helpers and types, matching index.ts. The browser bundle builds, and a custom streaming producer imported from that bundle successfully emits UTF-8 text through cat.
The maxArrayElements concern conflates retained arrays with processing work. Its documented contract is “Maximum array elements”; streaming seq does not accumulate the results array. It still enforces maxLoopIterations and charges execution-wide work. For example, with maxArrayElements: 2, seq 10 | rg --no-line-number . streams ten lines with maxLoopIterations: 100, but fails at maxLoopIterations: 5. Adding an array-size restriction to this bounded streaming path would not enforce an allocated-array invariant, so I have not made that change.
Also carried the shared deferred-getter protection from #642 (d89a36ca here). The mixed-file/stdin cat finding remains a shared #453 issue.
106 focused pipeline, redirection, and seq tests pass, as do typecheck, banned-pattern checks, formatting, and the browser-bundle smoke test.
Verified and fixed descriptor aliases: static /dev/stdout, /dev/stderr and /dev/full targets use the buffered executor, just like the equivalent descriptor-duplication forms. Reused expandWord for static target expansion; one regression compares /dev/stdout and /dev/stderr with 2>&1 and 2>&2. The shared cat operand-order fix is included too. Rebased onto current main without conflicts; 165 pipeline/redirection checks passed, plus typecheck, formatting, knip and banned-pattern checks.
This pull request is a draft
Merging is unavailable until it is marked ready for review.
@asafyish is attempting to deploy a commit to the Vercel Labs Team on Vercel.
A member of the Team first needs to authorize it.