Skip to content

Add Prisma Next Migrations view with visual contract diffs#1533

Merged
sorenbs merged 13 commits into
mainfrom
feature/migrations-view
Jul 13, 2026
Merged

Add Prisma Next Migrations view with visual contract diffs#1533
sorenbs merged 13 commits into
mainfrom
feature/migrations-view

Conversation

@sorenbs

@sorenbs sorenbs commented Jul 3, 2026

Copy link
Copy Markdown
Member

What

A new Migrations view for Studio that turns the Prisma Next migration ledger into a visual story of your schema's history.

When the connected database carries a prisma_contract.ledger with at least one applied migration, a Migrations item appears in the navigation (no prisma_contract schema, no ledger table, or an empty ledger → no menu entry; visibility comes from introspection plus a one-row EXISTS probe). The view shows:

  • A newest-first timeline of every applied migration — name, apply time, operation count, destructive-change marker, and compact +//~ chips summarizing what each migration did to models, fields, enums, and indexes.
  • A visual, FigJam-style diff canvas (React Flow + ELK, same stack as the Visualizer) built from the contract snapshots stored in the ledger: added models as green sticky-note cards, removed models red with strikethrough, changed models amber with per-field before → after detail pills, enum cards, and relation edges. Untouched neighbors render dimmed for context, and an All models toggle expands to the migration's full schema (including unchanged enums).
  • Collapsible detail panels behind a drag-resizable split: the executed SQL per operation (with operation class), and a Prisma-schema-style diff — a unified, color-coded line diff of the before/after schema with long unchanged runs collapsed into click-to-expand folds.
  • A morphing timeline: the canvas is one persistent React Flow instance with stable node ids, so switching migrations glides surviving cards to their new positions over ~500ms instead of rebuilding the scene.
  • The per-migration header floats over the canvas top edge (translucent, backdrop-blurred) so the diagram gets the full pane height, and the selected migration is URL-addressable.

Design decisions

  • The database is the single source of truth. Studio reads prisma_contract.ledger plus the content-addressed prisma_contract.contract store (one row per distinct contract, keyed by its storage hash) through the standard Adapter.raw surface (works on every executor) and never touches migration bundles on disk or the Prisma Next CLI.
  • Model status is table-anchored. Only field or index changes mark a model amber; gaining a back-relation whose foreign key lives in the other table keeps the model dimmed while the new relation edge is emphasized. Amber is synonymous with "this table's DDL changed".
  • Both edge endpoints are direct hash lookups. The ledger's origin_core_hash / destination_core_hash are contract identifiers, so the query LEFT JOINs the contract store twice — no client-side chain reconstruction. An unresolved origin (baseline, drift, snapshot-less predecessor) renders as an empty before-state rather than a wrong one.
  • Missing contract data degrades gracefully. A ledger written by an older prisma-next (contract store missing or empty) keeps the list and SQL panel and replaces the canvas with a notice to update Prisma Next.
  • jsdiff over @pierre/diffs for the schema diff: the latter hard-depends on shiki, which is too heavy for the published bundle. The renderer is isolated so it can be swapped.
  • The FigJam-style canvas cards are documented as an approved non-standard-UI exception; full architecture in Architecture/migrations-view.md.

Try it — live preview & demo video

Live preview (deployed from this PR): https://cmr485hrg05jfzvdiywd68wat.cdg.prisma.build — open Migrations in the left nav. The demo seeds the full 20-migration history.

Demo video (30s): migrations-view-demo-v2.mp4 — GitHub renders an inline player on that page. (Linked from a pinned PR commit; the video file is not part of the merged tree.)

Demo / PR preview

pnpm demo:ppg seeds a real 20-migration history (models, fields, enums, uniques, indexes, a destructive column drop, and a retired model), captured from the actual Prisma Next emit → migration plan → migrate flow (companion PR: prisma/prisma-next#908). The seeder replays it in one transaction and upgrades older demo volumes to the hash-keyed contract store in place. The fixture is statically imported so the Compute preview deploy inlines it — the PR preview seeds the full history; open Migrations in the nav to try the view.

Companion PR

Requires nothing at runtime, but the data model comes from prisma/prisma-next#908, which persists each applied migration's destination contract into the hash-keyed prisma_contract.contract store.

Testing

  • 50+ new unit tests across the diff engine, PSL projection/diff, graph scoping, ledger parsing/probing, view behavior (incl. fold expansion and the degraded-mode notice), and navigation gating; full suite green (907 tests).
  • Typecheck, lint (0 errors), pnpm build + check:exports pass with the new diff dependency.
  • Verified end-to-end in the running demo (light + dark, rapid migration switching, drag/keyboard panel resize, fold expansion, degraded/empty-ledger states via the SQL console) and fresh-boot seeds against the hash-keyed store, plus a build:deploy artifact check confirming the fixture is inlined.

🤖 Generated with Claude Code

sorenbs and others added 5 commits July 2, 2026 16:09
Studio now detects a Prisma Next migration ledger
(prisma_contract.ledger) via introspection and, only then, shows a
Migrations item in the navigation. The view lists every applied
migration newest-first (name, apply time, op count, destructive marker,
and compact diff-stat chips) and renders the selected migration as a
visual, UML-style diff on a React Flow canvas with ELK layout, built
from the contract snapshots stored in the ledger: green cards for added
models, red strikethrough for removed ones, amber cards with per-field
before → after pills for changed models, enum cards, and relation
edges, with untouched neighbors dimmed for context. A collapsible SQL
panel shows each operation class and the executed statements, and the
selection is URL-addressable.

The diff engine (contract-diff.ts) is pure and tested against the
Prisma Next contract JSON shape; ledger parsing repairs missing
before-snapshots from each predecessor edge. The demo seed replays a
20-migration history captured from the prisma-next migrations-showcase
example so pnpm demo:ppg exercises the whole feature.

The FigJam-style canvas cards are documented as an approved
non-standard UI exception in Architecture/non-standard-ui.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ema diff, morphing canvas

Follow-ups from design review of the Migrations view:

- A model now turns amber only when its own table changed (fields or
  indexes). Back-relations whose foreign key lives in the other table no
  longer mark the untouched model as updated — the new relation shows
  through the emphasized edge instead, and the list chips follow the
  same rule.
- An "All models" toggle (persisted UI state) expands the canvas from
  touched-plus-neighbors to the migration's full schema.
- A Schema detail panel joins SQL: contract snapshots are projected
  into Prisma-schema-style text and diffed line-by-line (jsdiff) with
  color-coded added/removed rows and collapsed unchanged runs. jsdiff
  over @pierre/diffs to keep shiki out of the published bundle; the
  renderer is isolated so it can be swapped.
- Switching migrations morphs the canvas instead of rebuilding it: the
  React Flow instance persists, stable node ids let surviving cards
  glide to their new ELK positions over ~500ms, entering nodes fade in,
  and the camera refits smoothly. This also removes the AnimatePresence
  remount path entirely.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The "All models" toggle expanded models but the canvas still filtered
enums to touched ones, so an enum added in an earlier migration (e.g.
Priority from #5) silently disappeared from later migrations even with
the toggle on. The graph builder now widens both models and enums when
showing everything, rendering unchanged enums in the dimmed context
style that already existed for them.

buildDiffGraph moves from MigrationsView.tsx into diff-layout.ts so the
scoping rules (touched-plus-neighbors default, all-models expansion,
touched-enum inclusion, relation-only touched scoping, no-op fallback)
are pinned by direct unit tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… resizable

Two layout refinements to the Migrations view:

- The per-migration header (title, hash edge, stat chips, All models /
  SQL / Schema controls) is now a slim translucent bar floating over the
  top edge of the canvas, so the diagram owns the full height of the
  content pane instead of losing a header strip.
- The SQL/Schema detail panel gained a drag handle on its top edge: the
  split is resizable by pointer (with row-resize cursor and text-select
  suppression during the drag) and by ArrowUp/ArrowDown on the focused
  handle, clamped to 120-640px and persisted in UI state. Both panels
  now share one scrollable container sized by that height.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The migrations seed read its fixture with readFileSync relative to
import.meta.url, which breaks in bundled artifacts (the Compute preview
deploy and demo:ppg:build bundle server.ts into a single file with no
sibling fixtures/ directory). A static JSON import lets every bundling
path inline the fixture, so PR preview deploys seed the full
20-migration history. Verified by building the deploy artifact and
booting a fresh demo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Changes

This PR adds a Prisma Next Migrations view to Studio, with ledger detection and parsing hooks, contract snapshot diffing, Prisma-schema rendering, React Flow/ELK graph layout, navigation and URL state wiring, demo seeding, documentation, and tests. It also updates preview deploy URL resolution to use a fallback lookup path and filters null GitHub output values.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: a new Prisma Next Migrations view with visual contract diffs.
Description check ✅ Passed The description is directly aligned with the changeset and accurately describes the new Migrations view and related behavior.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/migrations-view
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feature/migrations-view

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Compute preview deployed.

Branch: feature/migrations-view
Service: feature-migrations-view
Preview: https://cmr485hrg05jfzvdiywd68wat.cdg.prisma.build

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 14

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@demo/ppg-dev/seed-migrations.ts`:
- Around line 105-152: The fixture replay in the seeding flow is leaving partial
`prisma_contract.ledger` and side-effect SQL committed before completion, so
wrap the entire replay path in a single transaction. Update the seeding logic
around the `count` guard and the `for` loops in `seed-migrations.ts` to run
inside `sql.begin(...)` (or equivalent transactional wrapper), including the
nested `operation.execute` steps, the `ledger` inserts, the `marker` inserts,
and `seedShowcaseRows`. This keeps the `seed` function atomic and prevents the
`count > 0` idempotency check from permanently skipping a half-failed run.

In `@package.json`:
- Line 200: The runtime import of diffLines from
ui/studio/views/migrations/psl-schema.ts means diff must be available in
production, but it is currently only listed as a dev dependency. Move diff from
devDependencies to dependencies in package.json so the migration code can
resolve it in production and packaged workspace installs.

In `@ui/hooks/use-migrations.ts`:
- Around line 6-14: The ledger fetch in LEDGER_QUERY is loading the full
history, including large contract_json_before/contract_json_after payloads, on
every Migrations view refresh. Update use-migrations.ts so the initial query
returns only paginated ledger metadata or a limited page of rows, and defer
snapshot JSON loading until a specific migration is selected. Adjust the related
query path in the 211-230 section to support this lazy/split loading pattern
using the existing LEDGER_QUERY flow.
- Around line 47-57: The parseJsonish helper is swallowing JSON.parse failures
and returning null without any diagnostics, so corrupted migration payloads are
hidden. Update parseJsonish in use-migrations to report the parse error when
JSON parsing fails, and make sure the callers that process contract_json_before,
contract_json_after, and operations can surface or log that failure instead of
quietly treating it as empty data.
- Around line 93-101: The fallback in use-migrations.ts currently treats any
missing or invalid operationClass as "additive", which can hide destructive
migrations. Update the normalization logic in useMigrations so
operation.operationClass is only accepted when it matches a known valid value;
otherwise surface it as unknown/invalid instead of defaulting to "additive".
Then adjust the downstream destructive badge and isDestructive handling in the
same hook so unrecognized classes are treated conservatively and do not get
classified as non-destructive.
- Around line 76-105: The parsed migration operations in use-migrations.ts
currently default missing operation.id values to an empty string, which can
produce duplicate React keys in MigrationSqlPanel. Update the normalization
logic that builds the StudioMigrationOperation array so every operation gets a
stable unique id when the source id is missing or invalid, rather than "". Make
sure the fix is applied in the parser that feeds migration.operations so
MigrationsView’s map keying stays unique and deterministic.

In `@ui/index.css`:
- Around line 286-288: The `.react-flow__node.dragging` selector in the
migrations diff canvas stylesheet appears unreachable because `nodesDraggable`
is disabled, so either remove the dead rule or add a brief comment near the
relevant React Flow config/style block explaining that it is intentionally kept
for defensive or future draggable behavior. Use the `.react-flow__node.dragging`
rule and the `nodesDraggable={false}` setting to locate the affected styling.

In `@ui/studio/views/migrations/contract-diff.ts`:
- Around line 48-59: `diffFields` is missing primary-key detection, so PK-only
edits can still be treated as unchanged; update the field comparison logic in
`diffFields` to compare `isPrimaryKey` and add a corresponding
`FieldChangeDetail` when it differs so the `FieldDiff.status` becomes changed.
Also align `FieldChangeDetail.aspect` in `FieldChangeDetail`/`FieldDiff` with
the actual emitted details by either adding a real enum-diff branch or removing
the unused `"enum"` union member.

In `@ui/studio/views/migrations/diff-layout.ts`:
- Around line 239-276: The catch block in the ELK layout routine is swallowing
layout failures, so add error logging in the same try/catch around elk.layout
before falling back to the grid. Use a clear message that includes the caught
error details and keep the existing fallback behavior in diff-layout.ts so
issues in the layout path can be diagnosed without changing the returned
positions.
- Around line 174-226: The edge-deduping in the relation rendering logic is too
coarse because `pairKey` only keys by model pair, which collapses distinct
relations between the same two models. Update the edge identity in
`diff-layout.ts` so the `seenEdges` check and `id` use a relation-level key from
`relation.name`, `relation.toModel`, and `relation.cardinality`, matching the
distinctness used by `contract-diff.ts`. Keep the `added`/`removed` styling
logic in the same loop, but ensure each unique relation produces its own edge
instead of being skipped by the model-pair key.

In `@ui/studio/views/migrations/MigrationsView.tsx`:
- Around line 560-567: In MigrationsView, replace the array index keys used in
operation.statements.map and lines.map with stable identifiers. Update the <pre>
and related rendered items to derive a key from a persistent value such as the
statement/line content plus an operation-specific identifier (for example
operation.id or line type) so future reordering or insertions do not reuse the
wrong React element state.
- Around line 374-456: `MigrationDiffCanvas` is re-rendering on every
`MigrationsView` resize tick even when `migration` and `showAllModels` haven’t
changed. Wrap `MigrationDiffCanvas` with `React.memo` so the ReactFlow subtree
is skipped during `isPanelResizing`/`draftPanelHeight` updates, and keep its
existing `migration` and `showAllModels` props as the memo boundary.
- Around line 880-917: The SQL and Schema controls in MigrationsView are toggle
buttons but only expose state via data-active, so add aria-pressed to both
Button components and bind it to the same detailsPanel checks used for
variant/data-active. Update the SQL toggle and the Schema toggle together so
assistive tech can announce their pressed/unpressed state consistently.

In `@ui/studio/views/migrations/psl-schema.ts`:
- Around line 36-54: The pslDefault helper is emitting invalid PSL for
parameterized SQL defaults because only zero-arg calls are routed through
dbgenerated; update pslDefault to detect any function-like default expression
that is not one of the explicit special cases (such as now() and
gen_random_uuid()) and wrap it with `@default`(dbgenerated(...)) instead of
returning `@default`(...) directly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e334b34e-846f-47e7-abba-09906179f7bd

📥 Commits

Reviewing files that changed from the base of the PR and between 3631a93 and 20963ed.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (25)
  • .changeset/migrations-view.md
  • Architecture/migrations-view.md
  • Architecture/non-standard-ui.md
  • FEATURES.md
  • demo/ppg-dev/fixtures/prisma-contract-migrations.json
  • demo/ppg-dev/seed-database.ts
  • demo/ppg-dev/seed-migrations.ts
  • package.json
  • ui/hooks/nuqs.ts
  • ui/hooks/react-query.ts
  • ui/hooks/use-migrations.test.ts
  • ui/hooks/use-migrations.ts
  • ui/hooks/use-navigation.tsx
  • ui/index.css
  • ui/studio/Navigation.test.tsx
  • ui/studio/Navigation.tsx
  • ui/studio/Studio.tsx
  • ui/studio/views/migrations/MigrationsView.test.tsx
  • ui/studio/views/migrations/MigrationsView.tsx
  • ui/studio/views/migrations/contract-diff.test.ts
  • ui/studio/views/migrations/contract-diff.ts
  • ui/studio/views/migrations/diff-layout.test.ts
  • ui/studio/views/migrations/diff-layout.ts
  • ui/studio/views/migrations/psl-schema.test.ts
  • ui/studio/views/migrations/psl-schema.ts

Comment thread demo/ppg-dev/seed-migrations.ts
Comment thread package.json
Comment thread ui/hooks/use-migrations.ts
Comment thread ui/hooks/use-migrations.ts
Comment thread ui/hooks/use-migrations.ts
Comment thread ui/studio/views/migrations/diff-layout.ts
Comment thread ui/studio/views/migrations/MigrationsView.tsx Outdated
Comment thread ui/studio/views/migrations/MigrationsView.tsx
Comment thread ui/studio/views/migrations/MigrationsView.tsx
Comment thread ui/studio/views/migrations/psl-schema.ts
sorenbs and others added 2 commits July 2, 2026 18:13
- migrations-view-demo-v2.mp4: the 30s feature demo linked from the PR
  description (renders in GitHub via the blob page player).
- compute-preview-deploy.mjs: newer Compute CLI payloads expose the
  endpoint as appEndpointDomain rather than serviceEndpointDomain, so
  the deploy step emitted an undefined URL and the PR comment step
  skipped. Fall back through the known payload shapes (deploy result,
  then services show) so the preview URL always lands on the PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The deploy CLI payload does not currently include a version endpoint,
so the literal string "undefined" flowed through GITHUB_OUTPUT into the
PR comment ("Version: undefined"). Skip null/undefined values when
writing outputs; the comment script already treats them as optional.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/compute-preview/compute-preview-deploy.mjs`:
- Around line 44-59: The serviceUrl fallback chain in compute-preview-deploy.mjs
can still resolve to undefined, which then propagates into the outputs and the
preview comment flow. Add explicit validation right after deriving serviceUrl
(using deployResult.serviceEndpointDomain, deployResult.appEndpointDomain, and
showService(service.id)) and fail fast with a clear error if none of those
values are present. Keep the check close to the existing serviceUrl/result
construction so writeOutputs and compute-preview-comment.mjs never receive an
unset preview URL.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7e371a8e-26ba-4b89-9d22-26c0ea95939a

📥 Commits

Reviewing files that changed from the base of the PR and between 20963ed and 37ef2fa.

⛔ Files ignored due to path filters (1)
  • migrations-view-demo-v2.mp4 is excluded by !**/*.mp4
📒 Files selected for processing (1)
  • scripts/compute-preview/compute-preview-deploy.mjs

Comment thread scripts/compute-preview/compute-preview-deploy.mjs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
scripts/compute-preview/compute-preview-deploy.mjs (1)

44-59: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fallback chain can still resolve to undefined without explicit failure.

The serviceUrl fallback chain remains unchanged from the previous review — if deployResult.serviceEndpointDomain, deployResult.appEndpointDomain, and showService(service.id).appEndpointDomain are all nullish, serviceUrl silently becomes undefined. The new writeOutputs filter (Line 156) now omits the key entirely rather than writing undefined, which will eventually surface as a getRequiredEnv failure in compute-preview-comment.mjs, but that's an indirect, less actionable failure mode compared to throwing here with a clear message at the actual point of failure.

🛡️ Proposed fix
   const serviceUrl =
     deployResult.serviceEndpointDomain ??
     deployResult.appEndpointDomain ??
     (await showService(service.id)).appEndpointDomain;
+
+  if (!serviceUrl) {
+    throw new Error(
+      "Unable to determine the preview service URL from Compute CLI output.",
+    );
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/compute-preview/compute-preview-deploy.mjs` around lines 44 - 59, The
service URL resolution in compute-preview-deploy.mjs can still end up undefined,
so add an explicit failure after the
deployResult.serviceEndpointDomain/appEndpointDomain/showService(service.id).appEndpointDomain
fallback chain in the deploy flow. Update the logic around serviceUrl and the
result object so it throws a clear error at the point of resolution if no
endpoint domain is available, using the existing deployResult, showService, and
serviceUrl symbols to keep the failure actionable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@scripts/compute-preview/compute-preview-deploy.mjs`:
- Around line 44-59: The service URL resolution in compute-preview-deploy.mjs
can still end up undefined, so add an explicit failure after the
deployResult.serviceEndpointDomain/appEndpointDomain/showService(service.id).appEndpointDomain
fallback chain in the deploy flow. Update the logic around serviceUrl and the
result object so it throws a clear error at the point of resolution if no
endpoint domain is available, using the existing deployResult, showService, and
serviceUrl symbols to keep the failure actionable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d5138d10-11bc-43ef-9f96-d2cc65d94e57

📥 Commits

Reviewing files that changed from the base of the PR and between 37ef2fa and 8b38640.

📒 Files selected for processing (1)
  • scripts/compute-preview/compute-preview-deploy.mjs

sorenbs added a commit to prisma/prisma-next that referenced this pull request Jul 6, 2026
…ation

isDefaultValue() treats false and [] as omittable shape-defaults, but a
column default literal payload is data: dropping it turned
`Boolean @default(false)` schemas into contracts that failed their own
structural validation on the next read (surfaced as PN-CLI-4003 when
planning a migration). Preserve `value` whenever its parent key is
`default`.

Only previously-broken contracts change hashes: any contract carrying a
literal false/[] default was unloadable before this fix, so no working
contract identity moves.

Found while generating the 20-migration demo history that seeds the
Prisma Studio Migrations view (prisma/studio#1533).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Søren Bramer Schmidt <sorenbs@gmail.com>
sorenbs and others added 5 commits July 7, 2026 15:17
prisma/prisma-next#908 moved per-migration contract snapshots off the
ledger's contract_json_before/after columns into a 1:1 contract table
(ledger_id PK/FK, after-state only). The ledger query now LEFT JOINs
that table, and each migration's before-state is derived from its
predecessor's snapshot per contract space, guarded by hash continuity
(origin must equal the predecessor's destination) so a broken chain
renders an unknown baseline instead of a wrong one. Databases without
the contract table fall back to a join-less query via introspection
detection.

The demo seeder creates the new table, inserts ledger rows RETURNING id
with one contract row per snapshot-carrying migration, and upgrades a
legacy-seeded volume in place (backfill + drop of the old columns).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each '⋯ N unchanged lines' separator in the Migrations schema panel is
now a button carrying a discrete '· expand' hint; clicking it reveals
the folded lines in place. diffSchemas carries the folded run's lines
on the collapsed entry so expansion needs no re-diff, and the panel is
keyed by migration id so folds reset when switching migrations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ct data

The Migrations navigation item now requires actual history, not just
table presence: useHasMigrationHistory combines the introspection check
with a one-row EXISTS probe, so a missing prisma_contract schema, a
missing ledger table, or an empty ledger all hide the item (probing
beats fetching the full ledger, whose snapshots can be megabytes of
jsonb).

When the ledger has rows but none joins to a contract snapshot (the
contract table is missing or empty — a database written by an older
prisma-next), the view keeps the migration list and SQL panel, hides
the diff-dependent All models toggle and Schema button, and replaces
the canvas with a notice asking to update Prisma Next.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
prisma/prisma-next#908 re-keyed prisma_contract.contract by storage
hash (review feedback: a contract's identity is its hash, which the
ledger already carries in origin_core_hash / destination_core_hash).
The ledger query now LEFT JOINs the store twice — once per edge
endpoint — so both before- and after-states are direct lookups and the
client-side predecessor-chain derivation with its hash guard is
deleted. An unresolved origin hash joins to nothing and renders as an
unknown baseline, same as before.

The demo seeder creates the hash-keyed store, upserts each after-state
under its destination hash (ON CONFLICT DO NOTHING), and upgrades both
legacy volume shapes in place (ledger_id-keyed store re-keyed by hash;
two-column ledger backfilled and dropped).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- contract-diff: primary-key changes now surface as a 'key' detail and
  mark the model changed; the never-emitted 'enum' aspect member is
  gone.
- diff-layout: relation edges are keyed per relation (per model pair,
  the first-encountered side draws all of its relations), so two
  distinct relations to the same model render as two edges instead of
  collapsing into one; removed relations get their own ownership so a
  drop still renders when only the far side remembers it. ELK layout
  failures now log a warning before the grid fallback.
- psl-schema: any parameterized function default (nextval('seq') etc.)
  renders as @default(dbgenerated("…")) instead of invalid bare PSL.
- MigrationsView: canvas memoized so panel-resize pointermove updates
  don't re-render ReactFlow; SQL/Schema toggles carry aria-pressed; SQL
  panel keys are index-qualified against duplicate operation ids.
- seed-migrations: the fixture replay runs in one transaction so a
  partial failure can't strand the count-based idempotency guard.
- compute-preview-deploy: fails loudly when every endpoint fallback is
  exhausted instead of reporting an undefined preview URL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- FEATURES.md and the changeset catch up with the final feature set:
  menu gating on a non-empty ledger, the content-addressed snapshot
  store, click-to-expand diff folds, and the older-prisma-next notice.
- The changeset is written to double as the CHANGELOG entry and GitHub
  release notes.
- The 14 MB demo video leaves the tree before merge; the PR description
  now links it from a pinned PR commit instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sorenbs sorenbs merged commit ed30e6e into main Jul 13, 2026
3 checks passed
@sorenbs sorenbs deleted the feature/migrations-view branch July 13, 2026 13:40
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