Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
7569c09
drive(a2): spec and plan for prisma deploy list
wmadden-electric Oct 27, 2026
a01ac36
feat(lowering,cli): expose a stage's deployed state and the app ident…
wmadden-electric Oct 27, 2026
2fbd714
refactor(lowering): a stage with no topology reads as empty, not absent
wmadden-electric Oct 27, 2026
d092a1c
feat(composer-cli): deploy list lists what an app has deployed on a s…
wmadden-electric Oct 27, 2026
25d0a12
test(composer-cli): write deploy list's entry modules at test time
wmadden-electric Oct 27, 2026
302cb10
test(cli): the command-stdout check reads deploy list's sources
wmadden-electric Oct 27, 2026
2a9cbec
fix(composer-cli): deploy list rejects an invalid --stage before the …
wmadden-electric Oct 27, 2026
37d626b
fix(composer-cli): deploy list shows a service URL as the platform re…
wmadden-electric Oct 27, 2026
32e5053
refactor(scripts): read a stage's platform state in one shared module
wmadden-electric Oct 27, 2026
906a4db
ci(e2e): check deploy list --json against the platform after each rea…
wmadden-electric Oct 27, 2026
17c3d90
docs(design): record deploy list in the command family
wmadden-electric Oct 27, 2026
2e16ca7
docs(guides,skill): show users how to list what is deployed
wmadden-electric Oct 27, 2026
e902dbe
drive(a2): code review and system-design review of deploy list
wmadden-electric Oct 27, 2026
3ee8e3b
refactor(lowering): keep one table of which resource types have a pla…
wmadden-electric Oct 27, 2026
aa414ec
test(scripts): check each listed node's kind against its topology node
wmadden-electric Oct 27, 2026
90cf60c
fix(composer-cli): deploy list names PRISMA_WORKSPACE_ID when the cre…
wmadden-electric Oct 27, 2026
5464a84
fix(lowering): readDeployedStage fails when the container resolves no…
wmadden-electric Oct 27, 2026
aa817bd
drive(a2): record QA of deploy list against the real platform
wmadden-electric Oct 27, 2026
8f31853
docs(deploy-list): no release version for prisma; record review fixes
wmadden-electric Oct 27, 2026
844b31e
ci(e2e): the deploy list check declares the ORM's config section
wmadden-electric Oct 27, 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
25 changes: 25 additions & 0 deletions .drive/projects/node-addressing/slices/a2-deploy-list/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# A2 — dispatch plan

Spec: [spec.md](spec.md). Branch `drive/deploy-list`, cut from `origin/main` `87d001de`. Dispatches run in order. The PR opens after review and QA.

### D1. The command

- **Outcome:** `deploy list` in composer-cli's family, with `--stage`, `--name`, `--entry`, and human, stdout and JSON output as the spec says. The join and kind mapping are a pure function with unit tests: root, service, both Postgres types, bucket, module, a node with no row, a missing topology. The command test uses a fake Management API client.
- **Gate:** `pnpm typecheck`, `pnpm lint`, `pnpm lint:deps`, composer-cli tests.

### D2. The end-to-end check and the docs

- **Outcome:** the deploy-verify-destroy action runs `deploy list --json` after deploy and checks it against the platform rows. The design records and guides from the spec are updated.
- **Gate:** `pnpm lint`; the e2e workflow on the PR.

### Review

`/drive-code-review` on the branch (no walkthrough). Fix findings in follow-up dispatches.

### D3. prisma-cli mounts the command

- **Outcome:** a prisma/prisma-cli PR adds `"deploy list"` next to `"emulator list"`, once the composer-cli release that has it is out.

### D4. QA

- **Outcome:** deploy an example to a named stage on the real platform, run `deploy list` for it and for production, check every node, then delete the test Project.
129 changes: 129 additions & 0 deletions .drive/projects/node-addressing/slices/a2-deploy-list/qa.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# QA — A2 `deploy list` against the real platform

Run 2026-10-27 on `drive/deploy-list` at `5464a84d`, after `pnpm build`, under bun, from `examples/storefront-auth`.

Environment: `~/.config/prisma-compose/deploy.env` (service token, workspace, Alchemy password), plus `PRISMA_REGION=us-east-1` and `AUTH_SIGNING_SECRET=sk_test_ci_storefront_auth`, as CI sets them.

The installed `prisma` does not mount `deploy list` yet (D3). The human tables below come from a scratch copy of `scripts/e2e-verify-deploy-list.ts` that mounts the same Composer family with the same `createCli` options, passes its arguments through, and prints stdout and stderr as they are. The human table goes to stderr; the tab-separated lines go to stdout.

App name `a2-qa-99d5e9`, named stage `qa-99d5e9`.

## Results

| Check | Result |
| --- | --- |
| Deploy to production | PASS |
| `e2e-verify-deploy-list.ts` on production | PASS |
| Human table on production | PASS |
| Deploy to the named stage | PASS |
| `e2e-verify-deploy-list.ts` on the named stage (`STAGE=qa-99d5e9`) | PASS |
| Human table on the named stage | PASS |
| `deploy list --stage does-not-exist` fails with `DEPLOY.TARGET_NOT_FOUND` | PASS |
| Project deleted and gone | PASS |

The named stage's listing shows its own rows (`db_paj9…`, `cps_eju3…`, `cps_c0q5…`), not production's, though the Project holds rows for both Branches with the same `logicalId`s.

## Production

```
$ bun ../../node_modules/.bin/prisma deploy --name a2-qa-99d5e9
...
✔ Deployed a2-qa-99d5e9 to production in 46.7s.
```

```
$ STACK_NAME=a2-qa-99d5e9 bun ../../scripts/e2e-verify-deploy-list.ts
deploy list --name a2-qa-99d5e9 --json exited 0:
{"kind":"result","envelope":{"ok":true,"commandId":"deploy.list","result":[{"address":"a2-qa-99d5e9","kind":"project","platformId":"proj_x46eyjv91k1femz0acf82vdt","url":null},{"address":"a2-qa-99d5e9.auth","kind":"module","platformId":null,"url":null},{"address":"a2-qa-99d5e9.auth.database","kind":"postgres","platformId":"db_wi2h43um3td2e828a689xw08","url":null},{"address":"a2-qa-99d5e9.auth.service","kind":"service","platformId":"cps_v4lvdnj8la6pqcugsglxv1b5","url":"https://v4lvdnj8la6pqcugsglxv1b5.ewr.prisma.build"},{"address":"a2-qa-99d5e9.storefront","kind":"service","platformId":"cps_cnd2dlaije0vyimghp7qfdp6","url":"https://cnd2dlaije0vyimghp7qfdp6.ewr.prisma.build"}],...}}

Topology on branch br_k0h9v9o7cfz9sz1y2ueuayri:
module a2-qa-99d5e9
module a2-qa-99d5e9.auth
resource a2-qa-99d5e9.auth.database
service a2-qa-99d5e9.auth.service
service a2-qa-99d5e9.storefront
Rows in project proj_x46eyjv91k1femz0acf82vdt:
app cps_cnd2dlaije0vyimghp7qfdp6 branch=br_k0h9v9o7cfz9sz1y2ueuayri logicalId=a2-qa-99d5e9.storefront
app cps_v4lvdnj8la6pqcugsglxv1b5 branch=br_k0h9v9o7cfz9sz1y2ueuayri logicalId=a2-qa-99d5e9.auth.service
database db_wi2h43um3td2e828a689xw08 branch=br_k0h9v9o7cfz9sz1y2ueuayri logicalId=a2-qa-99d5e9.auth.database
deploy list shows every topology node with its platform ID and URL.
```

```
$ deploy list --name a2-qa-99d5e9 --format human
Address Kind Platform ID URL
a2-qa-99d5e9 project proj_x46eyjv91k1femz0acf82vdt —
a2-qa-99d5e9.auth module — —
a2-qa-99d5e9.auth.database postgres db_wi2h43um3td2e828a689xw08 —
a2-qa-99d5e9.auth.service service cps_v4lvdnj8la6pqcugsglxv1b5 https://v4lvdnj8la6pqcugsglxv1b5.ewr.prisma.build
a2-qa-99d5e9.storefront service cps_cnd2dlaije0vyimghp7qfdp6 https://cnd2dlaije0vyimghp7qfdp6.ewr.prisma.build
```

## Named stage

```
$ bun ../../node_modules/.bin/prisma deploy --name a2-qa-99d5e9 --stage qa-99d5e9
...
✔ Deployed a2-qa-99d5e9 to qa-99d5e9 in 45.3s.
```

```
$ STACK_NAME=a2-qa-99d5e9 STAGE=qa-99d5e9 bun ../../scripts/e2e-verify-deploy-list.ts
deploy list --name a2-qa-99d5e9 --stage qa-99d5e9 --json exited 0:
{"kind":"result","envelope":{"ok":true,"commandId":"deploy.list","result":[{"address":"a2-qa-99d5e9","kind":"project","platformId":"proj_x46eyjv91k1femz0acf82vdt","url":null},{"address":"a2-qa-99d5e9.auth","kind":"module","platformId":null,"url":null},{"address":"a2-qa-99d5e9.auth.database","kind":"postgres","platformId":"db_paj9wnn6izpx5ma5p9hv1pyo","url":null},{"address":"a2-qa-99d5e9.auth.service","kind":"service","platformId":"cps_eju3icljkbwes2op4hoivjkd","url":"https://eju3icljkbwes2op4hoivjkd.ewr.prisma.build"},{"address":"a2-qa-99d5e9.storefront","kind":"service","platformId":"cps_c0q52ww83ng7uzn387pjtyvb","url":"https://c0q52ww83ng7uzn387pjtyvb.ewr.prisma.build"}],...}}

Topology on branch br_yf243sg4fu64rgxcu1inhfvv:
module a2-qa-99d5e9
module a2-qa-99d5e9.auth
resource a2-qa-99d5e9.auth.database
service a2-qa-99d5e9.auth.service
service a2-qa-99d5e9.storefront
Rows in project proj_x46eyjv91k1femz0acf82vdt:
app cps_cnd2dlaije0vyimghp7qfdp6 branch=br_k0h9v9o7cfz9sz1y2ueuayri logicalId=a2-qa-99d5e9.storefront
app cps_v4lvdnj8la6pqcugsglxv1b5 branch=br_k0h9v9o7cfz9sz1y2ueuayri logicalId=a2-qa-99d5e9.auth.service
app cps_eju3icljkbwes2op4hoivjkd branch=br_yf243sg4fu64rgxcu1inhfvv logicalId=a2-qa-99d5e9.auth.service
app cps_c0q52ww83ng7uzn387pjtyvb branch=br_yf243sg4fu64rgxcu1inhfvv logicalId=a2-qa-99d5e9.storefront
database db_wi2h43um3td2e828a689xw08 branch=br_k0h9v9o7cfz9sz1y2ueuayri logicalId=a2-qa-99d5e9.auth.database
database db_paj9wnn6izpx5ma5p9hv1pyo branch=br_yf243sg4fu64rgxcu1inhfvv logicalId=a2-qa-99d5e9.auth.database
deploy list shows every topology node with its platform ID and URL.
```

```
$ deploy list --name a2-qa-99d5e9 --stage qa-99d5e9 --format human
Address Kind Platform ID URL
a2-qa-99d5e9 project proj_x46eyjv91k1femz0acf82vdt —
a2-qa-99d5e9.auth module — —
a2-qa-99d5e9.auth.database postgres db_paj9wnn6izpx5ma5p9hv1pyo —
a2-qa-99d5e9.auth.service service cps_eju3icljkbwes2op4hoivjkd https://eju3icljkbwes2op4hoivjkd.ewr.prisma.build
a2-qa-99d5e9.storefront service cps_c0q52ww83ng7uzn387pjtyvb https://c0q52ww83ng7uzn387pjtyvb.ewr.prisma.build
```

## A stage that does not exist

```
$ deploy list --name a2-qa-99d5e9 --stage does-not-exist --json
{"kind":"result","envelope":{"ok":false,"commandId":"deploy.list","error":{"code":"DEPLOY.TARGET_NOT_FOUND","severity":"error","summary":"Nothing deployed for a2-qa-99d5e9/does-not-exist.","nextActions":[{"kind":"user-choice","label":"Deploy it first."}],...}}
exit 2
```

## Teardown

```
$ bun ../../scripts/composer-destroy.ts --name a2-qa-99d5e9 --stage qa-99d5e9
Done: 18 succeeded (27.5s)
Destroyed.

$ bun ../../scripts/composer-destroy.ts --name a2-qa-99d5e9 --production
Done: 18 succeeded (28.7s)
Removed the Project (proj_x46eyjv91k1femz0acf82vdt) — nothing was left in it.
Destroyed.

$ curl -H "Authorization: Bearer $PRISMA_SERVICE_TOKEN" https://api.prisma.io/v1/projects/proj_x46eyjv91k1femz0acf82vdt
404

$ deploy list --name a2-qa-99d5e9 --json
... "code":"DEPLOY.TARGET_NOT_FOUND","summary":"Nothing deployed for a2-qa-99d5e9." ...
exit 2
```

No Project named `a2-qa-99d5e9` remains in the workspace's project list.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Code review — A2 `prisma deploy list`

Range: `origin/main...drive/deploy-list` (merge base `87d001de`, head `2e16ca74`). Spec: [../spec.md](../spec.md).

## Summary

The command does what the spec says, the join is correct, and the tests are well aimed. The gaps are in the end-to-end check (it does not check `kind`, and it only runs on production), one unhelpful error, and the resource-type table being copied a third time.

Checked locally: `bun test` for `test/deploy-list.test.ts` and `test/deployed-nodes.test.ts` (21 pass), `scripts/deploy-list-check.test.ts` (8 pass), `pnpm lint:deps` (clean). The Management API SDK's `getV1Services`, `getV1Databases` and `getV1Buckets` all accept `projectId` and `branchId` filters, so the Branch filter is real and rows from another stage with the same `logicalId` cannot leak in.

## What looks solid

- The join is a pure function with one test per kind, plus missing-row, empty and order cases.
- The command test asserts the actual request parameters (`branchId` on `/v1/services`, `gitName` on the Branch lookup), not just the output.
- An invalid `--stage` is rejected before any network call, and the test asserts that no request was made.
- The Management API code is lazily imported, so loading the family stays cheap.
- The e2e check reads the platform independently of the command, through `/v1/apps` and its own Branch filter.

## Findings

### F01 — The e2e check never compares `kind` (should-fix)

- **Location:** `scripts/deploy-list-check.ts` lines 63–86.
- **Issue:** `deployListMismatches` checks address order, platform ID and URL, but not `kind`. A regression that printed `module` for every node, or `raw-postgres` instead of `postgres`, would pass the real-platform check.
- **Suggestion:** Compute the expected kind from the topology node (`project` for the root, `service`, `postgres` for both Postgres types, `bucket` for `s3`, `module`, otherwise the raw `type`) and report a mismatch. Add a case to `scripts/deploy-list-check.test.ts`.

### F02 — A credential with no workspace gives an unhelpful error (should-fix)

- **Location:** `packages/9-public/composer-cli/src/family/list-deployed.ts` lines 47–49.
- **Issue:** It fails with `DEPLOY.CONTAINER_FAILED` and the message "The active credential names no workspace.", with no `fix`. `prisma deploy` in the same situation says `PRISMA_WORKSPACE_ID` is required (`packages/1-prisma-cloud/1-extensions/target/src/container.ts` line 163). The user learns what is wrong but not what to do, and the two commands disagree.
- **Suggestion:** Use the same message deploy uses, or add a `fix` naming `PRISMA_WORKSPACE_ID`. Add a command test for it.

### F03 — The target's resource types are restated a third time (should-fix)

- **Location:** `packages/9-public/composer-cli/src/family/deployed-nodes.ts` lines 11–15.
- **Issue:** `RESOURCE_KINDS` repeats which target types (`postgres`, `raw-postgres`, `s3`) have a database or bucket row. The same table is in `scripts/platform-identity.ts` (lines 23–27) and `packages/1-prisma-cloud/1-extensions/target/src/local-target/emulators.ts` (line 43). If the target adds a database type, `deploy list` shows its raw type with no platform ID, and nothing fails: the e2e check reads its own copy.
- **Suggestion:** Export one map from resource type to row list from the Prisma Cloud domain (for example next to `readDeployedStage`) and use it in `deployed-nodes.ts` and `scripts/platform-identity.ts`.

### F04 — The e2e check only covers production (should-fix)

- **Location:** `.github/actions/deploy-verify-destroy/action.yml` lines 68–74.
- **Issue:** The step sets no `STAGE`, and the action only deploys production. The named-stage path (Branch by `gitName`, rows filtered by that Branch) is proven only against the fake client. The spec's done condition asks for "a real deployed stage".
- **Suggestion:** Make D4 QA explicitly run `deploy list --stage <name>` against a real named-stage deploy and record the output in the slice, since the e2e workflow has no stage deploy to reuse.

### F05 — Dead branch in `readDeployedStage` (nit)

Comment thread
wmadden-electric marked this conversation as resolved.
Resolved
- **Location:** `packages/1-prisma-cloud/0-lowering/lowering/src/deployed.ts` lines 46–47.
- **Issue:** `resolveContainer` always returns `branchId` for a named stage and `defaultBranchId` for production, so `branchId === undefined` cannot happen. Returning `undefined` there would read as "nothing deployed", which hides a real bug if it ever did happen.
- **Suggestion:** Fail with an error ("resolved no Branch") as `scripts/platform-state.ts` does, instead of returning `undefined`.

### F06 — The human-table test checks only two substrings (nit)

- **Location:** `packages/9-public/composer-cli/test/deploy-list.test.ts` lines 240–248.
- **Issue:** It asserts that stderr contains `shop.catalog.db` and the URL. It would pass if the columns were missing or misordered.
- **Suggestion:** Also assert the header row (`Address`, `Kind`, `Platform ID`, `URL`) and that one row shows `shop.catalog.db`, `postgres` and `db_1` on the same line.

### F07 — The release claim depends on work not yet done (nit)

- **Location:** `docs/guides/deploying.md` lines 321–322, `skills/prisma-composer-core-concepts/SKILL.md` line 408.
- **Issue:** Both say `prisma deploy list` "first ships in the `prisma` release after 8.0.0-rc.26". That depends on the D3 prisma-cli pull request, which has not been opened.
- **Suggestion:** Recheck the version when D3 merges and correct both lines if it ships later.

## Deferred (out of scope)

- **Read failures use `DEPLOY.CONTAINER_FAILED`.** A topology or row read that fails with a 500 surfaces as a container failure (`list-deployed.ts` lines 27–33). It is the nearest existing ADR-0044 code; a read-specific code would mean amending ADR-0044, which is beyond this slice.
- **Two rows with one `logicalId` on a Branch.** The join takes the first match (`deployed-nodes.ts` lines 29–31 and 40). `e2e-verify-identity.ts` already fails a deploy that leaves duplicates, so this is guarded elsewhere.

## Already addressed

After this review, fixed on the branch:

- F01: `aa414ec7`. The check compares `kind`.
- F02: `90cf60cf`. The error says `PRISMA_WORKSPACE_ID` is required, as deploy does.
- F03: `3ee8e3b9`. One map, `RESOURCE_ROW_KINDS`, in `lowering/src/resource-rows.ts`, used by the command, the scripts and the emulators.
- F04: real named-stage QA in [qa.md](../qa.md).
- F05: `5464a84d`. A missing Branch fails with an error.
- F06: `90cf60cf`. The test checks the header and two full rows.
- F07: the docs no longer name a release version.

## Acceptance-criteria verification

The spec has no numbered acceptance criteria. These are its "Chosen design" bullets and done conditions, read as criteria.

| AC | Verdict | Detail |
| --- | --- | --- |
| AC1: Lives in composer-cli next to `emulator list` | **PASS** | `src/exports/family.ts` adds `'deploy list'`; test "the published family ships it". |
| AC2: Reads the application topology, not Alchemy state | **PASS** | `deployed.ts` lines 50–54 read only the topology endpoint; the fake client throws on any other route, and the command tests pass. |
| AC3: Join by `logicalId` in the Branch's lists, filtered by project and Branch; root is the Project | **PASS** | `deployed.ts` lines 67–77 send `projectId` and `branchId`; `deploy-list.test.ts` lines 160–162 assert the query; `deployed-nodes.test.ts` covers root and the `logicalId` match among decoys. |
| AC4: Kind mapping (`project`, `service`, `postgres` for both, `bucket`, `module`, else raw type; empty ID with no row) | **PASS** | `deployed-nodes.test.ts` has one test per case, including `s3-credentials` and a missing row. The e2e check does not verify kind (F01). |
| AC5: URL is the service's `appEndpointDomain` | **PASS** | Unit test and command test assert `url: 'https://api.prisma.app'` from the row; e2e compares it against `/v1/apps`. |
| AC6: Stage lookup is find-only by `logicalId` and `gitName`, production by default; `--name` and `--entry` work | **PASS** | Command tests assert the `gitName` query and the Branch used for the topology, and the Project chosen under `--name` and `--entry`. |
| AC7: Nothing deployed fails with `DEPLOY.TARGET_NOT_FOUND`, naming app and stage | **PASS** | Table test covers no Project, no Branch (message names `shop/pr-42`), 404 topology and empty topology. |
| AC8: Human table, tab-separated stdout, `--json` array with `null` | **PASS** | stdout test asserts the exact text; JSON test asserts the exact array with `null`s. The human-table test is loose (F06). |
| AC9: prisma-cli mounts `deploy list` | **NOT VERIFIED** | D3, not in this branch. |
| AC10: Design records and guides updated | **PASS** | ADR-0050 and ADR-0053 amendments, the ADR index, `deploy-cli.md`, `deploying.md`, `running-locally.md` and the skill all describe the command consistently with the code. |
| Done 1: Against a real deployed stage, every node has the right IDs and URLs | **NOT VERIFIED** | Production is covered by the e2e step once it runs in CI; a named stage is not covered anywhere yet (F04). D4 QA pending. |
| Done 2: The e2e workflow runs it after deploy and checks `--json` against the platform | **WEAK** | The step exists and checks order, IDs and URLs, but not kind (F01). It has not been seen running on a pull request yet. |

### Summary

| Result | Count | ACs |
| --- | --- | --- |
| PASS | 9 | AC1–AC8, AC10 |
| FAIL | 0 | |
| NOT VERIFIED | 2 | AC9, Done 1 |
| WEAK | 1 | Done 2 |
Loading
Loading