Composer's four commands become part of the prisma CLI - #152
Conversation
The prisma bin imports composer's family directly (contract S3, "The prisma bin imports the family directly"), so `prisma composer deploy | destroy | dev | log` run composer's own handlers in the prisma process. The family arrives from @prisma/composer's dedicated ./family entrypoint, as a production dependency of the published CLI, pinned to the pkg.pr.new preview of composer#220 until the tandem release. Everything the family needs was already on the Runtime the bin assembles: streams, env, cwd, exit, signal subscription, the node:child_process spawn adapter, the credential manager and the disk config loader. The one junction worth a test of its own is the config section, which is the only piece where a mount can be wrong without anything failing to construct: a test drives the real tree with a prisma.config.ts whose `composer` section names a path, and asserts the path reaches composer's handler. The published package's node floor moves to >= 24, composer's floor, because it now depends on composer. @prisma/cli-engine's floor does not move: composer consumes the engine and must not re-inherit its own floor through it. The repo's own toolchain moves with the package, since its tests import the family. The mount's price is measured rather than asserted. A fresh process runs `--version` through the real bin with a module loader hook recording every module it evaluates: composer's family module loads, nothing from alchemy or effect does, and no signal listener survives the run. The canary is the same probe plus the one dynamic import a composer command makes, which loads 468 constellation modules and installs alchemy's import-time SIGINT and SIGTERM handlers — the detector is not vacuous, and what it is detecting is real. Records: the S3 divergence file enumerates what a prisma-composer user sees change; the 1c brief is closed with a disposition per deliverable; ledger Q2 and two coverage-ledger rows are corrected where S3 falsified them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
|
Warning Review limit reached
Next review available in: 35 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
Summary by CodeRabbit
WalkthroughThe V8 CLI now mounts Composer 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
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. Comment |
commit: |
Three conflicts, all resolved by keeping both sides: - packages/cli/src/v8/cli.ts: main added the engine's telemetry command group and switched createCli to CLI_NAME; this branch added the composer family, its group, and its four mounted commands. Neither touches the other's lines. - .drive/projects/prisma-cli-v8/deferred.md: this branch had already rewritten the --tail item to record that D4 widened the divergence entry instead, so its version supersedes main's older wording of the same item; main's three new items are kept. - pnpm-lock.yaml: regenerated from main's lockfile with the composer dependency still resolving to the preview build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
@prisma/composer moves from the pkg.pr.new preview of composer#220 to the published 0.6.0-dev.15. The published package carries the family: ./family exports createComposerFamily, and the family it builds has all four commands plus the composer config section. The workspace entries the preview needed are still needed, for reasons that have nothing to do with previews: - msgpackr-extract and workerd still arrive through composer's own dependency graph (composer -> alchemy -> cloudflare/effect), so their allowBuilds entries stay. - @prisma/cli-engine is still reached through composer, which pins it exactly, and 0.0.7 is younger than the minimumReleaseAge cutoff, so its exclude entry stays. Removing it fails the install outright. The swap adds one entry rather than removing any: a registry version has a publish time, so composer itself now falls under minimumReleaseAge. pnpm wrote it version-pinned; it is recorded at the package level here, matching the policy the surrounding comment already states. Mounting the four composer commands trips the e2e coverage requirement main added, so each is recorded in EXCLUSIONS with why the real API cannot drive it: deploy and destroy provision and tear down real cloud infrastructure, and dev and log are session commands that run until a signal. The TODO(release) note now states what actually remains. It is no longer about the preview pin, which is gone; it is that composer pins engine 0.0.7 while this package ships the workspace engine at 8.0.0-rc.1, so an install resolves two copies. That is composer's move to make, not this repo's. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…ses on Windows The test failed on windows-latest only, and not for the reason the truncated assertion suggested. The full job log shows what the run actually printed: LOG.PLATFORM_UNSUPPORTED, "local dev is not supported on Windows yet". composer's log operation refuses Windows on its first statement, before it looks at the config section it was handed, so the section never reaches config discovery there. dev refuses the same way, and deploy and destroy stop earlier still at the credential check, so no shipped composer command can show the section arriving on Windows. So the test now proves the whole path on every other platform, and on Windows proves what is left to prove: the bin evaluated the config file, the engine accepted its `composer` section, and the command reached composer's own operation. What Windows no longer proves is that the path inside the section is the one composer acts on. When composer supports Windows the second test fails and the two collapse back into one. The old assertion also had two faults that would have surfaced the moment composer ran there, both fixed by reading the terminal result frame instead of substring-matching raw stdout: the stream is JSON, which escapes every separator in a Windows path, and `join` on a rooted POSIX string drops the drive letter composer's own `resolve` adds. The file already used `resolve` for this 250 lines up. Verified by running the suite on POSIX. Both Windows halves can only be confirmed by CI; the Windows assertion was checked against the exact stdout the failing job produced. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 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 @.drive/projects/prisma-cli-v8/assets/s2/parity-divergences-s3.md:
- Line 69: Update the fenced code block in parity-divergences-s3.md to include
an explicit language identifier, such as text, immediately after the opening
fence so it satisfies markdownlint MD040.
- Around line 35-47: Implement the coordinated fix across the engine and
Composer: add `{command}` expansion using the command’s mounted path, then
update all four Composer help example strings to use `{command}` instead of
`{bin}` while preserving their existing arguments. Ensure `prisma composer
deploy --help` renders the mounted `prisma composer deploy` invocation, without
changing unrelated help text or Composer’s standalone CLI behavior.
- Around line 252-259: In the Prisma-bin execution path, wire pipeline.ts to use
loadAppConfigDiagnostics() so DEPS.EFFECT_VERSION_CONFLICT can be surfaced
without the throwing loadAppConfig path, and add an integration test that
reaches this diagnostic. Update
.drive/projects/prisma-cli-v8/assets/s2/parity-divergences-s3.md lines 252-259
to remove the parity claim until covered; update
.drive/projects/prisma-cli-v8/plan.md line 176 to stop counting Composer
validator and unknown-key diagnostics as proven until the integration coverage
exists.
In @.drive/projects/prisma-cli-v8/deferred.md:
- Around line 7-11: Update the ownership heading in deferred.md to reflect that
remaining work spans both the Composer and Prisma CLI repositories, or assign
repository ownership separately for each item; specifically identify the help
fix and engine pin as Prisma CLI-owned while preserving Composer ownership for
the applicable items.
- Around line 24-31: Update the tandem release validation around the engine pin
and workspace dependency so shipping two `@prisma/cli-engine` copies is blocked
unless compatibility is verified. Add coverage for execution and signal behavior
across the resolved engine copies, or explicitly make that compatibility check a
release-blocking requirement; retain the existing structured-error coverage.
- Around line 126-138: Keep Composer signal handling out of the Prisma bin by
ensuring the patched `@alchemy.run/node-utils` is delivered through the published
dependency chain or isolating its import-time handlers. Update the
`@prisma/composer/deploy` integration so SIGINT and SIGTERM listeners cannot
bypass engine abort, child teardown, or settlement paths for dev and log. Extend
the prisma bin canary in v8-composer-isolation.test.ts to assert listener
ownership and cleanup, including no listeners for --version and proper removal
after Composer execution.
In @.drive/projects/prisma-cli-v8/plan.md:
- Line 175: Update the Composer child-command contract described in the S3 plan
to guarantee token validity for the entire child run: preferably hand off
refresh capability, or enforce and test a maximum run duration with a safety
margin; if neither is implemented, document the release limitation with an
explicit operational bound and ensure the near-expiry refusal reflects it.
In `@packages/cli/package.json`:
- Line 50: Update the `@prisma/composer` dependency in packages/cli/package.json
to a Composer release that depends on the workspace
`@prisma/cli-engine`@8.0.0-rc.1 contract, replacing 0.6.0-dev.15 so the
declarations include installsPackages and typecheck succeeds.
In `@packages/cli/src/v8/cli.ts`:
- Around line 222-225: Update the command documentation in README.md and
packages/cli/README.md to include the Composer command group with composer
deploy, composer destroy, composer dev, and composer log, and add at least one
usage example. Keep the documented commands aligned with
composerCommandFamily.commands.
- Around line 9-16: Resolve the engine version mismatch before merging by
updating the Composer dependency to pin `@prisma/cli-engine` at 8.0.0-rc.1, then
verify the packed `@prisma/cli` resolves a single engine copy and
createComposerFamily uses that version. If Composer cannot be republished, add
and test an explicit compatibility adapter for its family API.
🪄 Autofix
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: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 313d753e-6aaf-48e1-847d-66d05f01e518
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (22)
.drive/projects/prisma-cli-v8/assets/briefs/1c-leftovers-composer.md.drive/projects/prisma-cli-v8/assets/s2/parity-divergences-s2c.md.drive/projects/prisma-cli-v8/assets/s2/parity-divergences-s3.md.drive/projects/prisma-cli-v8/deferred.md.drive/projects/prisma-cli-v8/plan.md.drive/projects/prisma-cli-v8/specs/s2-overview.md.node-versionCONTRIBUTING.mdREADME.mddocs/onboarding/getting-started.mdpackage.jsonpackages/cli/README.mdpackages/cli/package.jsonpackages/cli/src/v8/cli.tspackages/cli/tests/e2e-coverage.test.tspackages/cli/tests/fixtures/v8-config/composer-section.config.tspackages/cli/tests/fixtures/v8-startup-probe.mjspackages/cli/tests/v8-bin.test.tspackages/cli/tests/v8-composer-isolation.test.tspackages/cli/tests/v8-mount-coverage.test.tspackages/cli/tests/v8-spawn-adapter.test.tspnpm-workspace.yaml
No conflicts. Main's only new commit (#154) adds the skills install suggestion to the auth success page, touching packages/cli/src/auth/login.ts and packages/cli/tests/auth-login.test.ts; this branch touches neither. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
composer 0.6.0-dev.16 pins @prisma/cli-engine 0.0.9, the first published engine carrying `installsPackages` on CommandDefinition. That is what the mount in packages/cli/src/v8/cli.ts needs, so `pnpm typecheck` goes from failing to clean with no change to the mount itself. dev.16 also drops composer's own Node requirement from >=24 to >=22.18.0, so @prisma/cli's floor, raised to >=24 only because composer demanded it, comes back down to >=22.18.0 to match composer exactly. packages/cli's README follows it. The repo's own toolchain stays on Node 24, and that is not an oversight. Moving .node-version back to 22.22.3 turned two CI jobs red — the jobs that read it, Test in pr-quality and preview — while the jobs pinned to node-version: 24 stayed green on the same commit. Both failures were the same test: the canary in v8-composer-isolation, whose import of composer's deploy executor exits non-zero on Node 22.22.3. So composer's declared floor of 22.18.0 and what its executor actually loads on disagree, and until that is composer's to settle, this repo builds and tests on 24: .node-version, the root engines, and the three contributor docs all say 24, with CONTRIBUTING recording why the published floor is lower. @prisma/cli-engine keeps its own >=22.12.0. composer depends on the engine, so raising the engine's floor would push that floor back onto composer through the dependency. An install still resolves two engine copies, not one: the packed CLI depends on @prisma/cli-engine 8.0.0-rc.1, its own workspace build, while composer pins the published 0.0.9. Two exact pins on two release lines cannot dedupe, so dev.16's move from 0.0.7 to 0.0.9 does not change the count. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
c2cf38c to
5d2c815
Compare
PR #152 recorded that composer's deploy executor fails to load on Node 22, and that composer's >=22.18.0 floor is therefore a false claim. It is not. All 16 published entrypoints of @prisma/composer 0.6.0-dev.16, deploy included, import cleanly in a fresh process on 22.18.0 and 22.22.3. What actually needs Node 24 is this repo's own startup-isolation probe. It spawns node with --import tsx, and tsx bypasses Node's module-syntax detection, so @alchemy.run/node-utils 0.0.5 — ESM in lib/*.js with no "type": "module", which that detection otherwise classifies correctly — stops loading. Separately, the probe's own registerHooks load hook cannot run on 22.18 at all: nextLoad returns source: undefined there, which fails with ERR_INVALID_RETURN_PROPERTY_VALUE. Neither cause is composer's. CONTRIBUTING now says composer runs fine on 22.18 and points at the isolation test, which carries the tsx and node-utils detail where a contributor running the suite on 22 will actually land. The floors are unchanged: @prisma/cli stays >=22.18.0, @prisma/cli-engine stays >=22.12.0, and .node-version stays 24. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
There was a problem hiding this comment.
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 `@packages/cli/tests/v8-composer-isolation.test.ts`:
- Around line 42-45: Update the Node-version rationale comment in
v8-composer-isolation.test.ts to remove the claim that the probe’s registerHooks
load hook requires Node 24, since registerHooks is available from Node 22.15.
Retain the Node 24 restriction only when supported by the independently
demonstrated tsx or `@alchemy.run/node-utils` loading failure.
🪄 Autofix
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: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 62d36743-5d87-4ea9-bc88-b04abdf21588
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
CONTRIBUTING.mdpackages/cli/README.mdpackages/cli/package.jsonpackages/cli/tests/v8-composer-isolation.test.ts
Nine review threads on #152 checked claims in the project records against the shipping code. Where they disagreed, the code won. Corrected, having re-read the published `@prisma/composer@0.6.0-dev.16` and this repo's engine: - the help-example defect is eight strings, not four (two on each of the four commands), and the rendered `prisma deploy src/service.ts` settles CLI.UNKNOWN_COMMAND; - composer pins `@prisma/cli-engine@0.0.9`, not `0.0.7`, against prisma-cli's workspace `8.0.0-rc.1`, and the entry now names the one crossing that is tested and calls matching pins a release requirement; - the effect-resolution check does run on the shipped path — it sits in `configSource`, the front both loader shapes share — so the deferred entry saying it never runs was the wrong record, not the parity one; - the coverage ledger no longer claims the prisma bin proves composer's validator, its absence case, or its unknown-key warning: the engine's own suite proves that machinery against toy sections, and the bin proves one real section arriving at composer's handler; - the child's credential bound is stated: five minutes at spawn (CREDENTIAL_NEAR_EXPIRY_MS) and nothing limiting the run after it, recorded as a release limitation with the two ways out deferred; - the section heading no longer says all remaining work is composer's, because two of its four items need a change in each repo. The canary in v8-composer-isolation.test.ts now asserts the signal listener counts it was only recording, so the unpatched node-utils leaving one SIGINT and one SIGTERM listener is a fact the suite holds. Its Node-version comment says which failure belongs to which Node: 22.18 rejects the probe's own load hook return value (the registerHooks API itself has been there since 22.15), and 22.22 fails the canary's import under tsx. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
That command works after this change. It is composer's
deploy— the same code composer ships — running inside theprismabinary, with this CLI's help text, argument parsing, error envelopes, exit codes, telemetry and authentication.What this does
Composer is a deployment framework with four commands:
deploy,destroy,devandlog. Until now it had its own CLI. This change makes those commands part ofprisma:There is no second CLI being launched behind the scenes and no duplicated implementation. Composer publishes a command family — a value describing its commands — and this bin mounts it. One process, one grammar, one set of rules about how output and exit codes work.
How it works
packages/cli/src/v8/cli.tsimportscreateComposerFamilyfrom@prisma/composer/familyand adds the result to the CLI's command families, under thecomposergroup. That is the whole mount.Nothing in the host needed changing. A command family expects its host to supply the process facts — input and output streams, environment, working directory, exit, signal handling, a way to spawn a child process, credentials, and config loading — and this bin already supplied all of them for its own commands, in the shapes composer's own CLI constructs.
Verification went through the running binary rather than the type checker:
prisma composer deploy --jsonis refused at parse time, becausedeployhands the terminal to another program and its output cannot be framed.prisma composer log --jsonstill works, because it does not.prisma composer deploywith no credentials stops at the sign-in error before it reads any config.composersection inprisma.config.tsreaches composer's handler. This is the one junction that can be wrong while everything still constructs successfully, so it has a test with a real config fixture.The startup question
Composer's commands ultimately drive Alchemy, which is a large dependency tree that installs its own process-level signal handlers when it loads. Importing composer into this bin must not make
prisma versionpay for any of that.It does not, and the proof is a measurement rather than an argument. A test spawns a fresh process running the real bin body, with a module-loader hook installed before anything else is imported. On
--version: composer's family module is evaluated, no Alchemy or Effect module is loaded, and the process ends with zero SIGINT and zero SIGTERM listeners.The same file contains the canary that keeps the test honest — the identical probe plus the one dynamic import a composer command performs. It loads 468 modules of that tree and installs one SIGINT and one SIGTERM listener. So the first measurement is detecting something real.
What this costs
Two consequences were accepted when this approach was chosen, and both are now concrete:
@prisma/clirequires Node 22.18, matching composer's floor.@prisma/cli-enginedeliberately stays at 22.12: composer depends on the engine, and must not re-inherit a floor through it.prismainstall carries composer's dependency tree, whether or not composer commands are ever run.Records
assets/s2/parity-divergences-s3.mdlists the nine user-visible differences from composer's own CLI, each checked against the running binary. One earlier claim is corrected there: composer's--tail 1.5silently truncated to1rather than rejecting the value.Three project records were also corrected. The
service runquestion is closed the other way than planned — that command was dropped entirely, so this slice built a mechanism for a command that no longer exists. The claim that this slice proves credential refresh during long runs is withdrawn: it proves the opposite case, a static token handed to a child. And the config-section claim is narrowed to the single section this slice actually exercises.Temporary
@prisma/composeris pinned to the published0.6.0-dev.16, which pins@prisma/cli-engine@0.0.9— the first published engine carrying the requiredinstallsPackagesonCommandDefinition. All checks are green.Two loose ends remain, and my earlier claim that composer's release would collapse the first one was wrong. An install still resolves two engine copies: this repo ships its own at
8.0.0-rc.1while composer pins the published0.0.9. Two exact pins on two release lines cannot dedupe. It works because the values crossing between them are matched bySymbol.forrather than by identity, and it collapses only when both sides name the same engine version.Second,
@prisma/clinow declares Node>=22.18.0, matching composer, and composer genuinely works there — all sixteen of its published entrypoints,deployincluded, import cleanly on 22.18.0. The repo itself still develops and tests on 24 because the startup-isolation probe cannot run on 22: it spawns node with--import tsx, which bypasses the module-syntax detection that a transitive dependency relies on, and its own loader hook fails there besides. That is a limitation of the probe, not of the shipped code.Alternatives considered
prismadelegate to it. Composer ships as a library with no binary, so there would be nothing to delegate to, and building one would mean two implementations that drift.prisma version, which is what the startup measurement above exists to prevent.🤖 Generated with Claude Code