Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
153 changes: 153 additions & 0 deletions .drive/projects/prisma-cli-v8/assets/briefs/s3-closeout-handover.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# S3 close-out handover — Composer adoption

Written 2026-08-12 for an agent with NO prior context. The operator
is Will Madden. Slice S3 (Composer adoption) is **substantially
done and merged**; what remains is one open PR, a short list of
follow-ups, and the slice's formal close-out.

## 1. What S3 was, and its state

Repo `prisma/prisma-cli` is the v8 Prisma CLI, built on
`@prisma/cli-engine` (`packages/cli-engine`). S3 made **Composer**
(repo `prisma/composer`) the engine's first cross-repo consumer:
composer's four commands — `deploy`, `destroy`, `dev`, `log` — are
now engine commands, composer's own CLI is a thin composition of
them, and the `prisma` binary mounts the same family.

**Merged (all of it):**

| PR | What |
| --- | --- |
| prisma-cli #136 | `ctx.spawn` — terminal handoff with credential injection |
| prisma-cli #145 | the engine settles signal-terminated runs (Ctrl-C → 130) |
| prisma-cli #150 | the engine records the child; `ctx.lastChild()`; `exitWithChildStatus()` loses its argument |
| prisma-cli #151 | a handler can fail with more than one finding |
| prisma-cli #155 | the engine detects CI itself; hosts stop answering |
| prisma-cli #152 | **the mount** — composer's family in the v8 bin (`42ee7891`) |
| composer #220 | composer's CLI becomes four commands on the engine |
| composer #224 | engine pin 0.0.9; Node floor 24 → 22.18 |

**Open, approved, needs merging: composer #226** — a CI check that
imports all 16 published entrypoints on the Node floor. It was
`BEHIND` main; I merged main into it and pushed (`32c85f27`).
Confirm its checks go green, then merge it. Nothing else blocks it.

## 2. Read these before touching anything

- `.drive/projects/prisma-cli-v8/specs/s3-composer.md` — the slice
contract, rev 2 final. Normative. Its §10 records every amendment
made during the slice.
- `.drive/projects/prisma-cli-v8/deferred.md` — **the live list of
everything carried out of this slice**, grouped by owner. Read it
in full; most of §3 below is a pointer into it.
- `.drive/projects/prisma-cli-v8/plan.md` — the project plan and
coverage ledger (corrected during S3; see §3).
- `.drive/projects/prisma-cli-v8/assets/s2/parity-divergences-s3.md`
— the nine user-visible changes from composer's own CLI.
- `.drive/projects/prisma-cli-v8/assets/s3/composer-inventory.md` —
what composer's CLI did before the port. Still the reference for
any behavioural question.

## 3. What is left

### Immediate
1. **Merge composer #226** (above).
2. **Composer should drop its `isCI` answer.** prisma-cli #155 made
`Runtime.isCI` the optional `isCIOverride` — the engine detects
CI itself now. Composer still passes `isCI` (via `ci-info`) in
`packages/0-framework/3-tooling/cli/src/family/runtime.ts`
because it pins engine `0.0.9`, which predates #155. Harmless
today. Drop the parameter and the `ci-info` dependency when
composer next bumps its engine pin.

### The engine-copy problem — needs the lockstep release
An install of `@prisma/cli` resolves **two** copies of the engine:
this repo ships its own at `8.0.0-rc.1` while composer pins the
published `0.0.9`. Two exact pins on two release lines cannot
dedupe. It works only because values crossing the boundary are
matched by `Symbol.for` rather than by identity, and **only the
structured-error crossing is tested**. It collapses when both sides
name the same engine version — i.e. when composer pins the lockstep
version `publish.yml` ships. Do not attempt to fix it by pinning a
different `0.0.x`; that changes nothing.

### Everything else
`deferred.md` is the record. Notable entries, so you know they
exist without reading it cold:
- **Composer's help examples are wrong under the prisma bin.** They
read `{bin} deploy src/service.ts`, so `prisma composer deploy
--help` shows `prisma deploy src/service.ts`, which exits 2. Eight
examples, two per command. Needs a mount-aware placeholder in the
engine plus composer rewriting its strings.
- **R-S3-2's diagnostics list still has no consumer.** Composer
builds a list of every config problem but commands still fail on
the first. Engine #151 supplied the missing surface (a failure can
now carry findings); wiring composer to it is the remaining work.
- **Two flaky tests under load**, both the same shape (a child
writes its ready marker before installing its signal handler):
`packages/cli/tests/v8-spawn-adapter.test.ts` and
`packages/cli-engine/tests/spawn-real-child.test.ts`.
- **`pnpm --filter @prisma/cli test` can pass against a stale engine
build** — it does not build first. Run the engine suite before it,
or use `turbo run test`.
- **The alchemy exit-hook patch** in composer
(`patches/@alchemy.run__node-utils@0.0.5.patch`) is vendored from
the open alchemy-run/node-utils#6. Delete it when that ships
through the chain.

## 4. Closing the slice

S3 is not formally closed. Per the drive process, close-out means:
verify the contract's acceptance list against what actually shipped
(the contract's §10 already records the amendments), fold anything
still true into `deferred.md`, and update `plan.md`'s slice table.
**Do not claim acceptance items that were amended away** — read §10
first; several were, deliberately.

The next slice by the plan's dependency graph is **S8** (service
primitives), whose design questions S3 answered — the answers are
in `deferred.md` and the inventory's §4a. **S5** (ORM adoption) has
its own brief at `assets/briefs/s5-orm-handover.md` and is
independent.

## 5. Process rules (operator-enforced)

- **Git identity is the `wmadden-electric` bot**: stage explicitly
by path (NEVER `git add -A`; NEVER anything under
`.drive/projects/prisma-cli-v8/specs/reviews/` or `wip/`); commit
`git commit -s --trailer "Signed-off-by: Will Madden <madden@prisma.io>"`
with body ending `Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>`;
push only to the bot remote (`bot` in prisma-cli, `origin` in the
composer clone — both are the `github-wmadden-electric` SSH alias).
- **Verification**: `pnpm --filter @prisma/cli test`,
`pnpm --filter @prisma/cli-engine test`,
`pnpm --filter @repo/cli-telemetry test`, `pnpm typecheck`,
`pnpm lint` — all must exit 0. **Lint fails on warnings.** Run
suites sequentially; parallel runs race the engine build.
- **Composer clone** lives at
`.claude/worktrees/s3-composer/wip/work/composer`. Its checks:
`pnpm build`, typecheck, `lint`, `lint:casts` (no ratchet delta),
`lint:deps`, `@internal/cli` suite, `check:cli-engine-pin`,
`check:family-static-graph`, `check:npm-effect-resolution`,
`check:publish-deps`, `check:floor-imports`. Known pre-existing
failures NOT yours: `@internal/dev-emulators` /
`@internal/local-target` when a leftover Prisma Dev daemon holds
ports 51316–51325.
- **Every PR**: address every CodeRabbit thread — fix it, or decline
it with verified evidence — then reply and resolve each thread
before merging. Several of its findings this slice were correct
where our own records were wrong.
- **PR descriptions** (ruled): a grounding example first (a real
command run), then the decision, then the narrative, alternatives
last. No internal process codes.
- **Reports to the operator**: plain English, short. Report outcomes,
decisions he must make, and changes to his world — nothing else.
Banned words: "load-bearing", "smoking gun", "belt and suspenders",
"gate". Bring questions to decide, not decisions to ratify. Never
use the question UI. Give absolute paths and branch-qualified
GitHub URLs, never relative links. **Never use spawn_task chips** —
follow-ups go in `deferred.md`.
- **Verify claims against source before asserting them.** This slice
produced several confident statements that were wrong (composer
"fails on Node 22" — it does not; the effect check "never runs" —
it does). Read the code or the published package.
7 changes: 7 additions & 0 deletions .drive/projects/prisma-cli-v8/deferred.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ composer can use it.
`0.6.0-dev.16` bundles. The effect-resolution check is unaffected and
does run: it sits in `configSource`, the front both loader shapes
share, so the throwing path the pipeline uses runs it first.
- **Composer should drop its `isCI` answer.** prisma-cli #155 made
`Runtime.isCI` the optional `isCIOverride` — the engine detects CI
itself now. Composer still passes `isCI` (via `ci-info`) in
`packages/0-framework/3-tooling/cli/src/family/runtime.ts` because
it pins engine `0.0.9`, which predates #155. Harmless today. Drop
the parameter and the `ci-info` dependency when composer next bumps
its engine pin.
- **`check:npm-effect-resolution` fixes are unverified.** D3 updated
three assertions (help proves the family mounted; the adversarial
`deploy` gets a service token because the credential check now
Expand Down
5 changes: 5 additions & 0 deletions .drive/projects/prisma-cli-v8/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ deleted, per-family shell integration proofs, parity list reviewed.

### S3 — Composer adoption (first cross-repo consumer)

**CLOSED 2026-08-12** — shipped as prisma-cli #136/#145/#150/#151/#155
plus the mount (#152) and composer #220/#224/#226; acceptance verified
in `specs/s3-composer.md`'s Close-out section; leftovers in
`deferred.md`. Next by the dependency graph: S8 (design first).

Repos: composer + prisma-cli. Composer exports a `CommandFamily`
(the `composer` config section token — its validator rewritten from the
current throwing loader per the section API — plus its command set);
Expand Down
74 changes: 58 additions & 16 deletions .drive/projects/prisma-cli-v8/specs/s3-composer.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# S3 — Composer adoption (slice contract, revision 2 final)

Status: rev 2 final (2026-08-11) — the first-principles design
(operator-settled) plus both delta reviews folded (architect + PE,
accept-with-changes; all findings adopted, dispositions in §10).
Status: CLOSED 2026-08-12 (acceptance verified; dispositions in the
Close-out section). Was rev 2 final (2026-08-11) — the
first-principles design (operator-settled) plus both delta reviews
folded (architect + PE, accept-with-changes; all findings adopted,
dispositions in §10).
Precedence: this contract > `specs/s2-overview.md` standing rulings
> `assets/s3/composer-inventory.md` > source. Unpinned facts are
STOP-and-surface.
Expand Down Expand Up @@ -345,48 +347,50 @@ operator); 1c deliverable 2 beyond the config-load effect check

## Acceptance

- [ ] Engine (real child, trivial script): exit passthrough incl.
- [x] Engine (real child, trivial script): exit passthrough incl.
1/2/3; ENOENT structured error; native Ctrl-C reaching the
child (POSIX; fake-level on Windows); record-and-replay
after child exit (one signal → abort; two → escalation);
SIGTERM forwarded during window; abort ladder
TERM→grace→KILL; engine-outlives-child; unframed child
stdout; buffered events flushed in order.
- [ ] Engine (fake spawn): `--json` parse rejection;
- [x] Engine (fake spawn): `--json` parse rejection;
near-expiry refusal; env composition both session sources;
env KEYS never values; reentrancy construction error;
telemetry settlement; the env-only credential manager's
composition + mutation refusals.
- [ ] SPI amendment recorded (single named call site + the
- [x] SPI amendment recorded (single named call site + the
one-client invariant outcome).
- [ ] Composer family static graph alchemy-free + effect-free on
- [x] Composer family static graph alchemy-free + effect-free on
BUILT output (CI check anchored at the family entrypoint);
executors remain behind the dynamic-import boundary; the
engine-sole-listener DETECTOR assertion after config
evaluation and local-target resolution (see design
consequence 4 — no workaround behind it, by ruling).
- [ ] Composer's rebuilt CLI (engine + own family + env-only
- [x] Composer's rebuilt CLI (engine + own family + env-only
manager) replaces clipanion; e2e tests drive the exported
commands; old shell/runner deleted in D3; D2/D3 stacked.
- [ ] Four commands green in composer CI (double + fake child; no
- [x] Four commands green in composer CI (double + fake child; no
alchemy, no containers) and mounted under `composer` in the
prisma bin.
- [ ] Tarball checks: engine external + exact pin; double's chunk
- [x] Tarball checks: engine external + exact pin; double's chunk
import-clean; dual-manifest pin equality; Dependabot ignore.
- [ ] prisma bin: `node >= 24` (bin only); install-footprint
consequence recorded.
- [ ] Divergences (`assets/s2/parity-divergences-s3.md`): dev
- [x] prisma bin: `node >= 24` (bin only); install-footprint
consequence recorded. AMENDED — shipped as `>=22.18.0`; see
Close-out.
- [x] Divergences (`assets/s2/parity-divergences-s3.md`): dev
Ctrl-C 130-vs-0; `--production` dropped; reproduce-hint
shape; `--json` rejection; PATH_MISMATCH conditional
retirement; help/usage output shape + bare-invocation exit
(inventory D6/D7); `--tail` becomes a typed number flag
(D5); `[dev]`/`[log]` console prefixes become engine events;
exit unifications on engine-side error paths.
- [ ] 1c closed with explicit dispositions (D1 → R-S3-2; D2 split:
- [x] 1c closed with explicit dispositions (D1 → R-S3-2; D2 split:
config-load effect check owned here, rest composer team;
D3 → R-S3-5).
- [ ] Ledger Q2 + coverage-ledger rows corrected.
- [ ] Both PRs through the slice review loop; suites green in
- [x] Ledger Q2 + coverage-ledger rows corrected. AMENDED — Q2's
disposition changed; see Close-out.
- [x] Both PRs through the slice review loop; suites green in
both repos.

## §10 Disposition record
Expand Down Expand Up @@ -452,3 +456,41 @@ record, and settles a signal-killed child as the abort whatever
retires with the argument that made the misuse reachable; a run that
settles this way with no child on record is the construction error
that takes its place.

## Close-out (2026-08-12)

Acceptance verified against source and merged PRs: prisma-cli #136,
#145, #150, #151, #155, #152 (the mount, `42ee7891`); composer #220,
#224, #226. Evidence, per item: the real-child and fake-spawn suites
are `packages/cli-engine/tests/spawn-real-child.test.ts` and
`spawn.test.ts` (plus `environment-credential-manager.test.ts`); the
SPI amendment is `credential-manager-design.md` §11.5
(`activeAccessToken()`, single consumer `execution/spawn.ts`); the
static-graph check is composer's `check:family-static-graph` and the
sole-listener detector is composer's
`cli/src/family/__tests__/signal-listeners.test.ts`; the tarball
checks are composer's `check:cli-engine-pin` / `check:publish-deps`
plus the Dependabot ignore in composer's `.github/dependabot.yml`;
the 1c closure is `assets/briefs/1c-leftovers-composer.md`.

Two items shipped amended, deliberately, and are NOT claimed as
written:

- **The bin's Node floor is `>=22.18.0`, not `>=24`.** Composer #224
dropped composer's own floor to 22.18 (Node 22 suffices), and the
contract's rule — the bin takes composer's floor, max wins — held;
only the number moved. `@prisma/cli-engine` stays at 22.12 so
composer does not re-inherit a floor through the engine.
- **Ledger Q2's disposition changed.** The Out-of-scope line "ledger
Q2 closes 'mechanism built in S3'" was superseded: `service run`
was RULED dropped (operator, 2026-08-11; `s2-overview.md` Q2), so
D4 recorded that S3 built the mechanism for a command that no
longer exists rather than claiming Q2 closed by it.

Two acceptance-suite tests are flaky under load (a child writes its
ready marker before installing its signal handler); both are recorded
in `deferred.md`. Everything carried out of the slice — including the
two-engine-copy install state that only the tandem release ends, and
composer dropping its `isCI` answer at its next engine-pin bump — is
in `deferred.md`. Hand-over context for a fresh agent:
`assets/briefs/s3-closeout-handover.md`.
Loading