Skip to content

fix(prisma): Prisma.Database with omitted branch attaches the project default branch - #1359

Open
kristof-siket wants to merge 4 commits into
alchemy-run:mainfrom
kristof-siket:fix/prisma-database-default-branch
Open

fix(prisma): Prisma.Database with omitted branch attaches the project default branch#1359
kristof-siket wants to merge 4 commits into
alchemy-run:mainfrom
kristof-siket:fix/prisma-database-default-branch

Conversation

@kristof-siket

Copy link
Copy Markdown
Contributor

Summary

The three Prisma resources that take an optional branch attachment interpret the same omission three different ways:

Resource branchId/branchGitName omitted means
Prisma.App resolve the project's default branch client-side and attach (desiredBranchId; fails loudly when the project has no default branch)
Prisma.Bucket pass through; the Management API attaches the default branch server-side
Prisma.Database desired state is UNASSIGNED — actively detach

The Prisma.Database reading is the odd one out, and it bites in two ways:

Downstream consumers currently work around the asymmetry by always passing branchId explicitly (see prisma/composer#258).

What changes

Prisma.Database now treats an omitted branch exactly like Prisma.App: resolve the project's default branch and converge toward it. branchId: null is never emitted.

  • New defaultBranchIdOf + resolvedBranchAttachment helpers mirror App's desiredBranchId, including the same-shaped errors for a missing or ambiguous default branch ("has no default branch to attach database … Create or promote a default branch, or specify branchId/branchGitName").
  • branchNeedsSync's omitted arm compares the observed branchId against the resolved default branch id instead of !== null.
  • The reconcile patch path writes the resolved attachment — the { branchId: null } detach PATCH is gone.
  • Generated-name creates pass the resolved default branchId in the create body, so a fresh database is born attached.
  • DatabaseProps.branchId/branchGitName lose | null; runtime null is rejected in validateDatabaseProps and an early diff guard with a clear error (mirroring validateAppProps), rather than silently resolving to the default — a caller who meant "detach" gets a loud contract error.
  • The existing refusal of explicit name + branch attachment at create is kept verbatim (the Management API still creates before attaching with no idempotency key).

Behavior notes

  • Pre-existing unassigned databases converge: one in-place PATCH onto the default branch on the next reconcile — an update, never a replace. A database already attached to the default branch is left untouched.
  • Explicitly named creates (branch omitted) are still created project-scoped (the atomicity refusal above), then converge onto the default branch via PATCH in the same reconcile.
  • Breaking edge: explicit branchId: null / branchGitName: null — previously the only way to request detachment — now fails validation. Anyone who deliberately wanted an unassigned database loses that expressibility; an unassigned database is no longer representable as desired state, matching Prisma.App.

Tests

New test/Prisma/Database.test.ts (fake-client provider tests in the App.test.ts style) pins:

  • a generated-name create resolves the default branch and carries it in the create body (no follow-up PATCH);
  • an attached database with omitted props is not detached — updateDatabase dies if called (regression test for the old behavior);
  • a pre-existing unassigned database converges onto the default branch with exactly one PATCH, and diff plans an update, not a replace;
  • an explicit branchId stays authoritative (no default-branch lookup);
  • explicit null branch props are rejected at reconcile and diff;
  • a project with no default branch fails with the clear, App-shaped error;
  • an explicitly named create converges onto the default branch via PATCH in the same reconcile.

Existing suites updated to the new contract: the old "detaches an observed branch when branch props are omitted" regression test is inverted into "keeps the observed branch attachment", and the fake Management clients in Resources.test.ts / ManagementLifecycle.test.ts gained listBranches / branch-aware createDatabase/updateDatabase.

pnpm test test/Prisma — 376 passed, 0 failed. pnpm tsc -b and pnpm docs:check-jsdoc clean.

🤖 Generated with Claude Code

kristof-siket and others added 4 commits August 25, 2026 14:18
… default branch

Prisma.Database read omitted branchId/branchGitName as desired-UNASSIGNED:
branchNeedsSync converged toward branchId null and reconcile's patch path
actively PATCHed { branchId: null }, re-detaching an out-of-band-attached
database on every deploy — while sibling Prisma.App resolves the project's
default branch for the same omission and Prisma.Bucket lets the Management
API attach the default branch server-side.

Omitted branch props now resolve to the project's default branch, mirroring
App's desiredBranchId (defaultBranchIdOf + resolvedBranchAttachment):

- branchNeedsSync compares the observed branchId against the RESOLVED
  default branch id, so a pre-existing unassigned database converges with
  one in-place PATCH on its next reconcile (never a replace), and a
  database already attached to the default branch is left alone.
- The reconcile patch path never emits branchId: null; the create path
  passes the resolved default branch id for generated-name creates, so a
  fresh database is born attached.
- Explicit branchId: null / branchGitName: null are removed from the prop
  types and rejected at validate and diff — an unassigned database is no
  longer expressible as desired state.
- The explicit-name + branch-attachment create refusal stays: an
  explicitly named create (branch omitted) is created project-scoped and
  converges onto the default branch via PATCH in the same reconcile.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Change return type from discriminated union to string | undefined
- Use limit: 2 for all gitName branch lookups (Branches.ts, SourceRepository.ts)
- Resolve default branch once before if (!database) for generated-name creates; plain createBranchId local replaces resolvedBranch cast and reuse dance
- Remove the 3-line skip-listBranches comment; restore invariant comment at conflict recovery site
- Update all callers in App.ts and Database.ts to check === undefined
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant