feat(cli): config diagnostics, dispatch-time effect preflight, and a control-API test double - #210
feat(cli): config diagnostics, dispatch-time effect preflight, and a control-API test double#210wmadden-electric wants to merge 3 commits into
Conversation
…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>
|
Warning Review limit reached
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 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 (30)
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 deliverables from the config-contract compliance brief, one commit each.
1. Config loading returns diagnostics instead of throwing
loadAppConfignow 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/destroyrequireextensions+statedevand identity readers (log) requireextensionsonly — an invalidstateno longer blocks themCONFIG.EVALUATION_FAILEDdiagnostic, sectionless, so every command fails early with itRegistry-coverage checking likewise collects every uncovered
(extension, type). A single diagnostic surfaces as itself (its code stays the branching surface); several combine into oneCONFIG.INVALID— added to ADR-0044's registry in the same change — carrying the list inmeta.issues(prisma/prisma's shared envelope idiom: one named failure,{kind, message}items, rendered as an indentedIssues:list). Representative rendered text andtoEnvelope()JSON are pinned inload-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.tsbefore every command. Each executor-loading operation (deploy/destroy/dev/log) runs it at dispatch — before config discovery and before the executor import — and returnsDEPS.EFFECT_VERSION_CONFLICTas 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./controlone). TheDEPS.EXECUTOR_UNLOADABLEbackstop is unchanged.scripts/check-npm-effect-resolution.mjskeeps every deploy assertion (nonzero exit, thealchemy resolves effect@marker, no raw TypeError, no misfire on healthy trees). Its adversarial--helpassertion 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/controlnested-subpath pattern) exportscreateControlDouble(): the four operations with the real signatures andResultshapes, scripted by per-operation fixtures — cannedResults or functions of the operation input — never a config load, an alchemy process, or a container. TheDevSessiondouble honors the lifecycle (stop()emitsstopping/stopped, settlesclosed, idempotent); the log double replays fixture lines with the real address filter and ends on signal abort.structuredFailure()buildsCliStructuredErrorfixtures since the class stays type-only on the control surface. Conformance is compile-time:ControlOperationsis built fromtypeofthe 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): passpnpm test(turbo, 62 tasks): 60 pass. The two failures are pre-existing, not from this change: the known@internal/local-targettimeout, and 7@internal/dev-emulatorspostgres tests failing on stale local@prisma/devdaemon state ("Port number … belongs to another Prisma Dev server"). Both packages (and the lockfile) are byte-identical to the merge-base —git diff f8b2e489..HEADover them is empty — so the failing runs execute exactly the merge-base codepnpm 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--helpworks therepnpm check:publish-deps: pass🤖 Generated with Claude Code