feat(cli): programmatic control API — @prisma/composer/control (TML-3174) - #205
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Summary by CodeRabbit
WalkthroughAdded the 🚥 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 |
There was a problem hiding this comment.
Actionable comments posted: 15
🤖 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/0-framework/3-tooling/cli/src/dev/run-dev.ts`:
- Around line 70-72: Update the `converge-failed` handler in the dev watch-loop
switch to read the emitted `stackFilePath`, `reproduceCommand`, and `cwd`
payload and print the stack path and reproduce command, matching the initial
converge-failure output. Preserve the existing message that the running app is
untouched and still watching, and use `cwd` when formatting or executing the
reproduction command as established by the startup failure path.
- Around line 109-128: Update runDev around the signal registration and
session.closed wait to use try/finally, removing the finish listener for both
SIGINT and SIGTERM in the finally block after session.closed settles or exits
early. Preserve the existing finish behavior while ensuring each runDev
invocation does not leave stale listeners behind.
In `@packages/0-framework/3-tooling/cli/src/main.ts`:
- Line 229: Guard the rethrow in the OperationFailure handling path the same way
as the existing line-231 logic: only throw failure.cause when it is an Error,
otherwise create or propagate an Error using failure.message so the formatter
always receives an Error. Keep the surrounding failure handling unchanged.
In `@packages/0-framework/3-tooling/cli/src/operations/execute-deploy-destroy.ts`:
- Around line 254-257: Update the deployment flow around resultFilePath and
readDeploymentSummary to use a unique per-run result filename, preventing
concurrent deploy() calls sharing or deleting each other’s reports. Wrap the
execution and summary-read lifecycle in a try/finally so the unique result file
is removed on success and every execution failure path, including returns before
readDeploymentSummary.
In `@packages/0-framework/3-tooling/cli/src/operations/execute-dev.ts`:
- Around line 266-271: Update the executeDev startup error handler to clean up
all partially started resources before returning the failed outcome. Declare a
watchHandle before the try, assign it from startWatch, then in the catch stop
the watcher when present and call stopServices() for every attachment,
preserving the existing failure response afterward.
- Around line 138-172: Update the converge helper to accept a pipeline argument
and use that argument for writeDevStackFile instead of the outer pipeline. Pass
the initial pipeline to the first converge call, and replace the watch-loop’s
duplicated stack-writing and runAlchemy logic with converge(rePipeline),
preserving the existing return and failure handling.
- Around line 206-240: Update the rebuild callback in startWatch to maintain a
single in-flight rebuild promise, preventing overlapping runPipeline,
writeDevStackFile, and deploy operations; queue or skip subsequent triggers
until the current rebuild completes. Track the dev session’s stopping/stopped
state and cancel or skip callbacks that begin after shutdown starts, including
already scheduled work. Before each writeDevStackFile, deploy/converge action,
and onEvent call, recheck that state so no stack writes, deploys, or events
occur after stopping begins.
- Around line 248-262: Update the shutdown logic in stop so host-supplied
onEvent callbacks cannot interrupt cleanup or leave closed unresolved: isolate
both stopping and stopped event calls from thrown exceptions, and place
resolveClosed() in a finally block that always executes after
attachment.stopServices() processing. Preserve the existing stopping guard and
closed return behavior.
In `@packages/0-framework/3-tooling/cli/src/operations/execute-log.ts`:
- Around line 38-56: Bound the shared queue used by the attachment pumps so it
cannot grow without limit when the consumer is slow. Update the queue production
and consumption flow around the attachment `logs` pumps and `notify` to apply
backpressure once the configured capacity is reached, ensuring producers resume
as items are consumed; alternatively, explicitly drop oldest entries and report
each drop through `onEvent`.
- Line 98: Update the local services declaration in the execute-log operation to
use readonly DevEndpoint[] instead of an inline object shape, reusing the
existing DevEndpoint symbol so changes to that type are enforced here.
- Around line 16-24: Remove toCliError and update the execute operation’s inner
catch blocks to rethrow the original error instead of wrapping it, so the outer
failure retains the original value as failure.cause. Keep failureMessage for
deriving the failure message only, preserving the existing message behavior.
- Around line 67-83: Update the async log generator’s cleanup around the
`finally` block to derive an internal abort signal from `input.signal`,
preserving propagation of the caller’s abort state. Abort the internal signal
before awaiting `Promise.all(pumps)` so early termination through `break`,
`return`, or `lines.return()` causes all pumps to exit and cleanup to resolve.
- Around line 113-148: Ensure every attachment created in executeLog is cleaned
up on pipeline failures, empty-service early returns, and invalid input returns;
retain them through normal log consumption. Update the attachment lifecycle
around target.attach and the services validation branches to invoke the existing
cleanup API, or add a close/dispose/detach mechanism (alternatively propagate
input.signal when creating subscriptions) so abandoned LocalTargetAttachment
followers cannot remain active.
In `@packages/0-framework/3-tooling/cli/src/render-deployment.ts`:
- Around line 149-152: Update deploymentReport’s summary-writing block to create
the parent directory for DEPLOYMENT_RESULT_FILE_ENV before writing, using
node:path to derive it, and wrap directory creation and fs.writeFileSync in
error handling that swallows failures. Preserve successful summary serialization
while ensuring report-hook write errors never escape.
In `@skills/prisma-composer/SKILL.md`:
- Around line 693-697: Update the dev and log result documentation in SKILL.md
to describe their outcome discriminants, matching the existing deploy
documentation. Identify the success and failure branches of DevStartResult and
LogResult, and state that session/services/lines are available only for the
successful outcome so callers handle failures before destructuring.
🪄 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: 435a7704-0c0d-4927-9cc6-6cbaeb5474fa
📒 Files selected for processing (26)
architecture.config.jsondocs/design/10-domains/deploy-cli.mddocs/design/90-decisions/ADR-0043-the-control-subpath-is-the-programmatic-deploy-surface.mddocs/design/90-decisions/README.mddocs/guides/deploying.mdpackages/0-framework/3-tooling/cli/package.jsonpackages/0-framework/3-tooling/cli/src/__tests__/render-deployment.test.tspackages/0-framework/3-tooling/cli/src/dev/run-dev.tspackages/0-framework/3-tooling/cli/src/exports/control.tspackages/0-framework/3-tooling/cli/src/log/run-log.tspackages/0-framework/3-tooling/cli/src/main.tspackages/0-framework/3-tooling/cli/src/operations/__tests__/operations.test.tspackages/0-framework/3-tooling/cli/src/operations/execute-deploy-destroy.tspackages/0-framework/3-tooling/cli/src/operations/execute-dev.tspackages/0-framework/3-tooling/cli/src/operations/execute-log.tspackages/0-framework/3-tooling/cli/src/operations/operations.tspackages/0-framework/3-tooling/cli/src/operations/results.tspackages/0-framework/3-tooling/cli/src/render-deployment.tspackages/0-framework/3-tooling/cli/tsdown.config.tspackages/9-public/composer/package.jsonpackages/9-public/composer/src/exports/control.tspackages/9-public/composer/tsdown.config.tsscripts/check-npm-effect-resolution.mjsskills/prisma-composer/SKILL.mdtest/integration/test/control.deploy.test.tstsconfig.depcruise.json
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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
`@docs/design/90-decisions/ADR-0043-the-control-subpath-is-the-programmatic-deploy-surface.md`:
- Line 5: Update the control subpath contract near the description of the four
operations to distinguish CLI rendering from inherited Alchemy output: state
that operations do not render CLI output themselves but may expose Alchemy
output when configured with stdio: 'inherit'. Keep the existing
structured-input/output and no-argv/process.exit guarantees unchanged.
- Line 44: Update ADR-0043 to explicitly define the programmatic surface’s cwd
contract: deploy(), destroy(), and dev() must use the target app directory,
including when cwd is omitted, before calling checkEffectResolution(cwd). Align
the examples and result-type description with this target-directory behavior,
including where .prisma-composer/ and .alchemy state are located.
🪄 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: efd36683-40fa-44dc-902e-e6c94d398a78
📒 Files selected for processing (1)
docs/design/90-decisions/ADR-0043-the-control-subpath-is-the-programmatic-deploy-surface.md
|
Round 2 pushed (12 commits since the last review): a full second-round review (architect + principal-engineer passes, CodeRabbit triage, and @wmadden's comments) drove this batch. @wmadden's review comments:
Contract neutralization (keeping the execution mechanism out of the exported types): spawn-shaped fields ( Probe-confirmed contract breaks fixed: CodeRabbit items: 10 accepted and fixed across Also: 🤖 Generated with Claude Code |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/0-framework/3-tooling/cli/src/operations/execute-deploy-destroy.ts (1)
217-249: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy liftUse a per-run generated stack file for concurrent operations.
writeStackFilestill writes only.prisma-composer/alchemy.run.ts, andstackFileRelativePath: GENERATED_STACK_RELATIVE_PATHpasses that fixed path to the Alchemy child. If two deployments/destroys target the samecwd, a later run can overwrite the generated stack before an earlier child reads it, so the earlier command can act on another run’s stack program. Generate a per-run stack path as well, and pass that path asstackFileRelativePath.🤖 Prompt for 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. In `@packages/0-framework/3-tooling/cli/src/operations/execute-deploy-destroy.ts` around lines 217 - 249, The deployment flow currently uses the shared GENERATED_STACK_RELATIVE_PATH, allowing concurrent runs to overwrite each other’s stack files. Update the stack-generation call around writeStackFile to create a unique per-run relative stack path, use it when writing the stack, and pass the same path as stackFileRelativePath in the deps.alchemy/runAlchemy invocation; keep result-file isolation unchanged.
🤖 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/0-framework/3-tooling/cli/src/dev/run-dev.ts`:
- Around line 54-87: Update the event switch in the onEvent handler to add a
stop-error case that logs event.message to the console with the existing [dev]
prefix, alongside the stopping and stopped cases. Preserve the current
stopped-event output while ensuring service stop failures are surfaced.
In `@packages/0-framework/3-tooling/cli/src/log/run-log.ts`:
- Around line 41-45: Update the onEvent callback to handle events with kind
`lines-dropped` by emitting a console.error warning, alongside the existing
`stream-failed` handling. Include the event’s available message or dropped-line
details in the warning.
In
`@packages/0-framework/3-tooling/cli/src/operations/__tests__/operations.test.ts`:
- Around line 1158-1186: Update the test around the async generator in “no event
is delivered after the merged iterable has ended” to assert that failLate is
defined before invoking it, ensuring the late-failure path is actually
exercised. If breaking after the first line prevents the generator from reaching
that assignment, advance the generator once more before breaking while
preserving the test’s expected empty events result.
- Around line 1125-1156: Update the test around logWithDeps to derive TOTAL from
the log module’s exported queue-bound constant, using a value comfortably above
that bound so the 20 ms stall reliably causes drops; preserve the existing
droppedTotal and conservation assertions. Export the bound from the log module
if it is not currently public.
In `@packages/0-framework/3-tooling/cli/src/operations/execute-deploy-destroy.ts`:
- Around line 314-323: Ensure every execution path in the operation that creates
resultFilePath removes the result file, including execution failures and
teardown failures, not only the successful deploy path. Reuse the existing
cleanup helper or centralize cleanup in a finally block spanning the result-file
assignment through all returns, while preserving the returned success and
failure values.
In `@packages/0-framework/3-tooling/cli/src/operations/execute-log.ts`:
- Around line 66-68: Update the emit callback in the log streaming operation to
catch and isolate errors thrown by input.onEvent?. Ensure handler failures do
not propagate through pump() or reject the public lines iterable, while
preserving the existing done guard and event delivery behavior.
---
Outside diff comments:
In `@packages/0-framework/3-tooling/cli/src/operations/execute-deploy-destroy.ts`:
- Around line 217-249: The deployment flow currently uses the shared
GENERATED_STACK_RELATIVE_PATH, allowing concurrent runs to overwrite each
other’s stack files. Update the stack-generation call around writeStackFile to
create a unique per-run relative stack path, use it when writing the stack, and
pass the same path as stackFileRelativePath in the deps.alchemy/runAlchemy
invocation; keep result-file isolation unchanged.
🪄 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: 6608894c-11a7-47a9-a096-52bbee179c57
📒 Files selected for processing (24)
docs/design/90-decisions/ADR-0043-the-control-subpath-is-the-programmatic-deploy-surface.mddocs/design/90-decisions/README.mddocs/guides/deploying.mdpackages/0-framework/3-tooling/cli/src/__tests__/render-deployment.test.tspackages/0-framework/3-tooling/cli/src/cli-error.tspackages/0-framework/3-tooling/cli/src/deployment-summary.tspackages/0-framework/3-tooling/cli/src/dev/run-dev.tspackages/0-framework/3-tooling/cli/src/dev/watch.tspackages/0-framework/3-tooling/cli/src/exports/__tests__/control-import.test.tspackages/0-framework/3-tooling/cli/src/exports/control.tspackages/0-framework/3-tooling/cli/src/log/run-log.tspackages/0-framework/3-tooling/cli/src/main.tspackages/0-framework/3-tooling/cli/src/operations/__tests__/operations.test.tspackages/0-framework/3-tooling/cli/src/operations/deploy.tspackages/0-framework/3-tooling/cli/src/operations/destroy.tspackages/0-framework/3-tooling/cli/src/operations/dev.tspackages/0-framework/3-tooling/cli/src/operations/emulator-retry.tspackages/0-framework/3-tooling/cli/src/operations/execute-deploy-destroy.tspackages/0-framework/3-tooling/cli/src/operations/execute-dev.tspackages/0-framework/3-tooling/cli/src/operations/execute-log.tspackages/0-framework/3-tooling/cli/src/operations/log.tspackages/0-framework/3-tooling/cli/src/operations/shared.tspackages/0-framework/3-tooling/cli/src/render-deployment.tsskills/prisma-composer/SKILL.md
commit: |
|
On the round-3 review body's outside-diff item (per-run generated stack file — |
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/0-framework/3-tooling/cli/src/operations/execute-deploy-destroy.ts`:
- Around line 286-309: Update the non-CliError wrapping in the teardown and
container-removal catches within the deployment flow to construct CliError with
the original error object supplied as its cause via ErrorOptions, while
preserving existing CliError instances unchanged. Apply this consistently to
both extension.teardown and extension.container.remove failure paths so the
original error and cause chain remain accessible.
🪄 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: 5cf30ad2-dd7e-42f3-bdc3-a010ea2f9445
📒 Files selected for processing (5)
packages/0-framework/3-tooling/cli/src/dev/run-dev.tspackages/0-framework/3-tooling/cli/src/log/run-log.tspackages/0-framework/3-tooling/cli/src/operations/__tests__/operations.test.tspackages/0-framework/3-tooling/cli/src/operations/execute-deploy-destroy.tspackages/0-framework/3-tooling/cli/src/operations/execute-log.ts
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
skills/prisma-composer/SKILL.md (1)
672-674: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winCorrect the console-output contract.
The deploy engine can stream output to host stdio, as documented at Lines 697-698. Replace “no console output” with “no CLI rendering” and state that inherited deploy-engine output can still reach the host terminal.
🤖 Prompt for 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. In `@skills/prisma-composer/SKILL.md` around lines 672 - 674, Update the `@prisma/composer/control` description to replace “no console output” with “no CLI rendering,” and explicitly state that inherited deploy-engine output may still reach the host terminal while preserving the existing structured in-process operation contract.
🤖 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 `@docs/design/10-domains/deploy-cli.md`:
- Around line 209-211: Update the documentation sentence in the CLI error
description to avoid claiming every structured error has a fix. State that the
renderer includes Why, Fix, and Where details when those fields are provided by
CliStructuredError, while preserving the existing error code and rendering
format description.
In
`@docs/design/90-decisions/ADR-0044-errors-are-structural-envelopes-with-dotted-namespace-codes.md`:
- Around line 86-91: Specify the text language on the error-layout code block by
changing its opening fence to use text, while leaving the block contents
unchanged.
In `@packages/0-framework/0-foundation/foundation/src/structured-error.ts`:
- Around line 4-5: The public StructuredError code contract must match
STRUCTURED_CODE_RE’s uppercase single-dot grammar. Tighten
StructuredError['code'] and update structuredError() and CliStructuredError to
validate or reject lowercase, empty-segment, and extra-dot values consistently
with isStructuredError(); add tests covering those invalid cases.
In `@packages/0-framework/3-tooling/cli/src/__tests__/load-graph.test.ts`:
- Around line 15-19: Update the test around Load(entry.root) so the caught error
is captured in a variable, then assert its LoadError.code outside the try/catch.
Ensure the test fails when Load() does not throw while preserving the expected
COMPOSE.GRAPH_INVALID assertion.
In `@packages/0-framework/3-tooling/cli/src/dev/run-dev.ts`:
- Around line 94-107: The child-status exception rendering is duplicated between
the failure handling in run-dev and renderDeployDestroyFailure in main.ts.
Export a shared renderer from main.ts, including both console.error messages and
the diagnostics.exitCode return behavior, then replace both local
implementations with calls to that renderer while continuing to use
executionDiagnostics.
In `@packages/0-framework/3-tooling/cli/src/operations/destroy.ts`:
- Line 49: Update the destroyWithDeps() executor-load failure path to provide
operation-specific wording instead of the deploy-specific message. Pass the
destroy operation name to executorLoadFailure(), or configure it to use neutral
“operation executor” wording while preserving the existing notOk() error flow.
In `@packages/0-framework/3-tooling/cli/src/operations/execute-dev.ts`:
- Around line 220-227: Update the attachment tracking in the executeDev startup
loop so each attachment is added to started before withEmulatorRetry invokes
attachment.startServices(). Remove the post-success insertion, preserving the
existing structured error handling and rollback behavior.
In `@packages/0-framework/3-tooling/cli/src/operations/log.ts`:
- Line 75: Update executorLoadFailure to accept an operation name and
interpolate it into the failure message and fix text, then pass the appropriate
operation name from each caller, including the log() and dev() flows, so each
reports its own executor.
In `@packages/0-framework/3-tooling/cli/src/operations/shared.ts`:
- Around line 94-115: Update executorLoadFailure to accept an operation name and
use it in both the failure summary and environment explanation. Update
devWithDeps, destroyWithDeps, and logWithDeps lazy-loader call sites to pass
their respective operation names, preserving existing error details and
diagnostic handling.
---
Outside diff comments:
In `@skills/prisma-composer/SKILL.md`:
- Around line 672-674: Update the `@prisma/composer/control` description to
replace “no console output” with “no CLI rendering,” and explicitly state that
inherited deploy-engine output may still reach the host terminal while
preserving the existing structured in-process operation contract.
🪄 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: 969e636b-2ef0-4277-ac80-f8c50d32b622
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (66)
docs/design/10-domains/deploy-cli.mddocs/design/90-decisions/ADR-0043-the-control-subpath-is-the-programmatic-deploy-surface.mddocs/design/90-decisions/ADR-0044-errors-are-structural-envelopes-with-dotted-namespace-codes.mddocs/design/90-decisions/README.mdpackages/0-framework/0-foundation/foundation/package.jsonpackages/0-framework/0-foundation/foundation/src/cli-structured-error.test.tspackages/0-framework/0-foundation/foundation/src/cli-structured-error.tspackages/0-framework/0-foundation/foundation/src/defined.tspackages/0-framework/0-foundation/foundation/src/exports/errors.tspackages/0-framework/0-foundation/foundation/src/exports/result.tspackages/0-framework/0-foundation/foundation/src/internal-error.test.tspackages/0-framework/0-foundation/foundation/src/internal-error.tspackages/0-framework/0-foundation/foundation/src/result.test.tspackages/0-framework/0-foundation/foundation/src/result.tspackages/0-framework/0-foundation/foundation/src/structured-error.test.tspackages/0-framework/0-foundation/foundation/src/structured-error.tspackages/0-framework/0-foundation/foundation/tsdown.config.tspackages/0-framework/1-core/core/src/control/local-target.tspackages/0-framework/1-core/core/src/graph-types.tspackages/0-framework/1-core/core/src/toposort.tspackages/0-framework/3-tooling/assemble/package.jsonpackages/0-framework/3-tooling/assemble/src/__tests__/assemble-services.test.tspackages/0-framework/3-tooling/assemble/src/assemble-error.tspackages/0-framework/3-tooling/assemble/src/assemble-services.tspackages/0-framework/3-tooling/assemble/src/exports/index.tspackages/0-framework/3-tooling/cli/package.jsonpackages/0-framework/3-tooling/cli/src/__tests__/check-effect-resolution.test.tspackages/0-framework/3-tooling/cli/src/__tests__/cli.test.tspackages/0-framework/3-tooling/cli/src/__tests__/fixtures/run-load-entry.tspackages/0-framework/3-tooling/cli/src/__tests__/jsx-load-error.test.tspackages/0-framework/3-tooling/cli/src/__tests__/load-config.test.tspackages/0-framework/3-tooling/cli/src/__tests__/load-entry.test.tspackages/0-framework/3-tooling/cli/src/__tests__/load-graph.test.tspackages/0-framework/3-tooling/cli/src/__tests__/render-error.test.tspackages/0-framework/3-tooling/cli/src/__tests__/run.test.tspackages/0-framework/3-tooling/cli/src/__tests__/validate-stage.test.tspackages/0-framework/3-tooling/cli/src/bin.tspackages/0-framework/3-tooling/cli/src/check-effect-resolution.tspackages/0-framework/3-tooling/cli/src/cli-error.tspackages/0-framework/3-tooling/cli/src/cli.tspackages/0-framework/3-tooling/cli/src/dev/run-dev.tspackages/0-framework/3-tooling/cli/src/exports/control.tspackages/0-framework/3-tooling/cli/src/exports/index.tspackages/0-framework/3-tooling/cli/src/jsx-load-error.tspackages/0-framework/3-tooling/cli/src/load-config.tspackages/0-framework/3-tooling/cli/src/load-entry.tspackages/0-framework/3-tooling/cli/src/log/__tests__/run-log.test.tspackages/0-framework/3-tooling/cli/src/log/run-log.tspackages/0-framework/3-tooling/cli/src/main.tspackages/0-framework/3-tooling/cli/src/operations/__tests__/operations.test.tspackages/0-framework/3-tooling/cli/src/operations/deploy.tspackages/0-framework/3-tooling/cli/src/operations/destroy.tspackages/0-framework/3-tooling/cli/src/operations/dev.tspackages/0-framework/3-tooling/cli/src/operations/execute-deploy-destroy.tspackages/0-framework/3-tooling/cli/src/operations/execute-dev.tspackages/0-framework/3-tooling/cli/src/operations/execute-log.tspackages/0-framework/3-tooling/cli/src/operations/log.tspackages/0-framework/3-tooling/cli/src/operations/shared.tspackages/0-framework/3-tooling/cli/src/pipeline.tspackages/0-framework/3-tooling/cli/src/render-error.tspackages/0-framework/3-tooling/cli/src/run-alchemy.tspackages/0-framework/3-tooling/cli/src/validate-coverage.tspackages/0-framework/3-tooling/cli/src/validate-stage.tsskills/prisma-composer/SKILL.mdtest/integration/test/control.deploy.test.tstsconfig.depcruise.json
💤 Files with no reviewable changes (1)
- packages/0-framework/3-tooling/cli/src/cli-error.ts
… throws Recorded in `started` before the call, so the existing catch stops an attachment whose startServices came up partway and then threw (stopServices failures are already swallowed there, so stopping a never-started attachment is harmless). Review A-001 on #205. Refs: TML-3181 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>
executorLoadFailure gains an operation-name parameter used in DEPS.EXECUTOR_UNLOADABLE's summary and why; deploy/destroy/dev/log each pass their own. Also lands the partial-start rollback test for A-001. Review A-002/A-003/A-004 on #205. Refs: TML-3181 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>
renderChildStatusHints in render-error.ts is the single implementation of the ADR-0044 child-status exception; main.ts and run-dev.ts call it, output byte-identical. Review A-005 on #205. Refs: TML-3181 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>
The captured-error pattern fails the test if Load() stops throwing, instead of silently skipping the code assertion. Review A-006 on #205. Refs: TML-3181 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>
deploy-cli.md no longer promises a Fix line for every failure; the ADR-0044 layout fence is tagged text (MD040); SKILL.md says the control operations do no CLI rendering while inherited deploy-engine output can still reach the host terminal. Refs: TML-3181 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>
deploymentReport now also writes a JSON DeploymentSummary — the serializable projection of DeploymentResult (address + entities, no in-process node) — to the file named by PRISMA_COMPOSER_DEPLOYMENT_RESULT_FILE when that env var is set. The printed report is unchanged, and nothing is written without the env var, so the generated stack file stays byte-identical. This is the writer half of the cross-process result contract the programmatic deploy operation reads back (TML-3174 design §3.4). Refs: TML-3174 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>
deploy() and destroy() are now programmatic operations (operations/): typed inputs, structured results, no argv, no console, no process.exit. The pipeline orchestration (main.ts steps 0-9.75) moved verbatim into execute-deploy-destroy.ts, reached only by dynamic import after a structured effect-resolution preflight (TML-3158) so the operations entry stays import-safe in a broken effect tree. main.ts run() becomes a thin renderer: flag combinations validated with the same CliError texts, the destroy no-state warning rendered from the operation event, alchemy-failure hints and passthrough exit codes unchanged. run.test.ts passes unmodified — the extraction proof. A successful deploy now also reads back the DeploymentSummary the alchemy child writes via PRISMA_COMPOSER_DEPLOYMENT_RESULT_FILE (absent or malformed file = undefined summary, never a failure). Refs: TML-3174 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>
dev() returns a DevSession (endpoints, stop(), closed) and reports lifecycle through onEvent — the operation never touches process signal handlers; the CLI adapter keeps the removeAllListeners + single-listener signal ownership and renders each event with the exact console lines it always printed. log() returns the running services plus a merged, address-filtered AsyncIterable of lines, ended by the caller-owned AbortSignal; per-stream failures surface as stream-failed events without ending the other streams. run-dev.ts and run-log.ts become thin parse-shaped adapters over the operations; their suites pass unchanged. Refs: TML-3174 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>
New `./control` subpath on @internal/cli (generated exports map committed) and @prisma/composer (hand-maintained map, per the published-package exception): the deploy/destroy/dev/log operations, their input/result types, and the DEPLOYMENT_RESULT_FILE_ENV cross-process contract. Depcruise aliases and the architecture per-file entry for the 9-public shim keep every edge visible to the cruiser; lint:deps is green. Named `/control` after the plane the CLI sources already occupy — the shim doc-comment distinguishes it from an extension's ADR-0017 `/control` entry. Refs: TML-3174 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>
…ublished tree operations.test.ts drives deploy/destroy/log with the run.test.ts fakes: the result-file round trip (env var passed, stale file removed pre-spawn, malformed file = undefined summary), structured invalid-input/pipeline/execution failures with the exact CLI messages, destroy target discrimination and teardown-before-remove order, the pre-pipeline no-local-deploy-state event, the merged/ filtered/abortable log stream with stream-failed events, and a seeded effect-mismatch tree returning an effect-resolution result. Every operation call runs inside silently(), which fails on any console output. control.deploy.test.ts is the slice done-condition: a consumer outside the CLI imports @prisma/composer/control and runs deploy over the real integration fixture, reaching the same missing-built-entry terminal point the binary test pins — as a structured result. check-npm-effect-resolution.mjs now also probes the adversarial package-manager tree in-process: importing the control surface must not crash, and deploy() must return the effect-resolution failure with exit 0. Refs: TML-3174 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>
ADR-0043 records the ./control surface and the PRISMA_COMPOSER_DEPLOYMENT_RESULT_FILE cross-process contract (plus the index entry). The deploying guide gains a "Driving deploys from code" section, mirrored tersely into skills/prisma-composer/SKILL.md (user-facing-surface-changes: both in the same PR), and deploy-cli.md § Contracts now names @internal/assemble's second consumer as shipped rather than future. Refs: TML-3174 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>
The endpoint merge and watch setup ran outside any try/catch, so a throw
there (e.g. withEmulatorRetry exhausting its attempts) rejected the dev()
promise instead of producing { outcome: "failed", kind: "pipeline" } as
design § 3.5 requires. The attach try block now extends through the
endpoint merge, watch setup, and session construction; the startServices
rollback is unchanged.
Refs: TML-3174
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>
The CLI adapter printed unwatchable notices before the `[dev] logs:` hint; the shipped order was front door, hint, then unwatchable lines. Notices received before the session is returned are now buffered and flushed right after the hint; later ones print immediately. Every string is unchanged. Refs: TML-3174 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>
Ground the decision in a usage example, build the narrative up from motivation through import safety and the process-boundary contract, and strip refactor-history framing and ticket references. Refs: TML-3174 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>
…catch path The per-operation checkEffectResolution preflight and the dedicated effect-resolution failure kind were oversized for what is now a transient upstream condition. Each operation instead wraps the lazy import of its executor in a try/catch: on a load failure it diagnoses the target tree with checkEffectResolution and returns a pipeline failure carrying the fix-naming message (or the original error message when the tree is healthy), with the import error as cause. The entry stays import-light as a general no-import-side-effects property; bin.ts keeps its own start-up check. Reverts the control-surface probe added to scripts/check-npm-effect-resolution.mjs back to main. Refs: TML-3174 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>
…l property ADR-0043 no longer frames import-safety as an effect-specific contract with its own failure kind: the entry is import-light with lazily loaded executors, and a tree that cannot load the deploy stack surfaces as a structured pipeline failure with a diagnostic message. Update the ADR index line, the deploying guide, and the composer skill to the reduced failure taxonomy. Refs: TML-3174 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>
…inputs Per operator ruling: either it's public API or it isn't — we don't ship production code just for tests. DeployInput/DestroyInput/DevInput/LogInput no longer carry deps, and OperationDeps/LogDeps are gone from the ./control shims. Each per-operation module now pairs the clean public function with an in-package *WithDeps variant (deployWithDeps, ...) that takes the seam as a separate parameter; the executors take (input, deps, cwd). The CLI adapters and unit tests thread RunDeps through the WithDeps variants — run.test.ts unchanged — and the published integration consumer (test/integration control.deploy.test.ts) keeps driving the real pipeline with no seam at all. Refs: TML-3174 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>
…nd-trip The result file was a fixed path under cwd, so two deploys sharing a working directory (two stages from one checkout, a host mid-deploy while the CLI runs) silently corrupted each other: run B's stale-guard deleted run A's summary and both children raced one file. The path now carries pid + a UUID, the pre-spawn stale-guard is gone (nothing can be stale on a unique path), and the file is deleted right after the operation reads it. The summary protocol also gains its first covering test through a REAL child process: the injected alchemy spawns a bun child that calls writeDeploymentSummaryFile with the env var the operation set, and the operation reads back exactly what the writer wrote — previously every regression in the pair (env-var drift, report unwired) presented as a normal deploy with summary: undefined. Refs: TML-3174 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>
… graph
Two ADR-level guarantees had no test:
- DevSession: closed settles only via stop(), stop() is idempotent
(stopping/stopped fire once, both calls resolve), and a dev() run leaves
process.listenerCount('SIGINT'/'SIGTERM') untouched — the operation
never registers signal handlers, so the host can own signals.
- Import-lightness: a fresh bun process imports src/exports/control.ts
with every heavy module poisoned (executors, pipeline, run-alchemy,
stack generators, watch, adapters); if the entry's static graph ever
reaches one, the import throws. Replaces the reverted CI probe with an
in-repo structural check.
Refs: TML-3174
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>
…th round 2
- ADR heading softened to what the body defends ('Importing the subpath
executes nothing'), and the body now cites the structural test pinning
the entry's static graph.
- The stdio consequence stops promising a mechanism: the spawned child and
'stdio: inherit' are how composer deploys today, not part of the
contract — matching the demotion of the spawn-shaped failure fields into
the optional diagnostics object, which all three documents now describe.
- The ADR names the accepted structural cost: @internal/cli now contains a
surface that is not a CLI.
- The summary-protocol section points at deployment-summary.ts, the
unique per-run result file, the best-effort writer, and the env var's
removal from the public exports.
- The guide and SKILL document dev/log outcome discriminants
({outcome:'started', session} / {outcome:'attached', ...}), the
lines-dropped and watch-error events, and clean early termination of the
log stream.
Refs: TML-3174
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>
The dev operation emits a stop-error DevEvent when stopServices() throws, but run-dev.ts had no case for it, so the failure never reached the console — the user only saw "[dev] stopped." Refs: TML-3174 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>
The bounded merge queue emits lines-dropped when the consumer falls behind, but runLog ignored every event except stream-failed, so lines vanished without warning. Branches explicitly on the kind so a future event kind is not misrendered. Refs: TML-3174 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>
TOTAL was 10_150 against a bound of 10_000 — a 1.5% margin that one consumed line plus scheduling variation could erase, flipping the droppedTotal > 0 assertion flaky. LOG_QUEUE_LIMIT is now exported and the flood is twice the bound. Refs: TML-3174 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>
The no-events-after-end test assigns failLate only when the generator resumes past its first yield; if the pump ever stopped requesting the second item, failLate?.() would no-op and the test would pass without exercising the late failure. Asserting failLate is defined first makes that regression loud. Refs: TML-3174 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>
emit() called input.onEvent unguarded: a throw during a pump's stream-failed emission rejected the fire-and-forget pump promise (an unhandled rejection), and a throw for lines-dropped rejected the merged iterable. Host events are advisory — a broken renderer must not tear down the stream. Refs: TML-3174 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>
The child can write deployment-result-<pid>-<uuid>.json via the report hook before a later step fails; cleanup only ran on the successful deploy path, so every failed run left resource ids/URLs on disk and the files accumulated. The stack-file/alchemy/teardown suffix now funnels through one try/finally that removes the file best-effort. Refs: TML-3174 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>
…181) Copies structured-error, internal-error, result, ifDefined, and CliStructuredError (with the cause repair baked in) from the donor, byte-close, exposed through the new /errors and /result subpaths. Donor tests travel (vitest -> bun:test) plus two new cause cases. Refs: TML-3181 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>
…ut (TML-3181) Refs: TML-3181 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>
LoadError becomes a CliStructuredError with the type-level COMPOSE.GRAPH_INVALID code; the toposort cycle assertion is reclassified as an InternalError (a bug, not a user failure); the no-dev-support refusal is DEV.TARGET_UNSUPPORTED at origin. AssembleError gains the ASSEMBLE.* codes, and a foreign build failure is wrapped as ASSEMBLE.BUILD_FAILED where the address is known. Refs: TML-3181 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>
… API (TML-3181) Every CliError raise site becomes a CliStructuredError with a dotted NAMESPACE.SUBCODE code and a why/fix split; cli-error.ts is deleted. OperationFailure is deleted — operation results ride the shared Result shape with CliStructuredError as the failure, and executionDiagnostics() reads an engine failure's meta.diagnostics. The boundary is exit-code mapped per the shared rule: usage/structured failures exit 2, a non-structured escape is a bug (exit 1 + report hint, no fallback codes). The alchemy child-status passthrough stays as the documented exception. The CLI manifest's repository slug is fixed to prisma/composer. Refs: TML-3181 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>
…ML-3181) ADR-0044 records the shared error convention: dotted namespace codes, structural recognition, bugs carry no code, the exit-code split, and the alchemy child-status exception. ADR-0043's example and consequences move to the Result shape and the code registry as the taxonomy. Refs: TML-3181 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>
Assigning undefined does not clear an already-set exit code under bun, so the leaked 2 failed the whole bun test run despite 0 failing tests. Refs: TML-3181 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/compose was a mistake; the repo is prisma/composer (the Amb 5 ruling on TML-3181, extended to every manifest). Refs: TML-3181 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>
…..> directly DeployResult, DestroyResult, DevStartResult, and LogResult are deleted (semver 0, no consumers) — operations and the ./control shim name the Result<Success, CliStructuredError> shapes directly. Refs: TML-3181 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>
… throws Recorded in `started` before the call, so the existing catch stops an attachment whose startServices came up partway and then threw (stopServices failures are already swallowed there, so stopping a never-started attachment is harmless). Review A-001 on #205. Refs: TML-3181 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>
executorLoadFailure gains an operation-name parameter used in DEPS.EXECUTOR_UNLOADABLE's summary and why; deploy/destroy/dev/log each pass their own. Also lands the partial-start rollback test for A-001. Review A-002/A-003/A-004 on #205. Refs: TML-3181 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>
renderChildStatusHints in render-error.ts is the single implementation of the ADR-0044 child-status exception; main.ts and run-dev.ts call it, output byte-identical. Review A-005 on #205. Refs: TML-3181 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>
The captured-error pattern fails the test if Load() stops throwing, instead of silently skipping the code assertion. Review A-006 on #205. Refs: TML-3181 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>
deploy-cli.md no longer promises a Fix line for every failure; the ADR-0044 layout fence is tagged text (MD040); SKILL.md says the control operations do no CLI rendering while inherited deploy-engine output can still reach the host terminal. Refs: TML-3181 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>
…sumer tree Workaround for the upstream alchemy bug (the TaggedErrorClass drift): alchemy's effect-family dependency and peer ranges float past the versions its shipped code supports, so a fresh npm install resolves the floaters (@effect/sql-d1, @effect/sql-pg, @effect/vitest, @effect/platform-*) to the newest beta, whose peer floors reject composer's effect pin and drag in a second effect — the check-npm-effect-resolution failure. Every consumer-shaped tree now pins the whole constellation to composer's exact pin via an npm overrides block: the examples/* manifests, the getting-started and deploying guides, and the check script's healthy shapes (which now install the scaffolded consumer shape, proving it genuinely dedupes to one effect@4.0.0-beta.103 with alchemy working). The adversarial WRONG_EFFECT shape still proves the CLI fails loudly on a bad tree. Delete the block everywhere when alchemy fixes its ranges; the upgrade-alchemy-effect skill records the lockstep rule. Refs: TML-3181 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>
dd37a91 to
ba427be
Compare
Composer's deploy pipeline becomes drivable in-process: this PR adds
@prisma/composer/control, four typed operations —deploy,destroy,dev,log— with structured inputs and results, and re-points theprisma-composerCLI to be a thin renderer over them.No argv, no console output from the operations, no
process.exit— failures are values a host can branch on. The full design rationale is ADR-0043 (in this diff).How it's built up
src/__tests__/run.test.ts: byte-identical tomainand green, so the command-line behavior demonstrably did not change.operations/{deploy,destroy,dev,log}.tseach hold their input types, result types, and the operation function; shared failure/deps types live inshared.ts. Each module is import-light and lazily imports its executor — importing./controlexecutes nothing (pinned by a poisoned-import test), and a dependency tree that can't load the deploy stack surfaces as a structuredpipelinefailure, never an import-time crash.diagnosticsobject; the child⇄parent summary protocol lives whole indeployment-summary.tswith a per-run unique result file; nothing mechanism-shaped is exported.*WithDepsvariants that the shims don't re-export.devreturns a session handle (endpoints,stop(),closed, events) and never touches process signal handlers — the host owns signals.logreturns anAsyncIterablethat terminates promptly onbreak/abort, with a bounded drop-oldest queue that reports drops as an event.Review history
Two full rounds are already folded in: an independent design+code review plus @wmadden's comments and the CodeRabbit triage (see the round-2 comment for the thread→commit map). Highlights fixed along the way:
deploy()/dev()rejection paths became failure results, thelog()early-breakhang, result-file collisions between concurrent runs, and the per-operation effect preflight was deliberately removed — broken trees now diagnose through the ordinary failure path, and only the CLI's ownbin.tsstart-up check remains.Alternatives considered (short form; ADR-0043 has the full reasoning)
prisma-composer: couples callers to output formatting and turns errors into string parsing.@internal/cli; onlypackages/9-public/publishes — a boundary with nothing on the other side.Known issues (pre-existing, not this PR)
check-npm-effect-resolutionis red onmain: alchemy's floating ranges resolveeffect@4.0.0-beta.104against the repo's beta.103 pin. Needs a pin bump in a separate change.Refs: TML-3174
🤖 Generated with Claude Code