Skip to content

TML-2716: route all read commands through ContractSpaceAggregate#644

Merged
wmadden merged 14 commits into
mainfrom
tml-2716-adopt-read-commands
May 30, 2026
Merged

TML-2716: route all read commands through ContractSpaceAggregate#644
wmadden merged 14 commits into
mainfrom
tml-2716-adopt-read-commands

Conversation

@wmadden-electric
Copy link
Copy Markdown
Contributor

@wmadden-electric wmadden-electric commented May 30, 2026

Linked issue

Refs TML-2716. Slice 2 of the migration-store project (TML-2709); builds on the tolerant, queryable ContractSpaceAggregate landed in Slice 1 (TML-2715).

At a glance

Every CLI command that reads migration packages now loads them once through the aggregate instead of its own ad-hoc package walk:

const loaded = await buildReadAggregate(config, { migrationsDir });
if (!loaded.ok) {
  return loaded;
}
const { aggregate, contractHash } = loaded.value;
const graph = aggregate.app.graph();
const refs = aggregate.app.refs;

Before this slice, each of these commands called loadMigrationPackages (and several also called enumerateMigrationSpaces, readRefs, and readContractEnvelope) directly, each reconstructing its own view of on-disk migration state.

Decision

This PR routes all seven migration-package-reading CLI commands — migration list, migration graph, migration log, db sign, db update, migration plan, and ref set — through a single read path (buildReadAggregateContractSpaceAggregate), and deletes both legacy helpers that path replaces:

  1. loadMigrationPackages (in cli/src/utils/command-helpers.ts)
  2. enumerateMigrationSpaces (the whole migration-tools/enumerate-migration-spaces module, its export entry, and its test)

The result is net-deletion: one model of migration state for read commands, no per-command package walks. Happy-path human output, --json, and structured errors are unchanged for every command — this is pure adoption, not a behavior change.

How it fits together

  1. Extract the read path. buildReadAggregate (in cli/src/utils/contract-space-aggregate-loader.ts) loads the ContractSpaceAggregate without the integrity gate that write commands use, and returns { aggregate, contractHash }. The offline-fallback helpers (appContractShellForAggregateLoad, loadContractRawSafely) move here from migration-status.ts so both status and read commands share them.
  2. Re-point the graph/log/writer-read commands. migration graph, migration log, db sign, db update, migration plan, and ref set swap their loadMigrationPackages calls for buildReadAggregate, deriving graph/packages/refs/contract-hash from the aggregate. loadMigrationPackages is then deleted.
  3. Re-point migration list and delete the enumerator. A new mapper, migrationSpaceListEntriesFromAggregate, derives the render-ready MigrationSpaceListEntry[] from aggregate members, using the same on-disk space-id ordering the old enumerator used (app-first, lexical, reserved-name filtered) so extension-only and empty-dir cases stay identical. enumerateMigrationSpaces is deleted; refsByContractHash / resolveRefsByContractHash move into migration-tools/refs.
  4. Pin the output. Each touched command gets a --json golden test (back-filling migration graph, which had no command-level test), so the "no observable change" claim is enforced rather than asserted in prose.

Reviewer notes

  • Largest semantic risk is migration list. The aggregate always exposes a synthetic app space even when migrations/app/ is absent on disk; the old enumerator listed only on-disk directories. The mapper resolves this by driving enumeration from the on-disk space-id list and reading aggregate.space(id) per id, so extension-only disks stay extension-only. Worth a spot-check.
  • Head-ref decoration parity. The tolerant model's member.refs excludes the structural refs/head.json, which the old list (via readRefs) included — so an extension space's tip-migration row would have lost its head decoration. The mapper folds member.headRef back into the by-destination-hash decoration to preserve byte-identical output. Whether head should appear in list decorations at all is a separate semantics question (follow-up, not this slice).
  • ref list / ref delete keep readRefs. Only ref set reads packages, so only it moves to the aggregate; the other two ref subcommands don't touch package state and are untouched.
  • Merge with main. This branch merged origin/main mid-flight (conflicts in migration-list.test.ts and a drive calibration doc); main's independent migration list graph-rendering work and a main-authored list golden test are both preserved on top of the aggregate re-point.

Verification

  • pnpm --filter @prisma-next/cli typecheck && pnpm --filter @prisma-next/cli lint && pnpm --filter @prisma-next/cli test — 1080 cases
  • pnpm --filter @prisma-next/migration-tools typecheck && pnpm --filter @prisma-next/migration-tools test — 555 cases
  • git grep loadMigrationPackages / git grep enumerateMigrationSpaces over packages/, examples/, test/ — zero hits
  • CI re-runs the full matrix on this PR.

Follow-ups

  • Whether head should appear in migration list ref decorations at all (semantics question deferred above) — to be filed at project close-out.
  • Reader-policy uniformity across read commands — TML-2734.

Alternatives considered

  • Keep loadMigrationPackages for the writer-read commands (db sign, db update, migration plan, ref set) and only migrate the three pure-read commands. Rejected: the writer-read commands' package reads are the same mechanical swap, and leaving the helper alive would block the net-deletion that is the point of the slice — the thesis "every command that reads migration packages goes through the aggregate" only holds if all seven move.
  • Change the aggregate to omit the synthetic app space when migrations/app/ is absent. Rejected for this slice: that's a model-semantics change with its own blast radius; the mapper reconciles disk-vs-synthetic locally instead, keeping this slice pure-adoption. The model question is noted as a follow-up.

Skill update

n/a — internal only. No CLI flags, public APIs, config fields, error codes, or glossary terms change; this is a read-path refactor with byte-identical command output.

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 (golden --json tests added for each touched command).
  • The PR title is in TML-NNNN: <sentence-case title> form.
  • The Skill update section above is filled in.

Summary by CodeRabbit

  • Bug Fixes

    • Improved robustness of migration commands when contract files are missing or invalid, with automatic fallback handling.
  • Refactor

    • Reorganized internal migration data loading and reference handling for consistency across CLI commands.
    • Internal restructuring of migration list utilities and reference resolution.

Review Change Stack

wmadden added 11 commits May 30, 2026 17:39
Land the slice-1 (TML-2715) retro lesson across the drive calibration
surfaces: dispatch validation must mirror CI's job matrix, not just
typecheck. PR #626 reported green then failed CI on lint (formatter +
unused import in a test file) and on behind-main test drift.

- failure-modes.md: new F11 (dispatch green / CI red).
- calibration/dod.md: pnpm lint becomes an always-run dispatch gate;
  typecheck must cover the package test tsconfig; sync origin/main
  before final slice validation + push.
- retro/findings.md: trial finding for the 2026-05-30 miss.

Signed-off-by: Will Madden <madden@prisma.io>
Slice 2 re-points every CLI command that reads migration packages onto
the slice-1 ContractSpaceAggregate via a shared buildReadAggregate
helper, then deletes both hand-rolled loaders (loadMigrationPackages +
enumerateMigrationSpaces).

Scope settled with operator: grounding found loadMigrationPackages had
six callers (graph, log, db-sign, db-update, migration-plan, ref), not
just list/graph/log, so all seven package-reading commands fold in to
make the helper deletable. Plan: 3 dispatches (helper+graph; fan-out+
delete loadMigrationPackages; list+delete enumerateMigrationSpaces).

Signed-off-by: Will Madden <madden@prisma.io>
Extract the migration-status offline aggregate assembly into a shared
helper with relocated appContractShellForAggregateLoad and
loadContractRawSafely. No integrity gate; unit tests cover readable and
missing contract paths.

Signed-off-by: Will Madden <madden@prisma.io>
Replace loadMigrationPackages, readRefs, and readContractEnvelope with one
offline aggregate build. Graph, refs, and contract marker come from
aggregate.app; top-level migrationsDir matches migration status.

Signed-off-by: Will Madden <madden@prisma.io>
D1 (buildReadAggregate + migration graph) reviewed SATISFIED. Land the
two slice-level decisions the reviewer surfaced: golden --json tests
required before AC-2 close (incl. backfilling migration graph, which
shipped without one), and AC-2 "identical output" recorded as a
happy-path guarantee (corruption-path refs now tolerant by design).

Signed-off-by: Will Madden <madden@prisma.io>
Re-point migration log, db sign, db update, migration plan, and ref
package reads through buildReadAggregate and aggregate.app facets.
Delete loadMigrationPackages and update the control-api doc reference.

Signed-off-by: Will Madden <madden@prisma.io>
Pin migration graph, log, ref set, and db sign JSON output. Add db update
--to dry-run golden and align ref/plan tests with buildReadAggregate config.

Signed-off-by: Will Madden <madden@prisma.io>
Re-point migration list through buildReadAggregate and a mapper that
preserves on-disk space membership. Move refsByContractHash to refs and
delete enumerateMigrationSpaces from migration-tools.

Signed-off-by: Will Madden <madden@prisma.io>
Exercise list via loadContractSpaceAggregate in unit tests, add --json
golden coverage, and relocate resolveRefsByContractHash tests to refs.

Signed-off-by: Will Madden <madden@prisma.io>
Sync with origin/main before adopt-read-commands slice close-out.

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

# Conflicts:
#	drive/calibration/failure-modes.md
#	packages/1-framework/3-tooling/cli/test/commands/migration-list.test.ts
Export HEAD_REF_NAME for extension-space list decorations, fold headRef
into the refs-by-hash map, add doUnmock guards on json golden suites, and
wire main's per-space graph test through runMigrationListFromDisk.

Signed-off-by: Will Madden <madden@prisma.io>
@wmadden-electric wmadden-electric requested a review from a team as a code owner May 30, 2026 17:57
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

📝 Walkthrough

Walkthrough

This PR consolidates CLI migration data loading through a new read-only aggregate loader while refactoring migration list to accept pre-enumerated spaces. It adds refs-indexing helpers, updates all affected commands and tests, removes obsolete enumeration functions, and updates dispatch validation gates per lessons learned from deployment.

Changes

CLI Aggregate-Based Data Loading Refactor

Layer / File(s) Summary
Dispatch DoD updates and retro findings
drive/calibration/dod.md, drive/calibration/failure-modes.md, drive/retro/findings.md
Updated always-run validation gates to require per-package lint and test-tsconfig typecheck coverage; documented F14 failure mode (dispatch-to-CI gaps); recorded dispatch lessons learned requiring lint/typecheck/test bundling.
Read-only aggregate loader infrastructure
packages/1-framework/3-tooling/cli/src/utils/contract-space-aggregate-loader.ts, packages/1-framework/3-tooling/cli/src/utils/command-helpers.ts
Added buildReadAggregate, appContractStandInFromIdentity, and loadContractRawSafely functions for tolerant read-only aggregate loading; removed loadMigrationPackages export.
Contract-hash indexed ref helpers
packages/1-framework/3-tooling/migration/src/refs.ts, packages/1-framework/3-tooling/migration/src/exports/refs.ts
Added refsByContractHash and resolveRefsByContractHash to bucket refs by destination hash; updated migration package exports.
Migration list core refactored for pre-enumerated spaces
packages/1-framework/3-tooling/cli/src/commands/migration-list.ts
Made runMigrationList synchronous and accept pre-computed spaces array; added migrationSpaceListEntriesFromAggregate to convert aggregate into ordered space entries with refs folding; exported executeMigrationListCommand.
Database commands use aggregate pattern
packages/1-framework/3-tooling/cli/src/commands/db-sign.ts, packages/1-framework/3-tooling/cli/src/commands/db-update.ts
Updated db sign and db update --to to resolve contracts via buildReadAggregate instead of separate package and refs loading.
Migration read commands use aggregate pattern
packages/1-framework/3-tooling/cli/src/commands/migration-graph.ts, packages/1-framework/3-tooling/cli/src/commands/migration-log.ts, packages/1-framework/3-tooling/cli/src/commands/ref.ts, packages/1-framework/3-tooling/cli/src/commands/migration-status.ts, packages/1-framework/3-tooling/cli/src/control-api/types.ts
Refactored all migration read commands to load data via buildReadAggregate; exported command executors; updated migration status to use aggregate contract helpers; reworded MigrationApplyStep documentation.
Cleanup old enumeration functions and exports
packages/1-framework/3-tooling/migration/package.json, packages/1-framework/3-tooling/migration/tsdown.config.ts
Removed obsolete loadMigrationPackages from command-helpers; removed enumerate-migration-spaces module and export; updated package.json and build config.
Formatter modules consolidate imports
packages/1-framework/3-tooling/cli/src/utils/formatters/*.ts, packages/1-framework/3-tooling/cli/test/utils/formatters/*.ts, packages/1-framework/3-tooling/migration/src/exports/migration-list-*.ts
Updated all migration-list formatter modules and tests to use local relative imports instead of @prisma-next/migration-tools/* package paths; removed re-exports of formatter types from migration package.
Golden and integration tests for aggregate-based commands
packages/1-framework/3-tooling/cli/test/commands/db-update-read-aggregate-json-golden.test.ts, packages/1-framework/3-tooling/cli/test/commands/migration-list-json-golden.test.ts, packages/1-framework/3-tooling/cli/test/commands/read-commands-json-golden.test.ts, packages/1-framework/3-tooling/cli/test/utils/build-read-aggregate.test.ts
Added comprehensive Vitest golden tests verifying CLI JSON output, aggregate loading, contract fallback behavior, and command behavior with mocked config and control API.
Migration list tests refactored for aggregate pattern
packages/1-framework/3-tooling/cli/test/commands/migration-list.test.ts
Updated all ~40 test invocations to use new runMigrationListFromDisk harness that loads contract-space aggregates; added harness functions and new Postgres head-ref decoration test case.
Refs test and command test updates
packages/1-framework/3-tooling/migration/test/refs.test.ts, packages/1-framework/3-tooling/cli/test/commands/ref.test.ts
Extended refs tests for new resolveRefsByContractHash function; updated ref command test to write contract.json fixture.
Test mock cleanup
packages/1-framework/3-tooling/cli/test/commands/migration-plan-command.test.ts, packages/1-framework/3-tooling/cli/test/commands/migration-tamper.test.ts
Removed mocking of loadMigrationPackages from migration plan tests; updated fixture comment to reference aggregate read pattern.

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

  • prisma/prisma-next#626: Refactored the underlying contract-space aggregate model to be tolerant and queryable with checkIntegrity(), which this PR builds upon.
  • prisma/prisma-next#603: Prior work on migration-list CLI command structure that this PR's refactor replaces.
  • prisma/prisma-next#383: Refactored migration-graph API (PathDecision import wiring) that affects command-helpers import updates in this PR.

Suggested Reviewers

  • wmadden
  • SevInf

🐰 A rabbit applauds the clean refactor clean,
With aggregates that load serene.
Spaces enumerated, refs bucketed bright,
The dispatch gates now validate right!
CLI commands dance in unified light. 🎯

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.00% 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-2716: route all read commands through ContractSpaceAggregate' clearly and specifically summarizes the main architectural change: consolidating CLI read commands to use a single migration-loading path via ContractSpaceAggregate.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tml-2716-adopt-read-commands

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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@644

@prisma-next/mongo-runtime

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

@prisma-next/family-mongo

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

@prisma-next/sql-runtime

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

@prisma-next/family-sql

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

@prisma-next/extension-arktype-json

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

@prisma-next/extension-cipherstash

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

@prisma-next/middleware-cache

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

@prisma-next/mongo

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

@prisma-next/extension-paradedb

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

@prisma-next/extension-pgvector

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

@prisma-next/extension-postgis

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

@prisma-next/postgres

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

@prisma-next/sql-orm-client

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

@prisma-next/sqlite

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

@prisma-next/target-mongo

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

@prisma-next/adapter-mongo

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

@prisma-next/driver-mongo

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

@prisma-next/contract

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

@prisma-next/utils

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

@prisma-next/config

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

@prisma-next/errors

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

@prisma-next/framework-components

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

@prisma-next/operations

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

@prisma-next/ts-render

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

@prisma-next/contract-authoring

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

@prisma-next/ids

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

@prisma-next/psl-parser

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

@prisma-next/psl-printer

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

@prisma-next/cli

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

@prisma-next/cli-telemetry

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

@prisma-next/emitter

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

@prisma-next/migration-tools

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

prisma-next

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

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

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

@prisma-next/mongo-codec

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

@prisma-next/mongo-contract

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

@prisma-next/mongo-value

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

@prisma-next/mongo-contract-psl

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

@prisma-next/mongo-contract-ts

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

@prisma-next/mongo-emitter

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

@prisma-next/mongo-schema-ir

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

@prisma-next/mongo-query-ast

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

@prisma-next/mongo-orm

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

@prisma-next/mongo-query-builder

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

@prisma-next/mongo-lowering

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

@prisma-next/mongo-wire

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

@prisma-next/sql-contract

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

@prisma-next/sql-errors

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

@prisma-next/sql-operations

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

@prisma-next/sql-schema-ir

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

@prisma-next/sql-contract-psl

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

@prisma-next/sql-contract-ts

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

@prisma-next/sql-contract-emitter

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

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

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

@prisma-next/sql-relational-core

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

@prisma-next/sql-builder

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

@prisma-next/target-postgres

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

@prisma-next/target-sqlite

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

@prisma-next/adapter-postgres

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

@prisma-next/adapter-sqlite

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

@prisma-next/driver-postgres

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

@prisma-next/driver-sqlite

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

commit: 14ea728

@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.9 KB (0%)
mongo / emit 68.89 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 `@drive/calibration/dod.md`:
- Line 82: The markdown link in the "Sync `origin/main` before the final
validation + push" checklist entry points to the wrong failure-mode anchor
(currently
`#f11-dispatch-reports-validation-green-but-ci-is-red-dispatch-gates-didnt-mirror-ci`);
update that link to the correct anchor for F14 (replace the `#f11...` fragment
with the F14 fragment `#f14-...` so the link targets § F14 in failure-modes.md),
ensuring the visible text and surrounding checklist remain unchanged.
- Line 18: Update the cross-reference in drive/calibration/dod.md so the
failure-mode link points to the newly added F14 section instead of F11: change
the reference text/URL segment that reads "failure-modes.md § F11" to
"failure-modes.md § F14" (i.e., point to the F14 anchor added in
failure-modes.md) so the sentence about lint being a separate CI job links to
the correct failure-mode entry.

In `@drive/retro/findings.md`:
- Line 11: Update the incorrect cross-reference that points to "failure-modes.md
§ F11" so it instead points to the newly added failure mode F14; search for
occurrences of the string "failure-modes.md § F11" (including the references
inside drive/retro/findings.md and drive/calibration/dod.md) and replace them
with "failure-modes.md § F14" so the drive/retro/findings and dod references
correctly link to the F14 section.

In
`@packages/1-framework/3-tooling/cli/src/utils/contract-space-aggregate-loader.ts`:
- Around line 339-357: The control stack and family bootstrap
(createControlStack(config) and config.family.create(stack)) are executed before
the error-handling try block so failures throw instead of being returned as a
CliStructuredError; move the creation of stack and familyInstance (and
deserializeContract closure if you prefer) into the existing try that surrounds
the aggregate load so any exceptions from createControlStack or
config.family.create are caught and converted into the Result/CliStructuredError
path used by buildReadAggregate, preserving the appContractShell fallback logic
(appContractShell/appContractForLoad) and keeping the outer API that returns a
Result.
🪄 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: b6a4db92-17c5-456d-a76d-298831513e17

📥 Commits

Reviewing files that changed from the base of the PR and between c37feca and 955d4df.

⛔ Files ignored due to path filters (2)
  • projects/migration-store/plan.md is excluded by !projects/**
  • projects/migration-store/slices/adopt-read-commands/spec.md is excluded by !projects/**
📒 Files selected for processing (31)
  • drive/calibration/dod.md
  • drive/calibration/failure-modes.md
  • drive/retro/findings.md
  • packages/1-framework/3-tooling/cli/src/commands/db-sign.ts
  • packages/1-framework/3-tooling/cli/src/commands/db-update.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-graph.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-list.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-log.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-plan.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-status.ts
  • packages/1-framework/3-tooling/cli/src/commands/ref.ts
  • packages/1-framework/3-tooling/cli/src/control-api/types.ts
  • packages/1-framework/3-tooling/cli/src/utils/command-helpers.ts
  • packages/1-framework/3-tooling/cli/src/utils/contract-space-aggregate-loader.ts
  • packages/1-framework/3-tooling/cli/src/utils/migration-space-list-from-aggregate.ts
  • packages/1-framework/3-tooling/cli/test/commands/db-update-read-aggregate-json-golden.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-list-json-golden.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-list.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-plan-command.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-tamper.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/read-commands-json-golden.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/ref.test.ts
  • packages/1-framework/3-tooling/cli/test/utils/build-read-aggregate.test.ts
  • packages/1-framework/3-tooling/migration/package.json
  • packages/1-framework/3-tooling/migration/src/enumerate-migration-spaces.ts
  • packages/1-framework/3-tooling/migration/src/exports/enumerate-migration-spaces.ts
  • packages/1-framework/3-tooling/migration/src/exports/refs.ts
  • packages/1-framework/3-tooling/migration/src/refs.ts
  • packages/1-framework/3-tooling/migration/test/enumerate-migration-spaces.test.ts
  • packages/1-framework/3-tooling/migration/test/refs.test.ts
  • packages/1-framework/3-tooling/migration/tsdown.config.ts
💤 Files with no reviewable changes (6)
  • packages/1-framework/3-tooling/migration/src/enumerate-migration-spaces.ts
  • packages/1-framework/3-tooling/migration/src/exports/enumerate-migration-spaces.ts
  • packages/1-framework/3-tooling/migration/test/enumerate-migration-spaces.test.ts
  • packages/1-framework/3-tooling/migration/tsdown.config.ts
  • packages/1-framework/3-tooling/cli/src/utils/command-helpers.ts
  • packages/1-framework/3-tooling/migration/package.json

Comment thread drive/calibration/dod.md Outdated
Comment thread drive/calibration/dod.md Outdated
Comment thread drive/retro/findings.md Outdated
Comment thread packages/1-framework/3-tooling/cli/src/utils/contract-space-aggregate-loader.ts Outdated
return ok(loaded.value);
}

export function appContractShellForAggregateLoad(args: {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What on earth does this method name mean?

readonly targetId: string;
readonly targetFamily: string;
}): Contract {
return blindCast<Contract, 'offline read aggregate without contract.json'>({
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This reason does not adequately explain why this cast exists, it just names it.

Comment on lines +317 to +322
/**
* Offline tolerant {@link ContractSpaceAggregate} assembly for read-only
* CLI commands. No integrity gate — callers query `aggregate.app` (or
* other facets) without re-reading `migrations/`.
*/
export async function buildReadAggregate(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Isn't the aggregate always loaded from disk - i.e. offline?

Comment on lines +75 to +85
const migrations: MigrationListEntry[] = member.packages
.map((pkg) => ({
dirName: pkg.dirName,
from: pkg.metadata.from,
to: pkg.metadata.to,
migrationHash: pkg.metadata.migrationHash,
operationCount: pkg.ops.length,
createdAt: pkg.metadata.createdAt,
refs: refsByHash.get(pkg.metadata.to) ?? [],
providedInvariants: pkg.metadata.providedInvariants,
}))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This map looks like a smell. Why do we maintain two similar but not identical representations of the same data structure? Can we consolidate and delete MigrationListEntry? Or otherwise, this mapping seems like it belongs in the consumer, migration list.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What is this file? These look like centralized junk methods that convert the canonical contract-space aggregate into other, worse data structures. Looks like a backwards compatibilitiy shim

wmadden added 2 commits May 30, 2026 21:07
…t stand-in naming

Move the CLI-only migration list presentation cluster (MigrationListEntry /
MigrationSpaceListEntry / MigrationListResult + classifyMigrationListGraphTopology
+ MigrationListGraphTopology + MigrationEdgeKind, plus its test) out of
@prisma-next/migration-tools into cli/src/utils/formatters, trimming the two now
unused package exports. Inline the aggregate->view mapper into the migration list
command and delete migration-space-list-from-aggregate.ts, so no second
representation of migration state lives in the shared package.

Rename appContractShellForAggregateLoad -> appContractStandInFromIdentity with a
cast reason that states read commands consume only storage.storageHash + target
(never models), and move buildReadAggregate control-stack/family bootstrap inside
the Result error boundary so bootstrap failures surface as CliStructuredError.

Fix three F11 -> F14 failure-mode cross-reference links. Records the falsified
assumption + Option-A relocation decision in projects/migration-store.

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

# Conflicts:
#	drive/retro/findings.md
#	packages/1-framework/3-tooling/cli/src/commands/migration-plan.ts
#	packages/1-framework/3-tooling/cli/test/commands/migration-plan-command.test.ts
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.

🧹 Nitpick comments (2)
packages/1-framework/3-tooling/cli/test/commands/migration-plan-command.test.ts (1)

355-373: 💤 Low value

Drop the now-stale command-helpers unmock and comment.

Since this file no longer registers vi.mock('../../src/utils/command-helpers', …), the vi.doUnmock('../../src/utils/command-helpers') at Line 363 is dead, and the comment at Line 358 listing command-helpers.loadMigrationPackages as a leaking mock is now misleading. Removing both keeps the unmock list in sync with the active mocks.

♻️ Proposed cleanup
   // The repo-wide vitest config uses `isolate: false`, so every `vi.mock(...)`
   // registered above leaks into the next test file in the same worker (which
   // breaks anything that does real fs I/O against `node:fs/promises.readFile`,
-  // `command-helpers.loadMigrationPackages`, or `migration-tools/io.writeMigrationPackage`).
+  // `node:fs/promises.readFile` or `migration-tools/io.writeMigrationPackage`).
   // Use `doUnmock` (non-hoisted) here so subsequent files see the real modules.
   afterAll(() => {
     vi.doUnmock('node:fs/promises');
     vi.doUnmock('../../src/config-loader');
-    vi.doUnmock('../../src/utils/command-helpers');
     vi.doUnmock('`@prisma-next/migration-tools/refs`');
🤖 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/cli/test/commands/migration-plan-command.test.ts`
around lines 355 - 373, Remove the stale unmock and related comment: delete the
vi.doUnmock('../../src/utils/command-helpers') call inside the afterAll block
and update the preceding comment that lists leaking mocks so it no longer
references command-helpers.loadMigrationPackages; ensure the afterAll cleanup
now only contains the actual modules that were mocked and still need doUnmock
(keep the other vi.doUnmock(...) and vi.resetModules() calls intact).
packages/1-framework/3-tooling/cli/src/utils/formatters/migration-list-render.ts (1)

16-22: 💤 Low value

Consider consolidating re-exports into an exports/ module.

The re-exports here violate the guideline "Don't reexport from one file in another, except in exports/ folders." While these re-exports appear to have existed prior to this PR (the changes only update their source paths), they should ideally be moved to a dedicated exports/ module if they're intended as a public API surface.

This is out of scope for the current PR (which focuses on import path consolidation), but consider consolidating public type/function exports into cli/src/exports/migration-list-formatters.ts or similar in a follow-up.

As per coding guidelines: "Don't reexport from one file in another, except in exports/ folders."

🤖 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/cli/src/utils/formatters/migration-list-render.ts`
around lines 16 - 22, This file currently re-exports types GlyphMode,
MigrationEdgeKind, MigrationListEntry, MigrationListResult, and
MigrationSpaceListEntry directly; move those re-exports into a dedicated exports
module (e.g., migration-list-formatters) and replace these re-export statements
with imports from that new exports module in any consumers; specifically, create
a new exports file that exports the listed types and update
migration-list-render.ts to import (not re-export) GlyphMode, MigrationEdgeKind,
MigrationListEntry, MigrationListResult, and MigrationSpaceListEntry from that
exports module so public API re-exports live only in the exports folder.
🤖 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.

Nitpick comments:
In
`@packages/1-framework/3-tooling/cli/src/utils/formatters/migration-list-render.ts`:
- Around line 16-22: This file currently re-exports types GlyphMode,
MigrationEdgeKind, MigrationListEntry, MigrationListResult, and
MigrationSpaceListEntry directly; move those re-exports into a dedicated exports
module (e.g., migration-list-formatters) and replace these re-export statements
with imports from that new exports module in any consumers; specifically, create
a new exports file that exports the listed types and update
migration-list-render.ts to import (not re-export) GlyphMode, MigrationEdgeKind,
MigrationListEntry, MigrationListResult, and MigrationSpaceListEntry from that
exports module so public API re-exports live only in the exports folder.

In
`@packages/1-framework/3-tooling/cli/test/commands/migration-plan-command.test.ts`:
- Around line 355-373: Remove the stale unmock and related comment: delete the
vi.doUnmock('../../src/utils/command-helpers') call inside the afterAll block
and update the preceding comment that lists leaking mocks so it no longer
references command-helpers.loadMigrationPackages; ensure the afterAll cleanup
now only contains the actual modules that were mocked and still need doUnmock
(keep the other vi.doUnmock(...) and vi.resetModules() calls intact).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 6220245c-819d-4bac-a0ad-0f8b7fe3dab3

📥 Commits

Reviewing files that changed from the base of the PR and between 955d4df and 14ea728.

⛔ Files ignored due to path filters (4)
  • projects/migration-store/design-decisions.md is excluded by !projects/**
  • projects/migration-store/plan.md is excluded by !projects/**
  • projects/migration-store/slices/adopt-read-commands/spec.md is excluded by !projects/**
  • projects/migration-store/spec.md is excluded by !projects/**
📒 Files selected for processing (24)
  • drive/calibration/dod.md
  • drive/retro/findings.md
  • packages/1-framework/3-tooling/cli/src/commands/migration-list.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-status.ts
  • packages/1-framework/3-tooling/cli/src/utils/contract-space-aggregate-loader.ts
  • packages/1-framework/3-tooling/cli/src/utils/formatters/migration-list-data-column.ts
  • packages/1-framework/3-tooling/cli/src/utils/formatters/migration-list-graph-layout.ts
  • packages/1-framework/3-tooling/cli/src/utils/formatters/migration-list-graph-render.ts
  • packages/1-framework/3-tooling/cli/src/utils/formatters/migration-list-graph-topology.ts
  • packages/1-framework/3-tooling/cli/src/utils/formatters/migration-list-render.ts
  • packages/1-framework/3-tooling/cli/src/utils/formatters/migration-list-types.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-list.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-plan-command.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-tamper.test.ts
  • packages/1-framework/3-tooling/cli/test/utils/build-read-aggregate.test.ts
  • packages/1-framework/3-tooling/cli/test/utils/formatters/migration-list-graph-fixtures.ts
  • packages/1-framework/3-tooling/cli/test/utils/formatters/migration-list-graph-layout.test.ts
  • packages/1-framework/3-tooling/cli/test/utils/formatters/migration-list-graph-topology.test.ts
  • packages/1-framework/3-tooling/cli/test/utils/formatters/migration-list-render.test.ts
  • packages/1-framework/3-tooling/cli/test/utils/formatters/migration-list-styler.test.ts
  • packages/1-framework/3-tooling/migration/package.json
  • packages/1-framework/3-tooling/migration/src/exports/migration-list-graph-topology.ts
  • packages/1-framework/3-tooling/migration/src/exports/migration-list-types.ts
  • packages/1-framework/3-tooling/migration/tsdown.config.ts
💤 Files with no reviewable changes (4)
  • packages/1-framework/3-tooling/migration/src/exports/migration-list-types.ts
  • packages/1-framework/3-tooling/migration/src/exports/migration-list-graph-topology.ts
  • packages/1-framework/3-tooling/migration/tsdown.config.ts
  • packages/1-framework/3-tooling/migration/package.json
✅ Files skipped from review due to trivial changes (6)
  • packages/1-framework/3-tooling/cli/test/commands/migration-tamper.test.ts
  • packages/1-framework/3-tooling/cli/src/utils/formatters/migration-list-graph-layout.ts
  • packages/1-framework/3-tooling/cli/test/utils/formatters/migration-list-graph-topology.test.ts
  • drive/retro/findings.md
  • packages/1-framework/3-tooling/cli/test/utils/formatters/migration-list-styler.test.ts
  • packages/1-framework/3-tooling/cli/src/utils/formatters/migration-list-graph-render.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/1-framework/3-tooling/cli/test/utils/build-read-aggregate.test.ts
  • drive/calibration/dod.md
  • packages/1-framework/3-tooling/cli/src/commands/migration-list.ts

@wmadden wmadden merged commit ccf094b into main May 30, 2026
9 of 10 checks passed
@wmadden wmadden deleted the tml-2716-adopt-read-commands branch May 30, 2026 19:32
wmadden added a commit that referenced this pull request May 30, 2026
Land the mandatory final retro for TML-2709 and delete the transient
project workspace now that both slices (TML-2715 PR #626, TML-2716
PR #644) are merged.

- Document the ContractSpaceAggregate read model (tolerant build /
  lazy query / integrity-as-query split) in the Migration System
  subsystem doc, cross-linked from ADR 212.
- Calibration: add failure-mode F15 (behavioural reports-all/tolerates/
  refuses ACs verified by code-read instead of a populated fixture) and
  a per-package test-invocation note in dod.md.
- Record the close-out retro in drive/retro/findings.md.
- Remove projects/migration-store/.

Signed-off-by: Will Madden <madden@prisma.io>
wmadden added a commit that referenced this pull request May 30, 2026
Land the mandatory final retro for TML-2709 and delete the transient
project workspace now that both slices (TML-2715 PR #626, TML-2716
PR #644) are merged.

- Document the ContractSpaceAggregate read model (tolerant build /
  lazy query / integrity-as-query split) in the Migration System
  subsystem doc, cross-linked from ADR 212.
- Calibration: add failure-mode F15 (behavioural reports-all/tolerates/
  refuses ACs verified by code-read instead of a populated fixture) and
  a per-package test-invocation note in dod.md.
- Record the close-out retro in drive/retro/findings.md.
- Remove projects/migration-store/.

Signed-off-by: Will Madden <madden@prisma.io>
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