Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a5079b5
docs(architecture): requirements for the unified CLI engine
wmadden-electric Aug 9, 2026
220807b
docs(architecture): rule the tree (shell-defined) and ban package-man…
wmadden-electric Aug 9, 2026
ced1388
docs(architecture): R5 clarifications — framework renderers are legal…
wmadden-electric Aug 9, 2026
7247cfb
docs(architecture): the engine wraps @stricli/core
wmadden-electric Aug 9, 2026
f7ea6e8
docs(architecture): R14 — engine-defined event vocabulary with produc…
wmadden-electric Aug 9, 2026
4851431
drive(prisma-cli-v8): commit the project workspace — spec, design rec…
wmadden-electric Aug 9, 2026
04809e8
drive(prisma-cli-v8): project plan — platform, then Composer, then ORM
wmadden-electric Aug 9, 2026
03f8703
drive(prisma-cli-v8): S1 slice contract — engine package + auth whoam…
wmadden-electric Aug 9, 2026
423cca6
drive(prisma-cli-v8): S1 dispatch plan — six sequential dispatches
wmadden-electric Aug 9, 2026
6c00a6b
drive(prisma-cli-v8): strip review artifacts and superseded interface…
wmadden-electric Aug 9, 2026
5e3da1f
docs(architecture): scope R4's prohibition and define the output surface
wmadden-electric Aug 9, 2026
e8c99af
docs(architecture): clarify R9 forbids discovery, not startup instant…
wmadden-electric Aug 9, 2026
911f452
docs(architecture): R10 covers config load and evaluation failures
wmadden-electric Aug 9, 2026
7c9b183
docs(architecture): state the R10 version-marker contract exactly
wmadden-electric Aug 9, 2026
0c34b78
docs(architecture): make the friction-points reference followable
wmadden-electric Aug 9, 2026
1899416
docs(architecture): record evaluated framework versions in the decision
wmadden-electric Aug 9, 2026
a920cc0
docs(plan): tag the dependency-graph fence as text
wmadden-electric Aug 9, 2026
22877f7
chore: exclude .drive design artifacts from biome
wmadden-electric Aug 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Brief: prisma/prisma config-contract repairs and ControlClient test double

Repo: prisma/prisma (main). Three independent deliverables; land as separate PRs or one PR with separate commits. Operator: Will Madden. Process: verify every claim below against current code before changing it; if you hit a judgment call this brief doesn't settle, stop and report it as a numbered question with options and a recommendation — do not decide it yourself.

## Context

These are the remaining config/contract items from the CLI-consolidation work. The governing rules are in-repo: ADR 239 (structural error envelopes, dotted codes), ADR 245 (errors structured at origin, no catch-all codes; one `ok` discriminator on results), and `docs/CLI Style Guide.md` (exit codes). The error-code registry is `docs/reference/error-reference.md`, enforced by `pnpm run check:error-reference` — any new code or new producing site is added there in the same change.

## Deliverable 1: config loading returns diagnostics instead of throwing

Today `loadConfig` (`packages/1-framework/3-tooling/config-loader/src/load.ts`, validation in `packages/1-framework/1-core/config/`) throws `CONFIG.VALIDATION_FAILED` / `CONFIG.EVALUATION_FAILED`-shaped errors on the first problem. The target semantics:

- Evaluating a config module never takes down the command wholesale. Loading returns the evaluated config **plus a diagnostics list** (each diagnostic a `CliErrorEnvelope`-shaped structured error with a `meta.section` identifying the config section it concerns).
- A command that needs section X fails (exit 2, rendering that diagnostic) only if section X has a diagnostic; commands not touching X proceed.
- A config file that cannot be evaluated at all (module threw, unparseable) yields a single evaluation diagnostic attached to no section; every command then fails early with it.
- Existing error codes are reused; genuinely new conditions get new registered codes. No behavior change to what users *see* for currently-failing configs beyond message framing — pin representative `--json` envelopes before and after.

Design the exact return type first (the repo convention is the shared `Result` from `@internal/utils/result`, but a config-with-diagnostics is not a failure — a `{ config, diagnostics }` value inside `Ok` is the expected shape) and validate it against every `loadConfig` call site before writing code.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

## Deliverable 2: versioned `defineConfig` marker

`defineConfig` (`packages/1-framework/1-core/config/src/config-types.ts`) stamps the object it returns with a config-format version marker (non-enumerable; survives spreads is NOT required — document that configs must return the `defineConfig` result directly). The loader then enforces:

- Marker present and current → proceed.
- Evaluation succeeded but no marker (a plain object export, or a config produced by a different `defineConfig` — i.e. a classic Prisma 7 file once the unified filename lands) → **fail early** with a new registered code (suggested: `CONFIG.UNVERSIONED_CONFIG`) whose fix text names `defineConfig` and links the migration path. This ruling is settled: fail early; no best-effort reading of unmarked configs.

The marker's purpose is downstream: the future unified host loader will claim `prisma.config.ts`, a filename Prisma 7 already uses, and must distinguish the two by marker rather than misparse. Build the marker and enforcement here; do not build any Prisma-7-filename discovery in this repo.

## Deliverable 3: published fixture-backed `ControlClient` test double

Hosts and product tests need to drive the CLI's control-api surface without a real database. Export a fixture-backed double of the control client (`packages/1-framework/3-tooling/cli/src/control-api/client.ts`) from a **published** entrypoint (decide placement against how `@prisma/orm-toolchain` composes its published surface; the double must not drag the real driver/database imports into consumers). It covers every seam operation the control API exposes, returns the shared `Result` shapes with realistic fixture payloads, and its per-operation fixtures are overridable per test. Add a conformance-style test asserting the double's surface stays in sync with the real client (compile-time: same operation names and signatures).

## Verification (all must pass before pushing)

`pnpm turbo build --filter=@internal/cli...`; full test + typecheck + lint in config-loader, config, cli packages; `test/integration` typecheck; `pnpm run check:error-reference` with zero failures; `pnpm lint:deps`.

## Commit discipline

Explicit staging only. `git commit -s --trailer "Signed-off-by: Will Madden <madden@prisma.io>"`, body ends with `Co-Authored-By: <your model attribution>`. Push via the `bot` remote, never origin. Verify the PR is open before any push to an existing PR branch.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Brief: composer config-contract compliance and control-API test double

Repo: prisma/composer (main). Three deliverables. Operator: Will Madden. Process: verify every claim against current code first; stop and report numbered questions with options and a recommendation on anything this brief doesn't settle.

## Context

Composer's error/result rules are recorded in its ADR-0043 and ADR-0044 (`docs/design/90-decisions/`): structured errors at origin with dotted codes from the closed registry, one `ok` discriminator, exit 1 = bug only. The registry in ADR-0044 is closed — a new subcode is an edit to that list in the same change. Constraint that must not move: the effect constellation stays pinned at `4.0.0-beta.103` via the consumer overrides block (alchemy is broken on effect >= beta.104; see `skills-contrib/upgrade-alchemy-effect/SKILL.md`).

## Deliverable 1: config validation returns diagnostics instead of throwing

Today `load-config.ts` / `validate-coverage.ts` throw on the first invalid field. Target semantics (the config contract all products will share):

- Loading a config returns the evaluated value **plus a diagnostics list** (structured errors, each tagged with the config section/field it concerns via `meta`), instead of throwing per field.
- Commands fail (exit 2, rendering the diagnostic) only when a section they need is invalid.
- A config module that cannot be evaluated at all yields one evaluation diagnostic (`CONFIG.EVALUATION_FAILED` already exists) and every command fails early with it.
- No import-time side effects and no throwing from `defineConfig`-equivalent factories: constructing a config value never throws; problems surface as diagnostics at load time.
- Pin representative rendered/`--json` output before and after — user-visible behavior for currently-failing configs may only change in framing.

## Deliverable 2: effect-resolution preflight becomes a diagnostic

`check-effect-resolution.ts` currently detects a mismatched `effect` in the consumer's tree by throwing during import, which takes out every command — including ones that never touch the deploy executor. Target:

- The preflight runs at config-load/command-dispatch time, not import time, and surfaces as a structured diagnostic (`DEPS.EFFECT_VERSION_CONFLICT`, already registered) carried in the diagnostics list from Deliverable 1.
- Commands that need the executor fail early rendering it; commands that don't (e.g. help, config inspection) still work.
- The lazy executor-load failure path (`DEPS.EXECUTOR_UNLOADABLE`) is unchanged — it remains the backstop when the preflight didn't fire.
Comment thread
wmadden-electric marked this conversation as resolved.
- The effect-CI probe and the `npm install effect dedupe` check must still pass; do not weaken either.

## Deliverable 3: published test double for the control API

Hosts driving `@prisma/composer/control` (deploy/destroy/dev/log) need a double that never spawns alchemy or containers. Export a fixture-backed double from a published entrypoint (placement judged against the existing `./control` shim in `packages/9-public/composer`): same operation signatures, same `Result<…, CliStructuredError>` shapes, per-operation fixtures overridable per test, including a `DevSession` double whose lifecycle methods behave. Add a compile-time conformance check that the double's surface matches the real operations.

## Verification (all must pass before pushing)

`pnpm build`, `pnpm typecheck`, `pnpm lint`, `pnpm lint:casts` (delta 0), `pnpm lint:deps` (all sub-checks), `@internal/cli` and integration test suites, `pnpm run check:npm-effect-resolution`. Known pre-existing failures that are not yours to fix: the `@internal/local-target` timeout pair.

## Commit discipline

Explicit staging only. `git commit -s --trailer "Signed-off-by: Will Madden <madden@prisma.io>"`, body ends with `Co-Authored-By: <your model attribution>`. Composer's `origin` in the operator's clones is the bot SSH alias; verify the target PR is open before pushing to an existing PR branch.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Daemon library — design conclusions, parked

Status: **excluded from the CLI-engine scope** (Will, 2026-08-09) — it is a
runtime dependency of product control clients, orthogonal to the engine.
These notes preserve what the design conversation concluded so the work is
picked up, not re-derived. Evidence citations: `output-modes-survey.md`
(emulators/daemons section).

## The finding that shaped the engine

"Daemon mode" needs **zero engine surface**. Commands touching daemons are
ordinary commands: `ls` is a result command presenting a table over
`scan()`; `stop` presents a result over `stop()`; `composer dev` calls
`ensure()` during startup then runs as a normal session command. The
daemon-ness lives in what handlers do (operations layer), like spawning
alchemy already does.

## What the library is

The lifecycle-and-discovery primitives that Composer's
`dev-emulators/src/daemon.ts` and `@prisma/dev`'s state layer each
hand-built (convergent evolution — the evidence they're one concept):

- **ensure(name, entry, opts)** — idempotent start: read registry entry,
probe health (identity/version-matched), adopt a healthy same-version
daemon, terminate-and-replace a stale-version one, spawn detached+unref
when absent, record `{pid, port, version, logPath}`, await health — all
serialized under a lockfile so concurrent CLI invocations can't race.
- **stop(name)** — SIGTERM, grace, SIGKILL, remove entry.
- **scan() / status(name)** — registry entries probed to
running/starting/dead.
- **logs(name)** — per-daemon stdio log file.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- Daemon-side: an entry-script harness (bind localhost port, serve
/health + the product's admin API, SIGTERM cleanup).

Each daemon's **admin API stays product-owned**; the library owns only
lifecycle and discovery. Registry entries need a product-data extension
slot (same shape of reasoning as the engine's R14).

## Open questions when picked up

1. **Unified machine-wide registry vs per-product registries + an
aggregating command.** Unified (one `ls` shows Composer emulators and
dev servers; one stop semantics; the second liveness implementation
stops existing) costs a real `@prisma/dev` internal migration
(`server.json` format, its `proper-lockfile` usage) including
old-format servers; per-product costs nothing now but keeps two
liveness protocols forever and adds one per future daemon.
2. **The package's home.**
3. The management-command surface the grammar parked (`emulator`
root: ls/stop/status) — the gap that today lets Composer leave
daemons on the machine with no user-facing way to list or stop them
(`stopDaemon` is "not called by any v1 command").

## Effect on @prisma/dev (under unification)

Public API (`startPrismaDevServer`, scan/status surface) unchanged;
internals swap to the shared library; its domain fields (ports, exports)
ride the registry's extension slot; migration must handle servers created
under the old on-disk format.
Loading
Loading