Skip to content

TML-2690: make rollback edges plannable and applyable via --to#635

Merged
wmadden merged 23 commits into
mainfrom
tml-2690-migrate-to-dir-advertised-in-help-but-fails-with
May 30, 2026
Merged

TML-2690: make rollback edges plannable and applyable via --to#635
wmadden merged 23 commits into
mainfrom
tml-2690-migrate-to-dir-advertised-in-help-but-fails-with

Conversation

@wmadden
Copy link
Copy Markdown
Contributor

@wmadden wmadden commented May 30, 2026

Linked issue

Refs TML-2690.

At a glance

Rolling back a migration is now a two-command flow with no contract-source edit — plan the reverse edge, then apply it:

$ prisma-next migrate --to 20260522T1240_add_comment_model^ --db "$DATABASE_URL"
# refuses: no migration edge from the current state to the target.
# fix:
#   1. prisma-next migration plan --from 20260522T1240_add_comment_model \
#        --to 20260522T1240_add_comment_model^ --name drop_comment_model
#   2. prisma-next migrate --to 20260522T1240_add_comment_model^ --db "$DATABASE_URL"

$ prisma-next migration plan --from 20260522T1240_add_comment_model \
    --to 20260522T1240_add_comment_model^ --name drop_comment_model
# plans the reverse edge: one DROP TABLE op, flagged (destructive)

$ prisma-next migrate --to 20260522T1240_add_comment_model^ --db "$DATABASE_URL"
# applies; the marker moves back to the baseline state

Before this PR, migrate --to <dir>^ was advertised in --help but dead-ended: it refused with a pathUnreachable error whose advice pointed at a migration plan command that couldn't target an arbitrary state — and even once you hand-edited your contract to plan the edge, migrate --to refused a second time with DESTINATION_CONTRACT_MISMATCH.

Decision

This PR makes a rollback (or any arbitrary-target migration) plannable and applyable as a normal two-command flow, with no contract-source surgery. Three coordinated changes:

  1. migration plan --to <ref>migration plan accepts an optional --to (and the already-existing explicit --from) using the same reference grammar --from accepts (hash / prefix / ref name / migration dir / <dir>^ / ./path). When supplied, the resolved contract becomes the planner destination instead of the emitted contract.json; omitted, behaviour is byte-identical to today.

  2. migrate --to <node> verifies against the target contractmigrate previously always verified the applied state against the emitted contract.json, so any target that wasn't the emitted contract (every rollback) dead-ended at DESTINATION_CONTRACT_MISMATCH even after the edge was planned. It now verifies/applies against the target bundle's end-contract.json, mirroring db update --to. It still refuses to invent a missing edge — it only redirects which contract it verifies against; it never auto-plans.

  3. Coherent pathUnreachable diagnostic — the migrate --to refusal now reads as one plan-then-apply sequence: why states the condition (no edge between the two named states), and fix gives the exact migration plan --from … --to … --name … command followed by the migrate --to … re-run, with a note that a rollback plan is expected to contain destructive (DROP) operations to review before applying. The sibling neverPlanned diagnostic's why was tightened the same way so the recovery instruction lives in one place.

migrate keeps refusing to invent a path (the correct invariant, see ADR 001 — Migrations as Edges); the reverse edge just becomes a real, committable migration package on disk.

Reviewer notes

  • Largest diff is plan-resolution.ts (+275/−…). This extracts the ref→contract resolution core that --from already used (landed with TML-2629) into a shared resolver reused by both --from and --to. The greenfield / auto-baseline branches stay --from-only. Spot-check that the --to path and the --from path now share one resolver and that the auto-baseline branch is untouched.
  • The migrate --to change (Package layering #2) was a mid-flight scope expansion, operator-confirmed. The slice originally assumed migrate --to already applied a planned arbitrary-target edge; implementing the end-to-end reproduction proved it dead-ended at DESTINATION_CONTRACT_MISMATCH. The fix mirrors the existing db update --to precedent (and reuses the end-contract.json read migrate.ts already performed for the ref-advancement snapshot, so there's a single non-divergent read). The read is placed after the marker / invariant pre-checks so synthetic fixtures without an end-contract.json don't error before those pre-checks fire.
  • A planned rollback contains destructive (DROP) operations. migration plan's policy allows the destructive op class (unlike db init's additive-only policy), so a clean rollback plans successfully with a "may cause data loss" warning rather than refusing. Narrower cases (rename inference, NOT-NULL re-add without a safe default) can still fail fast for a hint; the diagnostic acknowledges this rather than promising a frictionless path in every case.
  • A project spec is committed under projects/migrate-to-rollback-plannable/spec.md. It's the tracked home for this standalone slice's design rationale; it carries no implementation and will be cleaned up at project close-out.
  • Pre-existing flake, not introduced here: a cluster of ~9 CLI tests spawnSync the built dist/cli.mjs and hit a 500ms cap against a ~680ms cold start (version.test.ts, removed-verb-redirects.test.ts, no-parallel-ci-detection.test.ts); control-api/contract-emit.test.ts has a separate occasional emit-race timeout. Both pre-date this work and are unrelated to the diff.

Behavior changes & evidence

Testing performed

  • pnpm typecheck@prisma-next/cli, @prisma-next/integration-tests: pass.
  • Full @prisma-next/cli package suite — 1036 passing (76 files), incl. the new migrate-to-contract and cli-errors composition tests.
  • @prisma-next/integration-tests plan-to-rollback.e2e.test.ts — green (real PGlite journey harness).
  • pnpm fixtures:check — green, zero contract drift (after a clean pnpm build).
  • pnpm lint:deps — 0 violations; biome clean on touched files.

Skill update

n/a — internal CLI behaviour + diagnostics. The change is additive (migration plan --to) plus a bug fix (migrate --to now applies arbitrary-target edges) with no breaking change to downstream consumers; no upgrade translation is required. User-facing surface is documented in 7. Migration System.md and the @prisma-next/cli README.

Follow-ups

  • The pre-existing CLI spawnSync timeout cluster (and the contract-emit emit-race timeout) are candidates for a separate fix — bump the spawn-test timeout or move those tests to the e2e suite. Out of scope here.

Alternatives considered

  • Auto-plan the reverse edge inside migrate --to. Rejected: migrate replays existing edges and must never invent one — auto-planning a destructive DROP inside an apply command is a footgun, and it would blur the plan/apply boundary. The plan step stays explicit and committable; migrate --to only redirects which contract it verifies against.
  • Ship the diagnostic fix and the flag, but defer the migrate --to verification change to a follow-up. Rejected: that would knowingly ship a diagnostic advertising a migrate --to <target> command that still dead-ends — a worse version of the "advertised but fails" trap this work exists to close.
  • Redefine the reproduction to re-emit the target contract (a source edit). Rejected: it contradicts the no-contract-surgery promise that motivates the ticket.

Checklist

  • All commits are signed off (git commit -s) per the DCO.
  • I read CONTRIBUTING.md and the change is scoped to one logical concern.
  • Tests are updated.
  • The PR title is in TML-NNNN: <sentence-case title> form.
  • The Skill update section above is filled in.

Summary by CodeRabbit

  • New Features

    • Full --to support for planning and migrating to arbitrary destination contracts (including rollback via ^).
  • Improvements

    • CLI now resolves --from/--to in-memory and uses snapshot/artifact selection so destination selection and emitted artifacts align with intent.
    • Unreachable-path guidance converted into a clear plan-then-apply workflow with explicit commands and rollback hints.
  • Documentation

    • Migration system and CLI docs updated with new flags, semantics, examples.
  • Tests

    • Expanded unit, integration, and e2e coverage for --to, rollback planning, corrupted targets, and CLI guidance.

Review Change Stack

wmadden and others added 9 commits May 29, 2026 16:56
Signed-off-by: Will Madden <madden@prisma.io>
Signed-off-by: Will Madden <madden@prisma.io>
…plans

`migration plan` now accepts an optional `--to <contract>` that targets an
arbitrary resolved contract (e.g. an ancestor / rollback target) instead of
always the emitted `contract.json`. It accepts the same reference grammar as
`--from` (full hash, prefix, ref name, migration dir name, `<dir>^`, `./path`)
resolved via `parseContractRef`. When supplied, the resolved contract becomes
the planner destination and the source of the emitted `end-contract.*`, and the
no-op short-circuit runs against the resolved hash; when omitted, behaviour is
byte-identical to today.

A rollback that drops an added model plans successfully with a destructive
warning rather than refusing, since `migration plan` allows the destructive op
class.

The reference->contract resolution core of `resolveFromForPlan` is extracted
into a shared `resolveContractRef` helper reused by both `--from` and the new
`resolveToForPlan`; the greenfield / auto-baseline branches stay `--from`-only.

Refs: TML-2690
Signed-off-by: Will Madden <madden@prisma.io>
…en-apply story

The `migrate --to` path-unreachable diagnostic previously had `why` and `fix`
each independently telling the user to run `migration plan`, which read
redundantly. Rewrite the two halves so they compose into one sequence:

- `buildPathNotFoundFailure.why` now states only the condition — no migration
  edge connects the current state to the target, and migrate replays existing
  edges rather than inventing one — without prescribing a command.
- `errorPathUnreachable.fix` owns the recovery: plan the missing edge with
  `migration plan --from <current> --to <target> --name <slug>` (a real command
  as of the new `--to` support), then re-run `migrate --to <target>`. It adds a
  note that a rollback plan is expected to contain destructive (DROP) ops to
  review, and acknowledges that narrower cases (rename inference, NOT NULL
  re-add without a safe default, type change needing data) may need a hint.

buildPathNotFoundFailure is exported (@internal) so a test can drive the real
producer and assert the why+fix compose non-redundantly.

Refs: TML-2690
Signed-off-by: Will Madden <madden@prisma.io>
…item (TML-2690)

Signed-off-by: Will Madden <madden@prisma.io>
…rification (TML-2690)

Signed-off-by: Will Madden <madden@prisma.io>
`migrate --to <node>` previously always verified the applied path against the
emitted `contract.json`, so a rollback / arbitrary-target migrate dead-ended
with DESTINATION_CONTRACT_MISMATCH (plan destination != emitted contract) unless
the target contract had been re-emitted first.

When `--to` resolves to an on-disk graph node, apply against THAT bundle`s
`end-contract.json` instead of the emitted contract. The read is lifted to just
before the apply (after the marker / invariant pre-checks) and reused for the
optional ref-advancement snapshot, so the apply contract and the snapshot
contract are sourced identically — no duplicated, divergent reads. A missing
`end-contract.json` surfaces a structured file-not-found error rather than
throwing raw. With `--to` omitted (or a target with no matching bundle),
behaviour is byte-identical: the emitted contract stays the apply contract.
migrate still refuses to invent a missing edge; this only redirects which
contract is verified against.

This closes the rollback round-trip end-to-end: from a two-migration applied
state, `migration plan --to <dir>^` then `migrate --to <dir>^` succeeds and
moves the marker back, with no contract-source edit.

Refs: TML-2690
Signed-off-by: Will Madden <madden@prisma.io>
…L-2690)

Make buildNeverPlannedFailure.why condition-only so it composes with
errorPathUnreachable fix without redundant migration plan instructions.
Document migration plan --to and the rollback plan-then-apply workflow in
the migration subsystem doc and CLI README.

Signed-off-by: wmadden-electric <286902546+wmadden-electric@users.noreply.github.com>
@wmadden wmadden requested a review from a team as a code owner May 30, 2026 06:10
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

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

Walkthrough

Adds --to support to migration plan and migrate, centralizes contract-ref resolution (snapshot vs graph-node), materializes destination end-contract artifacts for planning, selects apply contracts from resolved targets, exports two failure builders, updates unreachable-path CLI guidance, and extends unit and integration tests (including an E2E rollback).

Changes

Migration System --to Support

Layer / File(s) Summary
All changes checkpoint
docs/..., packages/1-framework/3-tooling/cli/..., packages/1-framework/3-tooling/migration/..., packages/3-mongo-target/..., test/...
Monolithic checkpoint covering: docs updates for --to and PATH_UNREACHABLE guidance; unified plan-resolution (resolveContractRef, resolveFromPolicy, resolveToForPlan); ContractSpaceMember contractAt support, types, deserialization, and loader wiring; CLI migration plan and migrate --to wiring and artifact materialization; mapContractAtError mapping; exported failure builders and CLI error envelope updates; comprehensive test updates and additions including unit, integration, and an end-to-end rollback journey.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • prisma/prisma-next#626: The main PR’s changes extend the same ContractSpaceAggregate/ContractSpaceMember abstraction by adding contractAt/node contract materialization and integrating it into CLI planning/apply.
  • prisma/prisma-next#523: Introduced the unified contract-reference grammar and resolution plumbing (parseContractRef) that this PR builds upon.
  • prisma/prisma-next#582: Prior CLI error/envelope and unreachable-path diagnostic work that overlaps with the cli-errors changes here.

Suggested reviewers

  • aqrln

"🐰 I hopped through contracts, hashes bright,
I nudged a --to flag into the night.
Plan then apply — a rollback song,
Contracts intact, the steps go on.
🥕💫"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'TML-2690: make rollback edges plannable and applyable via --to' directly and accurately summarizes the main feature: enabling rollback planning and applying via the new --to flag.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tml-2690-migrate-to-dir-advertised-in-help-but-fails-with

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

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 30, 2026

Open in StackBlitz

@prisma-next/extension-author-tools

npm i https://pkg.pr.new/@prisma-next/extension-author-tools@635

@prisma-next/mongo-runtime

npm i https://pkg.pr.new/@prisma-next/mongo-runtime@635

@prisma-next/family-mongo

npm i https://pkg.pr.new/@prisma-next/family-mongo@635

@prisma-next/sql-runtime

npm i https://pkg.pr.new/@prisma-next/sql-runtime@635

@prisma-next/family-sql

npm i https://pkg.pr.new/@prisma-next/family-sql@635

@prisma-next/extension-arktype-json

npm i https://pkg.pr.new/@prisma-next/extension-arktype-json@635

@prisma-next/extension-cipherstash

npm i https://pkg.pr.new/@prisma-next/extension-cipherstash@635

@prisma-next/middleware-cache

npm i https://pkg.pr.new/@prisma-next/middleware-cache@635

@prisma-next/mongo

npm i https://pkg.pr.new/@prisma-next/mongo@635

@prisma-next/extension-paradedb

npm i https://pkg.pr.new/@prisma-next/extension-paradedb@635

@prisma-next/extension-pgvector

npm i https://pkg.pr.new/@prisma-next/extension-pgvector@635

@prisma-next/extension-postgis

npm i https://pkg.pr.new/@prisma-next/extension-postgis@635

@prisma-next/postgres

npm i https://pkg.pr.new/@prisma-next/postgres@635

@prisma-next/sql-orm-client

npm i https://pkg.pr.new/@prisma-next/sql-orm-client@635

@prisma-next/sqlite

npm i https://pkg.pr.new/@prisma-next/sqlite@635

@prisma-next/target-mongo

npm i https://pkg.pr.new/@prisma-next/target-mongo@635

@prisma-next/adapter-mongo

npm i https://pkg.pr.new/@prisma-next/adapter-mongo@635

@prisma-next/driver-mongo

npm i https://pkg.pr.new/@prisma-next/driver-mongo@635

@prisma-next/contract

npm i https://pkg.pr.new/@prisma-next/contract@635

@prisma-next/utils

npm i https://pkg.pr.new/@prisma-next/utils@635

@prisma-next/config

npm i https://pkg.pr.new/@prisma-next/config@635

@prisma-next/errors

npm i https://pkg.pr.new/@prisma-next/errors@635

@prisma-next/framework-components

npm i https://pkg.pr.new/@prisma-next/framework-components@635

@prisma-next/operations

npm i https://pkg.pr.new/@prisma-next/operations@635

@prisma-next/ts-render

npm i https://pkg.pr.new/@prisma-next/ts-render@635

@prisma-next/contract-authoring

npm i https://pkg.pr.new/@prisma-next/contract-authoring@635

@prisma-next/ids

npm i https://pkg.pr.new/@prisma-next/ids@635

@prisma-next/psl-parser

npm i https://pkg.pr.new/@prisma-next/psl-parser@635

@prisma-next/psl-printer

npm i https://pkg.pr.new/@prisma-next/psl-printer@635

@prisma-next/cli

npm i https://pkg.pr.new/@prisma-next/cli@635

@prisma-next/cli-telemetry

npm i https://pkg.pr.new/@prisma-next/cli-telemetry@635

@prisma-next/emitter

npm i https://pkg.pr.new/@prisma-next/emitter@635

@prisma-next/migration-tools

npm i https://pkg.pr.new/@prisma-next/migration-tools@635

prisma-next

npm i https://pkg.pr.new/prisma-next@635

@prisma-next/vite-plugin-contract-emit

npm i https://pkg.pr.new/@prisma-next/vite-plugin-contract-emit@635

@prisma-next/mongo-codec

npm i https://pkg.pr.new/@prisma-next/mongo-codec@635

@prisma-next/mongo-contract

npm i https://pkg.pr.new/@prisma-next/mongo-contract@635

@prisma-next/mongo-value

npm i https://pkg.pr.new/@prisma-next/mongo-value@635

@prisma-next/mongo-contract-psl

npm i https://pkg.pr.new/@prisma-next/mongo-contract-psl@635

@prisma-next/mongo-contract-ts

npm i https://pkg.pr.new/@prisma-next/mongo-contract-ts@635

@prisma-next/mongo-emitter

npm i https://pkg.pr.new/@prisma-next/mongo-emitter@635

@prisma-next/mongo-schema-ir

npm i https://pkg.pr.new/@prisma-next/mongo-schema-ir@635

@prisma-next/mongo-query-ast

npm i https://pkg.pr.new/@prisma-next/mongo-query-ast@635

@prisma-next/mongo-orm

npm i https://pkg.pr.new/@prisma-next/mongo-orm@635

@prisma-next/mongo-query-builder

npm i https://pkg.pr.new/@prisma-next/mongo-query-builder@635

@prisma-next/mongo-lowering

npm i https://pkg.pr.new/@prisma-next/mongo-lowering@635

@prisma-next/mongo-wire

npm i https://pkg.pr.new/@prisma-next/mongo-wire@635

@prisma-next/sql-contract

npm i https://pkg.pr.new/@prisma-next/sql-contract@635

@prisma-next/sql-errors

npm i https://pkg.pr.new/@prisma-next/sql-errors@635

@prisma-next/sql-operations

npm i https://pkg.pr.new/@prisma-next/sql-operations@635

@prisma-next/sql-schema-ir

npm i https://pkg.pr.new/@prisma-next/sql-schema-ir@635

@prisma-next/sql-contract-psl

npm i https://pkg.pr.new/@prisma-next/sql-contract-psl@635

@prisma-next/sql-contract-ts

npm i https://pkg.pr.new/@prisma-next/sql-contract-ts@635

@prisma-next/sql-contract-emitter

npm i https://pkg.pr.new/@prisma-next/sql-contract-emitter@635

@prisma-next/sql-lane-query-builder

npm i https://pkg.pr.new/@prisma-next/sql-lane-query-builder@635

@prisma-next/sql-relational-core

npm i https://pkg.pr.new/@prisma-next/sql-relational-core@635

@prisma-next/sql-builder

npm i https://pkg.pr.new/@prisma-next/sql-builder@635

@prisma-next/target-postgres

npm i https://pkg.pr.new/@prisma-next/target-postgres@635

@prisma-next/target-sqlite

npm i https://pkg.pr.new/@prisma-next/target-sqlite@635

@prisma-next/adapter-postgres

npm i https://pkg.pr.new/@prisma-next/adapter-postgres@635

@prisma-next/adapter-sqlite

npm i https://pkg.pr.new/@prisma-next/adapter-sqlite@635

@prisma-next/driver-postgres

npm i https://pkg.pr.new/@prisma-next/driver-postgres@635

@prisma-next/driver-sqlite

npm i https://pkg.pr.new/@prisma-next/driver-sqlite@635

commit: d8d9205

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 30, 2026

size-limit report 📦

Path Size
postgres / no-emit 135.35 KB (0%)
postgres / emit 125.16 KB (0%)
mongo / no-emit 73.85 KB (0%)
mongo / emit 68.85 KB (0%)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 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 `@packages/1-framework/3-tooling/cli/README.md`:
- Line 1046: The statement "After each migration, the runner verifies the schema
against the target contract and updates the marker/ledger" is too broad; change
it to explicitly scope verification to the specific migration target/family that
was applied (e.g., "After each migration, the runner verifies the schema for the
applied target/family against its target contract and updates that target's
marker/ledger"). Update the README text to use the phrase "applied
target/family" or similar to clarify verification is per-target rather than
global.

In `@packages/1-framework/3-tooling/cli/src/commands/migrate.ts`:
- Around line 293-318: The JSON.parse(endContractRaw) call can throw for
malformed end-contract.json and currently escapes the deserializeContract catch,
causing an unexpected error; wrap the parse in the same validation error path:
try to JSON.parse(endContractRaw) and on any parse error return the structured
notOk(errorContractValidationFailed(...)) referencing endContractPath (mirroring
the deserializeContract catch), then proceed to call
familyInstance.deserializeContract(parsed) as before; ensure both parse and
deserialize failures produce the same validation error that includes the path
and the original error message.

In `@packages/1-framework/3-tooling/cli/src/commands/migration-plan.ts`:
- Around line 83-87: The error handler currently passes migrationDir to
errorFileNotFound, which points the CLI error at the bundle directory instead of
the actual missing snapshot file; change the call so it passes the concrete
artifact path that failed to be read (the path variable used for the sibling
read — e.g. the end-contract.json or end-contract.d.ts path such as
endContractJsonPath or endContractDtsPath) into errorFileNotFound, preserving
the same why/fix metadata.

In `@packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts`:
- Around line 270-281: The planCommand builder currently treats meta.fromHash
set to the placeholder string '<empty>' as a real fromHash, producing an invalid
CLI snippet; update the guard in the planCommand IIFE to treat the placeholder
as absent by checking that fromHash is not null AND not equal to '<empty>' (use
the same check wherever fromHash is read), so branches use fromHash only when
fromHash !== null && fromHash !== '<empty>' (keep targetHash checks unchanged).
🪄 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: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 19269fb5-2c8c-42f4-ba6e-913d9890eebc

📥 Commits

Reviewing files that changed from the base of the PR and between ab6eaaa and bf34571.

⛔ Files ignored due to path filters (1)
  • projects/migrate-to-rollback-plannable/spec.md is excluded by !projects/**
📒 Files selected for processing (12)
  • docs/architecture docs/subsystems/7. Migration System.md
  • packages/1-framework/3-tooling/cli/README.md
  • packages/1-framework/3-tooling/cli/src/commands/migrate.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-plan.ts
  • packages/1-framework/3-tooling/cli/src/control-api/operations/migration-apply.ts
  • packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts
  • packages/1-framework/3-tooling/cli/src/utils/plan-resolution.ts
  • packages/1-framework/3-tooling/cli/test/cli-errors.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migrate-to-contract.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-plan-command.test.ts
  • packages/1-framework/3-tooling/cli/test/utils/plan-resolution.test.ts
  • test/integration/test/cli-journeys/plan-to-rollback.e2e.test.ts

Comment thread packages/1-framework/3-tooling/cli/README.md Outdated
Comment thread packages/1-framework/3-tooling/cli/src/commands/migrate.ts Outdated
Comment thread packages/1-framework/3-tooling/cli/src/commands/migration-plan.ts Outdated
Comment thread packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts
wmadden-electric and others added 4 commits May 30, 2026 16:00
…L-2690)

Move target end-contract JSON.parse into the deserialize try/catch so corrupt
snapshots surface PN-CLI-4003 with the file path. Name the missing sibling on
ENOENT in readBundleEndArtifacts. Treat the empty-marker sentinel as absent
when composing migration plan recovery commands. Reword migrate README post-
apply bullet to match universal post-check semantics.

Signed-off-by: wmadden-electric <286902546+wmadden-electric@users.noreply.github.com>
…ir-advertised-in-help-but-fails-with

Signed-off-by: Will Madden <madden@prisma.io>

# Conflicts:
#	packages/1-framework/3-tooling/cli/src/commands/migrate.ts
Comment thread packages/1-framework/3-tooling/cli/src/commands/migrate.ts Outdated
Comment thread packages/1-framework/3-tooling/cli/src/commands/migration-plan.ts Outdated
wmadden added 5 commits May 30, 2026 18:33
Expose lazy graph-node contract materialization on aggregate members so CLI
commands can resolve ref snapshots and bundle end-contract bookends without
hand-rolled disk reads. Resolution mirrors plan-resolution semantics with
typed MigrationToolsError codes for D6 wiring.

Signed-off-by: Will Madden <madden@prisma.io>
Route --from/--to resolution in migration plan through the tolerant
aggregate member's contractAt facet instead of hand-rolled readRefs,
readRefSnapshot, and bundle end-contract reads. loadContractSpaceAggregateForCli
now runs before from/to resolution; post-seed buildContractSpaceAggregate
remains for validated planning.

Signed-off-by: Will Madden <madden@prisma.io>
Use aggregate.app.refs and contractAt for target contract resolution
instead of readRefs and inline end-contract.json reads. Extract
mapContractAtError to a shared util reused by plan-resolution.

Signed-off-by: Will Madden <madden@prisma.io>
Add consolidation design and D5-D8 dispatch plan to slice spec after
operator review on PR #635.

Signed-off-by: Will Madden <madden@prisma.io>
Signed-off-by: Will Madden <madden@prisma.io>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/1-framework/3-tooling/migration/src/aggregate/aggregate.ts (1)

28-30: ⚡ Quick win

Avoid the bare cast in hasErrnoCode.

This helper can narrow code structurally and stay within the repo's casting rule.

Suggested change
 function hasErrnoCode(error: unknown, code: string): boolean {
-  return error instanceof Error && (error as { code?: string }).code === code;
+  return (
+    error instanceof Error &&
+    'code' in error &&
+    typeof error.code === 'string' &&
+    error.code === code
+  );
 }

As per coding guidelines, "No bare as in production code. Use blindCast<T, "Reason"> or castAs<T> from @prisma-next/utils/casts; see the no-bare-casts skill for the decision tree."

🤖 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 `@packages/1-framework/3-tooling/migration/src/aggregate/aggregate.ts` around
lines 28 - 30, The helper hasErrnoCode currently uses a bare cast (error as {
code?: string }); replace that with the repo-approved cast helpers from
`@prisma-next/utils/casts` (either blindCast<{ code?: string }, "Reason"> or
castAs<{ code?: string }>) so the runtime check stays the same but avoids a bare
`as`; update the import to bring in blindCast or castAs and use it in
hasErrnoCode to access .code safely when error instanceof Error.
🤖 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 `@packages/1-framework/3-tooling/cli/src/utils/plan-resolution.ts`:
- Around line 108-118: The code currently classifies results as kind: 'snapshot'
based solely on refName input; change it to classify by the actual provenance
returned from member.contractAt(hash, ...). After calling member.contractAt,
inspect the returned object's provenance/source field (e.g., at.provenance,
at.source or similar indicator present on the returned `at` object) and if it
indicates the on-disk bundle/graph-node origin return the appropriate kind
(e.g., 'bundle' or 'graph-node') and include sourceDir, otherwise return kind:
'snapshot'; also ensure resolveFromPolicy() continues to receive and use that
provenance/kind instead of inferring from the original refName.

---

Nitpick comments:
In `@packages/1-framework/3-tooling/migration/src/aggregate/aggregate.ts`:
- Around line 28-30: The helper hasErrnoCode currently uses a bare cast (error
as { code?: string }); replace that with the repo-approved cast helpers from
`@prisma-next/utils/casts` (either blindCast<{ code?: string }, "Reason"> or
castAs<{ code?: string }>) so the runtime check stays the same but avoids a bare
`as`; update the import to bring in blindCast or castAs and use it in
hasErrnoCode to access .code safely when error instanceof Error.
🪄 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: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: b0565561-2961-48c2-a331-031731d35a9b

📥 Commits

Reviewing files that changed from the base of the PR and between 5a74e07 and 7611d81.

⛔ Files ignored due to path filters (1)
  • projects/migrate-to-rollback-plannable/spec.md is excluded by !projects/**
📒 Files selected for processing (21)
  • docs/architecture docs/subsystems/7. Migration System.md
  • packages/1-framework/3-tooling/cli/src/commands/migrate.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-plan.ts
  • packages/1-framework/3-tooling/cli/src/control-api/operations/migration-apply.ts
  • packages/1-framework/3-tooling/cli/src/utils/contract-at-errors.ts
  • packages/1-framework/3-tooling/cli/src/utils/plan-resolution.ts
  • packages/1-framework/3-tooling/cli/test/commands/migrate-to-contract.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-plan-command.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-status-aggregate-spaces.test.ts
  • packages/1-framework/3-tooling/cli/test/control-api/apply-aggregate.test.ts
  • packages/1-framework/3-tooling/cli/test/control-api/db-verify.per-member-verifier.test.ts
  • packages/1-framework/3-tooling/cli/test/utils/plan-resolution.test.ts
  • packages/1-framework/3-tooling/migration/src/aggregate/aggregate.ts
  • packages/1-framework/3-tooling/migration/src/aggregate/loader.ts
  • packages/1-framework/3-tooling/migration/src/aggregate/types.ts
  • packages/1-framework/3-tooling/migration/src/errors.ts
  • packages/1-framework/3-tooling/migration/src/exports/aggregate.ts
  • packages/1-framework/3-tooling/migration/test/aggregate/check-integrity.test.ts
  • packages/1-framework/3-tooling/migration/test/aggregate/contract-at.test.ts
  • packages/1-framework/3-tooling/migration/test/fixtures.ts
  • packages/3-mongo-target/1-mongo-target/src/core/control-target.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/architecture docs/subsystems/7. Migration System.md
🚧 Files skipped from review as they are similar to previous changes (17)
  • packages/1-framework/3-tooling/cli/test/commands/migration-status-aggregate-spaces.test.ts
  • packages/1-framework/3-tooling/migration/test/aggregate/check-integrity.test.ts
  • packages/1-framework/3-tooling/cli/test/control-api/db-verify.per-member-verifier.test.ts
  • packages/1-framework/3-tooling/migration/src/exports/aggregate.ts
  • packages/1-framework/3-tooling/cli/test/commands/migrate-to-contract.test.ts
  • packages/1-framework/3-tooling/cli/test/control-api/apply-aggregate.test.ts
  • packages/1-framework/3-tooling/migration/test/fixtures.ts
  • packages/1-framework/3-tooling/migration/src/aggregate/types.ts
  • packages/1-framework/3-tooling/migration/src/aggregate/loader.ts
  • packages/1-framework/3-tooling/migration/src/errors.ts
  • packages/1-framework/3-tooling/cli/src/control-api/operations/migration-apply.ts
  • packages/1-framework/3-tooling/cli/src/utils/contract-at-errors.ts
  • packages/1-framework/3-tooling/cli/src/commands/migrate.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-plan-command.test.ts
  • packages/1-framework/3-tooling/migration/test/aggregate/contract-at.test.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-plan.ts
  • packages/1-framework/3-tooling/cli/test/utils/plan-resolution.test.ts

Comment thread packages/1-framework/3-tooling/cli/src/utils/plan-resolution.ts
wmadden added 5 commits May 30, 2026 19:05
contractAt now tags results as snapshot vs graph-node (with sourceDir),
so resolveContractRef classifies --from refs by actual provenance instead
of assuming snapshot whenever refName is set.

Signed-off-by: Will Madden <madden@prisma.io>
… union

sourceDir is now statically present on the graph-node variant, removing the
non-null assertion in resolveContractRef.

Signed-off-by: Will Madden <madden@prisma.io>
Minimal contract envelopes must include an empty namespaces map so
aggregate integrity checks can walk storage coordinates after ADR 221.

Signed-off-by: Will Madden <madden@prisma.io>
@wmadden wmadden merged commit 417b405 into main May 30, 2026
12 checks passed
@wmadden wmadden deleted the tml-2690-migrate-to-dir-advertised-in-help-but-fails-with branch May 30, 2026 19:05
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.

2 participants