Skip to content

feat(cli): config diagnostics, dispatch-time effect preflight, and a control-API test double - #210

Open
wmadden-electric wants to merge 3 commits into
mainfrom
claude/composer-config-diagnostics-646796
Open

feat(cli): config diagnostics, dispatch-time effect preflight, and a control-API test double#210
wmadden-electric wants to merge 3 commits into
mainfrom
claude/composer-config-diagnostics-646796

Conversation

@wmadden-electric

Copy link
Copy Markdown
Contributor

Three deliverables from the config-contract compliance brief, one commit each.

1. Config loading returns diagnostics instead of throwing

loadAppConfig now returns the evaluated value plus a diagnostics list — every problem found, not just the first — with each diagnostic tagged (meta.section/meta.field) with the config section it concerns. A command fails (exit 2) only when a section it reads is invalid:

  • deploy/destroy require extensions + state
  • dev and identity readers (log) require extensions only — an invalid state no longer blocks them
  • An unevaluatable config module stays one CONFIG.EVALUATION_FAILED diagnostic, sectionless, so every command fails early with it

Registry-coverage checking likewise collects every uncovered (extension, type). A single diagnostic surfaces as itself (its code stays the branching surface); several combine into one CONFIG.INVALID — added to ADR-0044's registry in the same change — carrying the list in meta.issues (prisma/prisma's shared envelope idiom: one named failure, {kind, message} items, rendered as an indented Issues: list). Representative rendered text and toEnvelope() JSON are pinned in load-config.test.ts; single-diagnostic configs render byte-identically to before, multi-diagnostic configs change only in framing (they now show every problem).

2. The effect-resolution preflight runs at dispatch, as a diagnostic

The TML-3158 check no longer runs in bin.ts before every command. Each executor-loading operation (deploy/destroy/dev/log) runs it at dispatch — before config discovery and before the executor import — and returns DEPS.EFFECT_VERSION_CONFLICT as a structured failure. Commands that load no executor (--help) now work even in a broken tree; the CLI's static import graph carries no alchemy/effect code, pinned structurally by a new poison test (cli-import.test.ts, same pattern as the ./control one). The DEPS.EXECUTOR_UNLOADABLE backstop is unchanged.

scripts/check-npm-effect-resolution.mjs keeps every deploy assertion (nonzero exit, the alchemy resolves effect@ marker, no raw TypeError, no misfire on healthy trees). Its adversarial --help assertion flips to the new contract: usage output, no marker, no crash. The effect constellation pin (4.0.0-beta.103) is untouched.

3. Published test double for the control API

@prisma/composer/control/testing (mirroring the ./node/control nested-subpath pattern) exports createControlDouble(): the four operations with the real signatures and Result shapes, scripted by per-operation fixtures — canned Results or functions of the operation input — never a config load, an alchemy process, or a container. The DevSession double honors the lifecycle (stop() emits stopping/stopped, settles closed, idempotent); the log double replays fixture lines with the real address filter and ends on signal abort. structuredFailure() builds CliStructuredError fixtures since the class stays type-only on the control surface. Conformance is compile-time: ControlOperations is built from typeof the real operations and annotates the double's return type, so signature drift stops the build. The entry is import-light, pinned by the parameterized poison test.

Verification

  • pnpm build, pnpm typecheck, pnpm lint, pnpm lint:casts (delta 0), pnpm lint:deps (all sub-checks): pass
  • pnpm test (turbo, 62 tasks): 60 pass. The two failures are pre-existing, not from this change: the known @internal/local-target timeout, and 7 @internal/dev-emulators postgres tests failing on stale local @prisma/dev daemon state ("Port number … belongs to another Prisma Dev server"). Both packages (and the lockfile) are byte-identical to the merge-base — git diff f8b2e489..HEAD over them is empty — so the failing runs execute exactly the merge-base code
  • pnpm check:npm-effect-resolution (real npm registry): pass — healthy shapes dedupe to the pinned effect, the adversarial tree is caught at dispatch with the actionable error, and --help works there
  • pnpm check:publish-deps: pass

🤖 Generated with Claude Code

wmadden-electric and others added 3 commits August 9, 2026 13:45
…sections they need

Loading prisma-composer.config.ts no longer throws on the first invalid
field. loadAppConfig returns the evaluated value plus a diagnostics list —
every problem found, each tagged with the config section it concerns via
meta.section/meta.field. A command declares the sections it reads
(deploy/destroy: extensions + state; dev and identity readers like log:
extensions only) and fails, exit 2, only when one of those is invalid. An
unevaluatable config module stays one CONFIG.EVALUATION_FAILED diagnostic,
sectionless, so every command fails early with it.

Registry-coverage checking collects every uncovered (extension, type)
instead of stopping at the first. A single diagnostic surfaces as itself —
its own code stays the branching surface; several combine into one
CONFIG.INVALID (added to the ADR-0044 registry) carrying the list in
meta.issues, prisma/prisma's shared envelope idiom, which the error
renderer now prints as an indented Issues list.

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>
…ration dispatch

The mismatched-effect check (TML-3158) no longer runs in bin.ts before
every command. Each executor-loading operation (deploy/destroy/dev/log)
runs it at dispatch — before config discovery or any executor import — and
returns DEPS.EFFECT_VERSION_CONFLICT as a structured failure. Commands
that load no executor (--help) now work even in a broken tree: the CLI's
static import graph carries no alchemy/effect code, pinned structurally by
the new cli-import poison test. The lazy executor-load backstop
(DEPS.EXECUTOR_UNLOADABLE, with the same diagnosis on a failed import) is
unchanged.

The npm effect-resolution probe keeps every deploy assertion (nonzero
exit, the fix-naming marker, no raw TypeError, no misfire on healthy
trees); its adversarial --help assertion flips to the new contract: usage
output, no marker, no crash. Verified against the real registry: healthy
shapes dedupe to the pinned effect and the adversarial tree is caught at
dispatch.

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>
…ontrol/testing

Hosts driving @prisma/composer/control can now test against
createControlDouble(): the four operations with the real signatures and
Result shapes, scripted by per-operation fixtures (canned Results or
functions of the operation input) — never a config load, an alchemy
process, or a container. The DevSession double honors the lifecycle
(stop() emits stopping/stopped, settles closed, idempotent); the log
double replays fixture lines with the real address filter and ends on
signal abort. structuredFailure() builds CliStructuredError fixtures,
since the class itself stays type-only on the control surface (ADR-0044
structural recognition).

Conformance is compile-time: ControlOperations is built from typeof the
real operations and annotates createControlDouble's return type, so
signature drift stops the build. The entry is import-light like ./control
itself, pinned by the same poison test, now parameterized over both
entries. Published as @prisma/composer/control/testing, mirroring the
existing nested-subpath pattern (./node/control).

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

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Warning

Review limit reached

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

Next review available in: 42 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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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

How do review limits work?

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

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

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5b656877-97f0-4666-bcdd-da90df27701b

📥 Commits

Reviewing files that changed from the base of the PR and between de78022 and 516d9b3.

📒 Files selected for processing (30)
  • architecture.config.json
  • docs/design/90-decisions/ADR-0043-the-control-subpath-is-the-programmatic-deploy-surface.md
  • docs/design/90-decisions/ADR-0044-errors-are-structural-envelopes-with-dotted-namespace-codes.md
  • packages/0-framework/3-tooling/cli/package.json
  • packages/0-framework/3-tooling/cli/src/__tests__/check-effect-resolution.test.ts
  • packages/0-framework/3-tooling/cli/src/__tests__/load-config.test.ts
  • packages/0-framework/3-tooling/cli/src/bin.ts
  • packages/0-framework/3-tooling/cli/src/check-effect-resolution.ts
  • packages/0-framework/3-tooling/cli/src/exports/__tests__/cli-import.test.ts
  • packages/0-framework/3-tooling/cli/src/exports/__tests__/control-import.test.ts
  • packages/0-framework/3-tooling/cli/src/exports/control-testing.ts
  • packages/0-framework/3-tooling/cli/src/load-config.ts
  • packages/0-framework/3-tooling/cli/src/operations/__tests__/control-double.test.ts
  • packages/0-framework/3-tooling/cli/src/operations/__tests__/operations.test.ts
  • packages/0-framework/3-tooling/cli/src/operations/control-double.ts
  • packages/0-framework/3-tooling/cli/src/operations/deploy.ts
  • packages/0-framework/3-tooling/cli/src/operations/destroy.ts
  • packages/0-framework/3-tooling/cli/src/operations/dev.ts
  • packages/0-framework/3-tooling/cli/src/operations/execute-dev.ts
  • packages/0-framework/3-tooling/cli/src/operations/log.ts
  • packages/0-framework/3-tooling/cli/src/operations/shared.ts
  • packages/0-framework/3-tooling/cli/src/pipeline.ts
  • packages/0-framework/3-tooling/cli/src/render-error.ts
  • packages/0-framework/3-tooling/cli/src/validate-coverage.ts
  • packages/0-framework/3-tooling/cli/tsdown.config.ts
  • packages/9-public/composer/package.json
  • packages/9-public/composer/src/exports/control-testing.ts
  • packages/9-public/composer/tsdown.config.ts
  • scripts/check-npm-effect-resolution.mjs
  • tsconfig.depcruise.json

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 9, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@prisma/composer@210
npm i https://pkg.pr.new/@prisma/composer-prisma-cloud@210

commit: 516d9b3

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.

1 participant