Skip to content

fix(cli): answer outputStreamsShareDevice from the fds so split terminals keep the stdout mirror - #30060

Merged
wmadden-electric merged 2 commits into
mainfrom
fix/output-streams-share-device
Aug 18, 2026
Merged

fix(cli): answer outputStreamsShareDevice from the fds so split terminals keep the stdout mirror#30060
wmadden-electric merged 2 commits into
mainfrom
fix/output-streams-share-device

Conversation

@wmadden-electric

@wmadden-electric wmadden-electric commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

What this closes

The last piece of the cli-engine 0.1.1 stream-suppression adoption. The engine drops a command's stdout payload when both output streams are TTYs, unless the host says they are separate devices via Runtime.outputStreamsShareDevice. Our bin adapter never set the field, so stdout and stderr routed to two different terminals were still treated as one screen and the machine-readable stdout copy was dropped. (#30057 fixed the data-loss half — every payload now has a human mirror — this fixes the over-suppression half.)

The fix

runtimeFromProcess now compares the device and inode behind the two streams' fds (fstatSync) and answers the field:

  • same file identity → true: one screen, mirror suppressed, human copy shown — unchanged behavior
  • different identities → false: two sinks, both copies delivered
  • no fd on a stream, or fstat fails → field stays absent and the engine keeps its one-screen default, which is the safe common case

This also pre-empts prisma/prisma-cli#198, which makes the field required — when the next engine version lands, this adapter already answers.

Tests

Four new cases in packages/1-framework/3-tooling/cli/test/orm/cli.test.ts driving runtimeFromProcess with real fds: same file twice → true, two files → false, fd-less streams → absent, unstat-able fd → absent.

Verified locally: cli package suite (1425 tests), pnpm typecheck, pnpm lint — all green.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Runtime detection now reports whether standard output and error streams share the same device when this can be determined.
  • Bug Fixes
    • Improved handling of missing or invalid output stream descriptors by returning an indeterminate result instead of failing.
    • Improved configuration loading across environments with more reliable module resolution.
  • Tests
    • Added coverage for shared devices, separate devices, unavailable or invalid stream descriptors, and configuration loading.

@wmadden-electric
wmadden-electric requested a review from a team as a code owner August 18, 2026 10:41
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 23522be6-0e16-4d65-844e-e5aa813a404a

📥 Commits

Reviewing files that changed from the base of the PR and between 8f059fb and d83c6ae.

📒 Files selected for processing (1)
  • packages/1-framework/3-tooling/config-loader/src/load.ts

Included review availability: Your plan includes up to 10 reviews per rolling hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The CLI detects whether stdout and stderr share a device and inode. The config loader resolves c12 through its canonical path before dynamic import. Tests cover stream identity outcomes.

Changes

Output stream device detection

Layer / File(s) Summary
Stream identity runtime detection
packages/1-framework/3-tooling/cli/src/orm/cli.ts
The runtime reads stream descriptors with fstatSync, compares device/inode identities, and exposes outputStreamsShareDevice.
Stream identity test coverage
packages/1-framework/3-tooling/cli/test/orm/cli.test.ts
Tests validate shared, distinct, missing, and invalid descriptors.
Configuration loader resolution
Layer / File(s) Summary
Canonical c12 dynamic loading
packages/1-framework/3-tooling/config-loader/src/load.ts
The loader resolves c12 with createRequire, canonicalizes its entry path with realpathSync, and dynamically imports the resulting file URL.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to d83c6

This change corrects stdout mirroring when output streams target different terminals, while preserving the existing single-screen behavior; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers: aqrln

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: determining output stream identity from file descriptors to preserve split-terminal output behavior.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/output-streams-share-device

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@pkg-pr-new

pkg-pr-new Bot commented Aug 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

@prisma/orm-extension-arktype-json

npm i https://pkg.pr.new/@prisma/orm-extension-arktype-json@30060

@prisma/orm-extension-middleware-cache

npm i https://pkg.pr.new/@prisma/orm-extension-middleware-cache@30060

@prisma/orm-extension-paradedb

npm i https://pkg.pr.new/@prisma/orm-extension-paradedb@30060

@prisma/orm-extension-pgvector

npm i https://pkg.pr.new/@prisma/orm-extension-pgvector@30060

@prisma/orm-extension-postgis

npm i https://pkg.pr.new/@prisma/orm-extension-postgis@30060

@prisma/orm-extension-supabase

npm i https://pkg.pr.new/@prisma/orm-extension-supabase@30060

@prisma/orm-family-mongo

npm i https://pkg.pr.new/@prisma/orm-family-mongo@30060

@prisma/orm-family-sql

npm i https://pkg.pr.new/@prisma/orm-family-sql@30060

@prisma/orm-framework

npm i https://pkg.pr.new/@prisma/orm-framework@30060

@prisma/orm-mongo

npm i https://pkg.pr.new/@prisma/orm-mongo@30060

@prisma/orm-postgres

npm i https://pkg.pr.new/@prisma/orm-postgres@30060

@prisma/orm-sqlite

npm i https://pkg.pr.new/@prisma/orm-sqlite@30060

@prisma/orm-target-mongo

npm i https://pkg.pr.new/@prisma/orm-target-mongo@30060

@prisma/orm-target-postgres

npm i https://pkg.pr.new/@prisma/orm-target-postgres@30060

@prisma/orm-target-sqlite

npm i https://pkg.pr.new/@prisma/orm-target-sqlite@30060

@prisma/orm-toolchain

npm i https://pkg.pr.new/@prisma/orm-toolchain@30060

commit: d83c6ae

@github-actions

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size
postgres / no-emit 172.72 KB (0%)
postgres / emit 149.89 KB (0%)
mongo / no-emit 101.15 KB (0%)
mongo / emit 91.01 KB (0%)
cf-worker / no-emit 197.16 KB (0%)
cf-worker / emit 171.8 KB (0%)

…nals keep the stdout mirror

The engine suppresses the stdout payload when both output streams are TTYs
unless the host says they are separate devices. The bin never answered, so
stdout and stderr routed to two different terminals were still treated as
one screen and the mirror dropped. runtimeFromProcess now compares the
device and inode behind fds 1 and 2 and sets the field; where a stream
exposes no fd or cannot be stat-ed the answer stays absent and the engine
keeps its one-screen default. Upstream prisma/prisma-cli#198 makes the
field required, so this also pre-empts the next engine adoption.

Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
@wmadden-electric
wmadden-electric force-pushed the fix/output-streams-share-device branch from 8f059fb to f2278b8 Compare August 18, 2026 11:09
… loading survives symlink-preserving resolution

Under pnpm, resolving the bare c12 specifier can pin c12 at its symlinked
node_modules path: Node's synchronous ESM linker (and the resolver state
the CLI process ends up in) skips the realpath step. From the symlink path
c12's own dependencies (dotenv) do not resolve, and every config load dies
with CONFIG.EVALUATION_FAILED before evaluating anything. Which linker
serves the import depends on the bundler's chunk graph, so any unrelated
source change in the cli package could flip it — the previous commit did,
turning five CI checks red while main stayed green by luck.

Resolving the entry with the CommonJS resolver, realpathing it, and
importing that file URL anchors c12 and all its transitive dependencies at
their real on-disk locations in every resolver state.

Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
@wmadden-electric

Copy link
Copy Markdown
Contributor Author

The Integration/E2E/Fixtures/Test failures were real but not what they looked like. The first commit's one-line source change flipped tsdown's chunk graph in the cli package, which changed which of Node's module linkers serves config-loader's import('c12'). The synchronous linker skips symlink realpathing, so under pnpm c12 got pinned at its symlinked path where its own dotenv dependency cannot resolve — every config load then failed with CONFIG.EVALUATION_FAILED. Main only passes by luck of the current chunk graph; any cli source change could re-trigger this.

The second commit fixes the class: config-loader now resolves c12's entry with the CommonJS resolver, realpaths it, and imports that file URL, anchoring c12 and its transitive deps at their real locations in every resolver state. Verified locally: repeated contract emit runs green at branch tip, config-loader + cli package suites, the previously failing integration suites, typecheck, lint.

@wmadden-electric
wmadden-electric added this pull request to the merge queue Aug 18, 2026
Merged via the queue into main with commit 2f7444a Aug 18, 2026
18 checks passed
@wmadden-electric
wmadden-electric deleted the fix/output-streams-share-device branch August 18, 2026 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants