Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
7 changes: 4 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ jobs:
test:
name: Test
runs-on: ubuntu-latest
# Backs the prisma-alchemy state/lock suites (packages/prisma-alchemy/src/state/__tests__).
# Without this, harness.ts finds no Postgres and those suites — the
# load-bearing coverage for the hosted state store — would silently skip.
# Backs the suites that need a real Postgres via STATE_TEST_DATABASE_URL:
# examples/auth and examples/storage (tests/pg-harness.ts) and the target
# extension's integration tests (src/__tests__/postgres-harness.ts).
# Without it those harnesses find no Postgres and the suites silently skip.
services:
postgres:
image: postgres:16
Expand Down
6 changes: 0 additions & 6 deletions architecture.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -306,12 +306,6 @@
"layer": "extensions",
"plane": "control"
},
{
"glob": "packages/1-prisma-cloud/1-extensions/target/src/teardown.ts",
"domain": "prisma-cloud",
"layer": "extensions",
"plane": "control"
},
{
"glob": "packages/1-prisma-cloud/1-extensions/target/src/container.ts",
"domain": "prisma-cloud",
Expand Down
6 changes: 1 addition & 5 deletions docs/design/03-domain-model/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,11 +397,7 @@ is in `layering.md`; this is the term-by-term catalogue.
*above* providers, not inside them.
- **Stage** — an isolated instance of a Stack (`dev`, `staging`, `prod`,
`pr-42`) with its own state and physical names. `→` **Environment**.
- **State store** — persists each Resource's state per stack+stage so the engine
can diff the next deploy. `prismaCloud()` defaults every deploy to a
Prisma-hosted, workspace-scoped store (`@internal/lowering/state`); an
explicit state layer always overrides it. Control-plane infra, never a
topology node.
- **State store** — persists each Resource's state per stack+stage so the engine can diff the next deploy. `prismaCloud()` defaults every deploy to platform-hosted state behind the Management API, scoped to the stage's Branch (`@internal/lowering/state`, ADR-0045); an explicit state layer always overrides it. Control-plane infra, never a topology node.

### Alchemy — engine verbs (provider lifecycle)

Expand Down
34 changes: 3 additions & 31 deletions docs/design/03-domain-model/layering.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,40 +120,12 @@ reproduce-in-the-emulator goal (see `../00-purpose/goals.md`).

## Provisioning & state

Provisioning runs through **Alchemy's engine**, invoked from the client or a
privileged CD environment (see claim 3). The engine keeps a **state store** —
the source of truth for what's provisioned. State sits on a spectrum from
local, to branch-hosted, to eventually platform-run:
Provisioning runs through **Alchemy's engine**, invoked from the client or a privileged CD environment (see claim 3). The engine keeps a **state store** — the source of truth for what's provisioned. State sits on a spectrum from local, to platform-hosted (where we are), to eventually platform-run:

- **Local** — Alchemy's local or Cloudflare-backed state. Fine for a solo
developer; nothing else needs to see it.
- **Branch-hosted** — a `StateService` implementation
(`@internal/lowering/state`) backed by a framework-owned Prisma Postgres
database in each stage's Branch of the app's own Project (ADR-0034),
native to the Workspace → Project → Branch hierarchy
(Pulumi/Terraform-Cloud-style hosted state, without the BYO-state
bootstrap). Bootstrap is automatic: the Management API finds or creates
the stage's state database from the container ids the CLI already
resolves, so a deployer needs nothing beyond the service token and
workspace id it already has, and the state's lifetime is the
environment's — deleting the Branch or Project deletes it. Concurrency is
a per-`(stack, stage)` advisory lock, so two deployers can never race the
same stack. `prismaCloud()` supplies this as the default deploy state for
every service and Module; an explicit state layer always overrides it.
This is framework-owned operational infrastructure, not a user-topology
Resource — ambient per stage, never declared by a Module (the containers
it lives in are created before the engine runs, which sidesteps the
chicken-and-egg of provisioning the store itself). Like hosted-state
backends generally, it also holds state for the user's BYO resources in
other clouds.
- **Server-side runs** — the platform executes the apply loop itself
(git-push-style deploys). Once state is platform-hosted, moving the engine
server-side is incremental — the same evolution Pulumi/Terraform Cloud
followed. This step's platform surface is implementing Alchemy's own HTTP
`StateApi` (bearer auth → workspace RBAC) as a Management API endpoint; once
it exists, the branch-hosted store's visible databases disappear and the
platform can answer "what's provisioned in this project" natively (the
platform side of the inspectable-topology goal).
- **Platform-hosted** — the Management API implements Alchemy's own HTTP `StateApi` wire contract per Branch of the app's own Project (`…/branches/{branchId}/alchemy-state`, ADR-0045), and the framework's state layer (`@internal/lowering/state`) is Alchemy's stock HTTP client pointed at it — Pulumi/Terraform-Cloud-style hosted state, native to the Workspace → Project → Branch hierarchy, with no BYO-state bootstrap and no visible state database. A deployer needs nothing beyond the service token it already has, and the state's lifetime is the environment's — deleting the Branch or Project deletes it. Concurrency is a server-side per-`(stack, stage)` deploy lease held around the run, so two deployers can never race the same stack. `prismaCloud()` supplies this as the default deploy state for every service and Module; an explicit state layer always overrides it. Like hosted-state backends generally, it also holds state for the user's BYO resources in other clouds, and it lets the platform answer "what's provisioned in this project" natively (the platform side of the inspectable-topology goal).
- **Server-side runs** — the platform executes the apply loop itself (git-push-style deploys). With state already platform-hosted, moving the engine server-side is incremental — the same evolution Pulumi/Terraform Cloud followed.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

## Open questions

Expand Down
23 changes: 3 additions & 20 deletions docs/design/10-domains/deploy-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,26 +130,9 @@ targets **production**; `--stage <name>` targets a **named stage**.
extension; core hands that extension's own resolved container to
`state.create()`, so the state layer is built from it rather than from the
environment.
- **Destroy is explicit.** `prisma-composer destroy` requires `--stage <name>` or
`--production`; a bare `destroy` is an error, so an omitted or mistyped
stage can never silently tear down production. `destroy` resolves
find-only (no container is ever created); after `alchemy destroy` succeeds
and after every extension's `teardown` has run, the CLI removes each
resolved container. That two-loop order — every teardown, then every
removal — is what keeps a stage's deploy state deleted before its
container goes.

**Prisma Cloud's own containers** are its app's **Project** and, for a named
stage, that stage's **Branch** — found by name, created if absent on deploy,
never created on destroy; each stage's deploy state lives in a
framework-owned `prisma-composer-state` database attached to its Branch
(production's on the Project's implicit default Branch). See
[ADR-0023](../90-decisions/ADR-0023-a-prisma-app-is-one-project-a-stage-is-a-branch.md)
(App = one Project, Stage = Branch),
[ADR-0024](../90-decisions/ADR-0024-a-stage-is-a-deploy-time-environment-resolved-to-project-and-branch.md)
(stage resolution mechanics), and
[ADR-0034](../90-decisions/ADR-0034-deploy-state-lives-in-the-stage-branch.md)
(deploy state lives on the stage's Branch).
- **Destroy is explicit.** `prisma-composer destroy` requires `--stage <name>` or `--production`; a bare `destroy` is an error, so an omitted or mistyped stage can never silently tear down production. `destroy` resolves find-only (no container is ever created); after `alchemy destroy` succeeds and after every extension's `teardown` has run, the CLI removes each resolved container. That two-loop order — every teardown, then every removal — is what guarantees every extension's teardown runs against a still-live container.

**Prisma Cloud's own containers** are its app's **Project** and, for a named stage, that stage's **Branch** — found by name, created if absent on deploy, never created on destroy; each stage's deploy state lives behind the platform state API, scoped to its Branch (production's to the Project's implicit default Branch). See [ADR-0023](../90-decisions/ADR-0023-a-prisma-app-is-one-project-a-stage-is-a-branch.md) (App = one Project, Stage = Branch), [ADR-0024](../90-decisions/ADR-0024-a-stage-is-a-deploy-time-environment-resolved-to-project-and-branch.md) (stage resolution mechanics), and [ADR-0045](../90-decisions/ADR-0045-deploy-state-lives-behind-the-platform-state-api.md) (deploy state behind the platform state API, per Branch).

## Build ownership

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ADR-0010: Deploys hold a session advisory lock per stack and stage

> Superseded by [ADR-0045](ADR-0045-deploy-state-lives-behind-the-platform-state-api.md): the per-`(stack, stage)` deploy lease is now held server-side against the platform state API (TTL + heartbeat), not as a Postgres session advisory lock. The fail-fast contention behavior — refuse immediately, name the holder, never queue — is preserved.

## Decision

A deploy acquires a Postgres session advisory lock on the stage's hosted
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ADR-0012: The state store speaks SQL directly; Prisma Next adoption is deferred

> Closed as obsolete by [ADR-0045](ADR-0045-deploy-state-lives-behind-the-platform-state-api.md), via this record's own pick-up trigger: the platform-side state API landed, the SQL store is gone, and composer speaks the API through Alchemy's stock HTTP client — there is no store data layer left to adopt Prisma Next for.

## Decision

The hosted state store's data access is hand-written SQL over a plain Postgres
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ADR-0034: Deploy state lives in a framework-owned database in the stage's Branch

> Superseded in part by [ADR-0045](ADR-0045-deploy-state-lives-behind-the-platform-state-api.md): the container and lifetime reasoning stands — state is still a child of the stage's Branch, deleted with it — but the storage mechanism is replaced. State lives behind the platform state API; the visible per-stage `prisma-composer-state` database is gone.

## Decision

Each stage's deploy state — the provisioning engine's record of what exists in
Expand Down
Loading
Loading