Skip to content

TML-2812: unified migration tree rendering, column alignment, colored lanes, ledger op-count tests#713

Merged
wmadden-electric merged 26 commits into
mainfrom
tml-2812-render-polish-and-ledger-tests
Jun 3, 2026
Merged

TML-2812: unified migration tree rendering, column alignment, colored lanes, ledger op-count tests#713
wmadden-electric merged 26 commits into
mainfrom
tml-2812-render-polish-and-ledger-tests

Conversation

@wmadden

@wmadden wmadden commented Jun 3, 2026

Copy link
Copy Markdown
Contributor
SnapDragon-Cursor-2026-06-03_18-58-43

Linked issue

Refs TML-2812, TML-2811, TML-2773, TML-2774. Closes TML-2767 (superseded — multi-space policy lands here as D16).

At a glance

Same DB, same on-disk migrations — three commands now draw the same tree:

$ prisma-next migration list          $ prisma-next migration status           $ prisma-next migration graph

app:                                  app:                                     app:
  ○ 1375f13              (contract)     ○ 1375f13          (db, contract)        ○ 1375f13              (contract)
  │↑ 20260603…  ∅→1375f13  10 ops       │↑ 20260603…  ∅→1375f13  ✓ applied       │↑ 20260603…  ∅→1375f13  10 ops
  │  ○ f7a8eb5              (prod)       │  ○ f7a8eb5              (prod)         │  ○ f7a8eb5              (prod)
  │  │↑ 20260518…  …  0 ops              │  │↑ 20260518…  …  ⧗ pending           │  │↑ 20260518…  …  0 ops
  …                                    …                                       …

pgvector:                             pgvector:                                pgvector:
  ○ 29059df  (head, db, contract)      ○ 29059df  (head, db, contract)         ○ 29059df  (head, db, contract)
  │↑ …  1 ops  {pgvector:…}            │↑ …  1 ops  {pgvector:…}  ✓ applied    │↑ …  1 ops  {pgvector:…}

Migration data columns align at a fixed offset regardless of branch depth; the gutter is per-column colored (git log --graph style); migration graph --legend prints a stderr key.

Summary

After the read-command-family slices merged (list→tree, status overlay, log→ledger), the three tree commands still diverged in trunk choice, per-row data, and space iteration — plus two presentation gaps (column alignment, colored lanes) and two open ledger test holes from the TML-2769 review. This PR closes all of that in one slice: one shared renderer path with locked rules (live-contract trunk, full per-row data everywhere, all spaces by default), aligned data columns, colored gutter + --legend, and a cross-target op-count parity harness.

What changed

Rendering unification (TML-2812 / D14–D16). New shared module migration-graph-space-render.ts drives list, status, and graph human output through the same buildMigrationGraphRows → layout → tree renderer pipeline. Live-contract hash is the trunk in all three commands; graph iterates every on-disk space (fixes the silent pgvector elision); status composes list overlay (ops, {invariants}) with its applied/pending column instead of replacing it. Parity test: migration-read-commands-parity.test.ts.

Column alignment (TML-2811 / D17). Per-space block, pad the tree prefix to max visible width so dirName starts at the same column on every migration row — branches and rollback peels included.

Colored lanes + legend (TML-2773 / D18–D19). Six locked lane-color rules in migration-graph-lane-colors.ts (column-0 neutral, per-arc single hue, bright arrows, etc.). --legend on migration graph prints glyph + lane-color key to stderr; rejected on --json / --dot.

Ledger test coverage (TML-2774 items 3 + 5 / D20). Integration harness asserts Postgres, SQLite, and Mongo agree on per-edge operationCount for multi-edge and idempotency-skip applies. Postgres op-count-mismatch throw test added (SQLite + Mongo already had it).

Project docs. Combined slice spec at projects/migration-graph-rendering/slices/render-polish-and-ledger-tests/spec.md; supersedes unify-pretty-rendering and lane-colors-and-legend drafts; README updated; TML-2767 marked superseded.

Why

Project decision D1 promised one shared graphical renderer with command-specific annotations only. On disk the commands fed the same renderer different inputs (trunk hash, overlay shape, space list). Fixing that plus the alignment and coloring gaps makes the migration graph the coherent demo surface it was designed to be. The ledger tests pin a cross-target invariant the TML-2769 review left open.

Testing performed

  • @prisma-next/cli: pnpm typecheck pass; pnpm test 1108/1110 (2 pre-existing flakes: db-update-read-aggregate-json-golden, removed-verb-redirects — not touched by this branch)
  • pnpm lint:deps pass
  • Targeted: migration-read-commands-parity, migration-graph-tree-render (alignment + colorized kitchen-sink + legend snapshots), ledger-operation-count-parity integration harness, Postgres ledger integration throw test
  • CI will run full suite on PR open

Skill update

n/a — CLI flags and output formatting only; no new public API surface beyond --legend on an existing command (help text updated).

Checklist

  • All commits signed off (git commit -s)
  • Scoped to one slice (render polish + ledger test coverage)
  • Tests updated
  • Title uses TML-2812 prefix

Notes for the reviewer

  • JSON shapes stay distinct by design (graph {nodes,edges} vs list/status per-space arrays) — only human pretty output converges.
  • Whether migration graph remains a separate top-level command now that pretty output matches list is explicitly deferred.
  • edges-on-plan and empty-origin-as-null ledger cleanups remain deferred (not in TML-2774's open items).

Summary by CodeRabbit

  • New Features

    • Per-space migration tree output via --space with per-space headings, indentation, merged status overlays, and marker-aware list styling; improved lane/color rendering.
  • Documentation

    • Migration graph docs rewritten: tree rendering clarified, legend is human-only and written to stderr, and is suppressed in quiet mode.
  • Bug Fixes

    • CLI validation now rejects --legend when used with machine-readable output.
  • Tests

    • Expanded unit/integration coverage for legend validation, rendering parity, color/legend snapshots, status diagnostics, and ledger operation-count parity.

@wmadden wmadden requested a review from a team as a code owner June 3, 2026 14:20
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

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

Walkthrough

Refactors migration CLI rendering to produce per-space condensed tree outputs, adds a --space flag, centralizes space-tree rendering, implements arc-aware lane coloring, adjusts list/status command result shapes, and expands unit and integration tests for parity and ledger operation counts.

Changes

Migration Graph Space-Scoped Rendering

Layer / File(s) Summary
Legend validation and error handling
packages/1-framework/3-tooling/cli/src/commands/migration-graph.ts, packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts, packages/1-framework/3-tooling/cli/test/commands/migration-graph.test.ts
Extracted validateMigrationGraphLegendOptions to enforce human-only legend rendering and added errorMigrationGraphLegendHumanOnly factory for structured errors when --legend combines with --json/--dot.
Migration graph space-scoped output
packages/1-framework/3-tooling/cli/src/commands/migration-graph.ts
Added --space <id> option, refactored MigrationGraphResult to use treeSections (space id + rendered tree + heading flag) instead of contractHash/refs, and rewrote execution to build app graph, filter spaces, render per-space trees, and conditionally indent multi-space output.
Shared migration graph space tree rendering
packages/1-framework/3-tooling/cli/src/utils/formatters/migration-graph-space-render.ts
New module providing renderMigrationGraphSpaceTree to assemble rows/layout/annotations and delegate to tree renderer, mergeMigrationEdgeAnnotations for combining list and status overlays, and indentMigrationGraphTreeBlock for multi-space formatting.
Arc-aware lane color resolution
packages/1-framework/3-tooling/cli/src/utils/formatters/migration-graph-lane-colors.ts
Introduced NEUTRAL_LANE_COLUMN constant, stylerForLaneColumn helper, and resolveRowArcLaneColors/resolveConnectorLaneColors functions that statefully track arc corners and landing anchors to assign per-cell color columns, enabling consistent hue routing across routed arcs.
Tree rendering refinements
packages/1-framework/3-tooling/cli/src/utils/formatters/migration-graph-tree-render.ts
Switched to arc-aware lane color resolution, reorganized edge annotation formatting into segmented operationCount/invariants rendering, added maxEdgeTreePrefixWidth for edge-row column alignment, refactored legend rendering to conditionally append lane-swatch lines, and updated neutral-lane handling throughout.
Migration list extended rendering
packages/1-framework/3-tooling/cli/src/commands/migration-list.ts, packages/1-framework/3-tooling/cli/src/utils/formatters/migration-list-render.ts
Extended MigrationListExecuteResult to bundle list/aggregate/liveContractHash, added liveContractHash and graphForSpace callback to MigrationListHumanRenderOptions, updated renderSpaceTreeBlock to use shared renderMigrationGraphSpaceTree instead of inline layout construction, and wired aggregate graphs into rendering via lambda.
Migration status refactored to shared renderer
packages/1-framework/3-tooling/cli/src/commands/migration-status.ts
Refactored renderSpaceTree helper to use renderMigrationGraphSpaceTree instead of building rows/layout directly, updated signature to accept liveContractHash/migrations/statusOverlay, simplified target resolution with resolveTarget, and added buildNoPathSummary.
Documentation for --legend stderr behavior
docs/reference/migration-graph-rendering.md
Clarified that --legend output writes to stderr alongside the command header, --legend with --json/--dot is rejected, and --quiet omits the legend along with the header.
Command tests and cross-command parity
packages/1-framework/3-tooling/cli/test/commands/*.test.ts
Added legend validation unit tests, updated migration-list JSON golden expectations for new result shape, created migration-read-commands-parity.test.ts asserting byte-equivalent output across list/graph/status after normalization, and extended migration-status tests with annotation merging verification.
Tree rendering tests with alignment and colorization
packages/1-framework/3-tooling/cli/test/utils/formatters/migration-graph-tree-render.test.ts
Added dirName column alignment assertions, updated snapshot expectations for init/connector rows across topologies, expanded legend tests for colorization with lane-swatch coverage, added kitchen-sink multi-fixture snapshot, and verified status overlay ordering.
Ledger operation count parity fixtures and integration
packages/1-framework/3-tooling/migration/test/fixtures/ledger-operation-count-parity.ts, test/integration/test/migration-ledger/ledger-operation-count-parity.test.ts, packages/3-targets/6-adapters/postgres/test/migrations/runner.ledger.integration.test.ts, test/integration/package.json
Added operation count extraction/validation fixtures, Postgres runner test for operationCount sum matching, three-scenario integration test suite (multi-edge apply, idempotency skip, synth apply) validating ledger parity across Postgres/SQLite/Mongo, and updated dependencies with SQLite packages.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • prisma/prisma-next#674: Both PRs modify the migration graph legend/lane-color rendering pipeline—e.g., migration-graph.ts legend handling/validation and the shared legend + lane-color formatter modules (migration-graph-lane-colors.ts, migration-graph-tree-render.ts).
  • prisma/prisma-next#702: Both PRs modify the migration graph tree rendering pipeline—specifically migration-graph-tree-render.ts and its exported connector/lane color resolution via resolveConnectorLaneColors (and related arc/tee landing glyph handling).
  • prisma/prisma-next#644: The main PR’s changes to the migration-graph/migration-list commands (new per-space rendering, result shape, and legend validation) build directly on the retrieved PR’s refactor of those commands to route reads through buildReadAggregate/ContractSpaceAggregate.

Suggested reviewers

  • aqrln

Poem

A rabbit hops through migration trees,
Scopes each space with gentle ease,
Arcs get colored, legends sing,
Tests nod in parity—what a spring! 🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.91% 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
Title check ✅ Passed The title accurately describes the main change: unified migration tree rendering across three commands with column alignment, colored lanes, and new ledger operation-count tests.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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-2812-render-polish-and-ledger-tests

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

@pkg-pr-new

pkg-pr-new Bot commented Jun 3, 2026

Copy link
Copy Markdown

Open in StackBlitz

@prisma-next/extension-author-tools

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

@prisma-next/mongo-runtime

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

@prisma-next/family-mongo

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

@prisma-next/sql-runtime

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

@prisma-next/family-sql

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

@prisma-next/extension-arktype-json

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

@prisma-next/middleware-cache

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

@prisma-next/mongo

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

@prisma-next/extension-paradedb

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

@prisma-next/extension-pgvector

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

@prisma-next/extension-postgis

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

@prisma-next/postgres

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

@prisma-next/sql-orm-client

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

@prisma-next/sqlite

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

@prisma-next/target-mongo

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

@prisma-next/adapter-mongo

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

@prisma-next/driver-mongo

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

@prisma-next/contract

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

@prisma-next/utils

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

@prisma-next/config

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

@prisma-next/errors

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

@prisma-next/framework-components

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

@prisma-next/operations

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

@prisma-next/ts-render

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

@prisma-next/contract-authoring

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

@prisma-next/ids

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

@prisma-next/psl-parser

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

@prisma-next/psl-printer

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

@prisma-next/cli

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

@prisma-next/cli-telemetry

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

@prisma-next/emitter

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

@prisma-next/migration-tools

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

prisma-next

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

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

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

@prisma-next/mongo-codec

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

@prisma-next/mongo-contract

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

@prisma-next/mongo-value

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

@prisma-next/mongo-contract-psl

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

@prisma-next/mongo-contract-ts

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

@prisma-next/mongo-emitter

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

@prisma-next/mongo-schema-ir

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

@prisma-next/mongo-query-ast

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

@prisma-next/mongo-orm

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

@prisma-next/mongo-query-builder

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

@prisma-next/mongo-lowering

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

@prisma-next/mongo-wire

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

@prisma-next/sql-contract

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

@prisma-next/sql-errors

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

@prisma-next/sql-operations

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

@prisma-next/sql-schema-ir

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

@prisma-next/sql-contract-psl

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

@prisma-next/sql-contract-ts

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

@prisma-next/sql-contract-emitter

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

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

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

@prisma-next/sql-relational-core

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

@prisma-next/sql-builder

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

@prisma-next/target-postgres

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

@prisma-next/target-sqlite

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

@prisma-next/adapter-postgres

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

@prisma-next/adapter-sqlite

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

@prisma-next/driver-postgres

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

@prisma-next/driver-sqlite

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

commit: f02610d

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

size-limit report 📦

Path Size
postgres / no-emit 137.37 KB (0%)
postgres / emit 108.83 KB (0%)
mongo / no-emit 75.96 KB (0%)
mongo / emit 70.78 KB (0%)
cf-worker / no-emit 166.35 KB (0%)
cf-worker / emit 134.51 KB (0%)

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts (1)

111-120: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Move the displaced errorInvalidSpaceId JSDoc back above its function.

Lines 111-116 now describe errorInvalidSpaceId, but the next symbol is errorMigrationGraphLegendHumanOnly. That leaves the space-id contract orphaned and makes the hover/docs here misleading. Move that block down above Line 134, or delete it if the comment is no longer needed.

🤖 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/cli-errors.ts` around lines 111
- 120, The JSDoc block describing the space-id contract currently sits above
errorMigrationGraphLegendHumanOnly and should be moved so it documents
errorInvalidSpaceId instead; locate the errorInvalidSpaceId function and place
the displaced comment block immediately above it (or remove the comment entirely
if it's no longer relevant), ensuring the JSDoc text matches the symbol
errorInvalidSpaceId so hover/docs show the correct description.
docs/reference/migration-graph-rendering.md (1)

1-7: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove the stale --tree framing from this reference.

This page still documents migration graph --tree as a distinct mode and says plain migration graph uses the dagre renderer, but createMigrationGraphCommand() no longer defines a --tree flag and the human path always renders the tree unless --json or --dot is selected. As written, the reference tells users to use a mode that no longer exists and describes the default behavior backwards.

As per coding guidelines, "Keep docs current (READMEs, rules, links)."

Also applies to: 11-21, 156-183, 213-218

🤖 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 `@docs/reference/migration-graph-rendering.md` around lines 1 - 7, The docs
incorrectly describe a non-existent "migration graph --tree" flag and state that
plain "migration graph" uses the dagre renderer; update the document so it
removes the stale `--tree` framing and instead documents the current behavior:
the human renderer outputs the tree by default and
`createMigrationGraphCommand()` no longer defines a `--tree` flag, with only
`--json` and `--dot` as alternate outputs; revise all affected sections
(including the intro and the sections called out) to reflect that default tree
rendering, adjust examples and terminology to drop `--tree`, and ensure
references to `--json`/`--dot` and the tree rendering are accurate and
consistent.
🧹 Nitpick comments (2)
packages/1-framework/3-tooling/migration/test/fixtures/ledger-operation-count-parity.ts (1)

25-49: ⚡ Quick win

Derive buildMultiEdgeRefs() from MULTI_EDGE_OPERATION_COUNTS.

This helper hardcodes 1, 2, 1 a second time, so the exported expected vector and the built edge refs can drift independently. Building the refs from MULTI_EDGE_OPERATION_COUNTS keeps the fixture internally consistent.

♻️ Suggested refactor
 export function buildMultiEdgeRefs(destHash: string): readonly AggregateMigrationEdgeRef[] {
   const hashA = 'sha256:parity-mid-a';
   const hashB = 'sha256:parity-mid-b';
   return [
     {
       migrationHash: 'sha256:parity-mig-a',
       dirName: '001_a',
       from: EMPTY_CONTRACT_HASH,
       to: hashA,
-      operationCount: 1,
+      operationCount: MULTI_EDGE_OPERATION_COUNTS[0],
     },
     {
       migrationHash: 'sha256:parity-mig-b',
       dirName: '002_b',
       from: hashA,
       to: hashB,
-      operationCount: 2,
+      operationCount: MULTI_EDGE_OPERATION_COUNTS[1],
     },
     {
       migrationHash: 'sha256:parity-mig-c',
       dirName: '003_c',
       from: hashB,
       to: destHash,
-      operationCount: 1,
+      operationCount: MULTI_EDGE_OPERATION_COUNTS[2],
     },
   ];
 }
🤖 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/test/fixtures/ledger-operation-count-parity.ts`
around lines 25 - 49, The buildMultiEdgeRefs function currently hardcodes
operation counts (1,2,1) which duplicates MULTI_EDGE_OPERATION_COUNTS and can
drift; change buildMultiEdgeRefs to derive its operationCount values from the
MULTI_EDGE_OPERATION_COUNTS constant (e.g., iterate over
MULTI_EDGE_OPERATION_COUNTS or destructure it) when constructing each
AggregateMigrationEdgeRef so the fixture stays consistent with the exported
expected vector; update the entries for migrationHash/dirName/from/to to set
operationCount = MULTI_EDGE_OPERATION_COUNTS[index] (using the same index order
as the exported vector).
packages/3-targets/6-adapters/postgres/test/migrations/runner.ledger.integration.test.ts (1)

178-228: ⚡ Quick win

Also assert that the failed run leaves the ledger empty.

This test locks down the thrown invariant, but not the side effect it is protecting. Adding a readLedger/readLedgerRows assertion after the rejection would catch regressions where rows are partially written before the error is surfaced.

✅ Suggested assertion
     await expect(
       runner.execute({
         driver: driver!,
         perSpaceOptions: [
           {
@@
         ],
       }),
     ).rejects.toThrow(/does not match sum of migrationEdges operationCount/);
+
+    expect(await readLedgerRows(driver!)).toEqual([]);
   });
🤖 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/3-targets/6-adapters/postgres/test/migrations/runner.ledger.integration.test.ts`
around lines 178 - 228, After the existing expect(...).rejects.toThrow(...) add
an assertion that the ledger remained empty by calling the ledger read helper
(e.g., readLedger or readLedgerRows) with the test driver and
LEDGER_TEST_SPACE_ID and asserting it returns no rows; this should run after the
rejected runner.execute call (references: runner.execute, driver,
LEDGER_TEST_SPACE_ID, readLedger/readLedgerRows).
🤖 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/formatters/migration-list-render.ts`:
- Around line 143-151: The change bypasses MigrationListStyler for non-empty
tree rows by calling renderMigrationGraphSpaceTree with only graph/migrations;
restore usage of the styler by passing the active MigrationListStyler (or its
computed style object) into renderMigrationGraphSpaceTree so that style.kind,
style.dirName, style.sourceHash, style.destHash, style.glyph, style.lane,
style.invariants and style.refs are applied per-row; update
renderMigrationListWithStyle to forward the styler (or apply it to the tree rows
before rendering), and ensure buildRefsByHashFromListEntries remains used for
refs resolution.

In
`@packages/1-framework/3-tooling/cli/test/commands/migration-read-commands-parity.test.ts`:
- Around line 253-260: The test hard-codes HASH_804e018 as targetHash (which is
the app-space tip) so the postgis branch doesn't exercise real per-space target
resolution; replace that hard-coded use of HASH_804e018 with a target derived
from the current test space before calling deriveStatusEdgeAnnotations (i.e.,
compute targetHash from the graph/space tip used in this fixture instead of
using HASH_804e018). Locate the test that calls deriveStatusEdgeAnnotations and
set targetHash by calling the same helper or graph API used elsewhere in the
suite to get the tip for the current space (the per-space tip resolver used in
migration status), then pass that derived targetHash into
deriveStatusEdgeAnnotations.

---

Outside diff comments:
In `@docs/reference/migration-graph-rendering.md`:
- Around line 1-7: The docs incorrectly describe a non-existent "migration graph
--tree" flag and state that plain "migration graph" uses the dagre renderer;
update the document so it removes the stale `--tree` framing and instead
documents the current behavior: the human renderer outputs the tree by default
and `createMigrationGraphCommand()` no longer defines a `--tree` flag, with only
`--json` and `--dot` as alternate outputs; revise all affected sections
(including the intro and the sections called out) to reflect that default tree
rendering, adjust examples and terminology to drop `--tree`, and ensure
references to `--json`/`--dot` and the tree rendering are accurate and
consistent.

In `@packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts`:
- Around line 111-120: The JSDoc block describing the space-id contract
currently sits above errorMigrationGraphLegendHumanOnly and should be moved so
it documents errorInvalidSpaceId instead; locate the errorInvalidSpaceId
function and place the displaced comment block immediately above it (or remove
the comment entirely if it's no longer relevant), ensuring the JSDoc text
matches the symbol errorInvalidSpaceId so hover/docs show the correct
description.

---

Nitpick comments:
In
`@packages/1-framework/3-tooling/migration/test/fixtures/ledger-operation-count-parity.ts`:
- Around line 25-49: The buildMultiEdgeRefs function currently hardcodes
operation counts (1,2,1) which duplicates MULTI_EDGE_OPERATION_COUNTS and can
drift; change buildMultiEdgeRefs to derive its operationCount values from the
MULTI_EDGE_OPERATION_COUNTS constant (e.g., iterate over
MULTI_EDGE_OPERATION_COUNTS or destructure it) when constructing each
AggregateMigrationEdgeRef so the fixture stays consistent with the exported
expected vector; update the entries for migrationHash/dirName/from/to to set
operationCount = MULTI_EDGE_OPERATION_COUNTS[index] (using the same index order
as the exported vector).

In
`@packages/3-targets/6-adapters/postgres/test/migrations/runner.ledger.integration.test.ts`:
- Around line 178-228: After the existing expect(...).rejects.toThrow(...) add
an assertion that the ledger remained empty by calling the ledger read helper
(e.g., readLedger or readLedgerRows) with the test driver and
LEDGER_TEST_SPACE_ID and asserting it returns no rows; this should run after the
rejected runner.execute call (references: runner.execute, driver,
LEDGER_TEST_SPACE_ID, readLedger/readLedgerRows).
🪄 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: 62ff4887-c34c-406e-915f-1f485c1c69a5

📥 Commits

Reviewing files that changed from the base of the PR and between e4b7a7c and 2a96879.

⛔ Files ignored due to path filters (5)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • projects/migration-graph-rendering/README.md is excluded by !projects/**
  • projects/migration-graph-rendering/slices/lane-colors-and-legend/spec.md is excluded by !projects/**
  • projects/migration-graph-rendering/slices/render-polish-and-ledger-tests/spec.md is excluded by !projects/**
  • projects/migration-graph-rendering/slices/unify-pretty-rendering/spec.md is excluded by !projects/**
📒 Files selected for processing (19)
  • docs/reference/migration-graph-rendering.md
  • 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-status.ts
  • packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts
  • packages/1-framework/3-tooling/cli/src/utils/formatters/migration-graph-lane-colors.ts
  • packages/1-framework/3-tooling/cli/src/utils/formatters/migration-graph-space-render.ts
  • packages/1-framework/3-tooling/cli/src/utils/formatters/migration-graph-tree-render.ts
  • packages/1-framework/3-tooling/cli/src/utils/formatters/migration-list-render.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-graph.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-read-commands-parity.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-status-overlay.test.ts
  • packages/1-framework/3-tooling/cli/test/utils/formatters/migration-graph-tree-render.test.ts
  • packages/1-framework/3-tooling/migration/test/fixtures/ledger-operation-count-parity.ts
  • packages/3-targets/6-adapters/postgres/test/migrations/runner.ledger.integration.test.ts
  • test/integration/package.json
  • test/integration/test/migration-ledger/ledger-operation-count-parity.test.ts

wmadden-electric pushed a commit that referenced this pull request Jun 3, 2026
Pass MigrationListStyler through the shared tree renderer, fix integration
parity test types (mongo family + migrationEdges), derive multi-edge fixture
counts from one source, assert empty ledger on operationCount mismatch,
use per-space target hash in status parity test, and refresh the graph
rendering reference doc for the unified tree default.

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

@coderabbitai coderabbitai Bot left a comment

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/1-framework/3-tooling/cli/src/commands/migration-status.ts (2)

490-493: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pass the current space’s contract hash into the shared tree renderer.

renderMigrationGraphSpaceTree() renders against member.graph(), but this call still feeds it the app-level contractHash. In multi-space repos that will mark the wrong live node/trunk for every non-app space, because spaceContractHash is already computed just above for the graph you’re rendering.

Suggested fix
     const tree = renderSpaceTree({
       member,
-      liveContractHash: contractHash,
+      liveContractHash: spaceContractHash,
       migrations: spaceEntry.migrations,
       markerHash,
       showDbMarker,
       statusOverlay: annotations,
🤖 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/commands/migration-status.ts` around
lines 490 - 493, The call to renderSpaceTree for a space is using the app-level
contractHash (contractHash) but should use the space-specific hash
(spaceContractHash); update the renderSpaceTree invocation (the one building
tree for spaceEntry/migrations) to pass liveContractHash: spaceContractHash so
renderMigrationGraphSpaceTree which renders against member.graph() highlights
the correct live node/trunk for that space.

455-464: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep markerCannotReachTarget scoped to the app space.

The final summary always talks about appMarkerHash/headlineTargetHash, but this flag is currently set by any space. In a multi-space aggregate, one disconnected secondary space can therefore flip the top-level summary to “No migration path …” even when the app space is reachable.

Suggested fix
-    if (
+    if (
+      spaceEntry.spaceId === aggregate.app.spaceId &&
       connected &&
       !usingFromOverride &&
       markerHash !== undefined &&
       markerInGraph &&
       markerHash !== targetHash &&
🤖 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/commands/migration-status.ts` around
lines 455 - 464, The flag markerCannotReachTarget is being set for any space but
should only reflect reachability for the app space; update the condition that
sets markerCannotReachTarget (the if that currently checks connected,
!usingFromOverride, markerHash, markerInGraph, markerHash !== targetHash, and
findPath(...)=null) to also require that this evaluation is for the app space —
e.g. add a check like space === appSpace or markerHash ===
appMarkerHash/headlineTargetHash (whichever identifies the app space in this
scope) so only the app's disconnected state flips markerCannotReachTarget.
🤖 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.

Outside diff comments:
In `@packages/1-framework/3-tooling/cli/src/commands/migration-status.ts`:
- Around line 490-493: The call to renderSpaceTree for a space is using the
app-level contractHash (contractHash) but should use the space-specific hash
(spaceContractHash); update the renderSpaceTree invocation (the one building
tree for spaceEntry/migrations) to pass liveContractHash: spaceContractHash so
renderMigrationGraphSpaceTree which renders against member.graph() highlights
the correct live node/trunk for that space.
- Around line 455-464: The flag markerCannotReachTarget is being set for any
space but should only reflect reachability for the app space; update the
condition that sets markerCannotReachTarget (the if that currently checks
connected, !usingFromOverride, markerHash, markerInGraph, markerHash !==
targetHash, and findPath(...)=null) to also require that this evaluation is for
the app space — e.g. add a check like space === appSpace or markerHash ===
appMarkerHash/headlineTargetHash (whichever identifies the app space in this
scope) so only the app's disconnected state flips markerCannotReachTarget.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: a7ece171-95b8-4ee2-9a3e-4b3fb7643bf8

📥 Commits

Reviewing files that changed from the base of the PR and between 83f8168 and daefbc5.

⛔ Files ignored due to path filters (1)
  • projects/migration-graph-rendering/slices/render-polish-and-ledger-tests/spec.md is excluded by !projects/**
📒 Files selected for processing (3)
  • packages/1-framework/3-tooling/cli/src/commands/migration-status.ts
  • packages/1-framework/3-tooling/cli/test/commands/format-status-summary.test.ts
  • test/integration/test/cli-journeys/migration-status-diagnostics.e2e.test.ts

@wmadden-electric wmadden-electric left a comment

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.

LGTM — render-polish-and-ledger-tests slice (D1–D12). Trunk choice now anchors on the live contract; cross-space columns align; --legend is universal across list/graph/status with the bracket-convention key.

wmadden added 22 commits June 3, 2026 19:00
… spec + plan

Single combined slice covering TML-2812 (unify pretty rendering across
list / status / graph), TML-2811 (column alignment), TML-2773 (colored
lanes + --legend), and TML-2774 (the two open op-count test-coverage
items). Subsumes the unify-pretty-rendering and lane-colors-and-legend
slice drafts. TML-2767 is superseded by D16 and closed.

Five dispatches, all decisions locked (D14-D20). The deferred
edges-on-plan and empty-origin-as-null drafts stay deferred per
operator (the empty-origin work has non-trivial blast radius into
the MigrationGraph node-keying and was already ruled out of scope
in the TML-2769 review).

Signed-off-by: Will Madden <madden@prisma.io>
Route list, status, and graph through the shared space-tree renderer with
live-contract trunk choice, list-derived edge annotations, and all-space
iteration (including --space on graph). Status composes the status overlay
onto the list overlay. Add cross-command parity tests.

Signed-off-by: Will Madden <madden@prisma.io>
Right-pad each migration row's tree prefix to the widest prefix in the
render block so dirName and hash columns line up on branched graphs.

Signed-off-by: Will Madden <madden@prisma.io>
Add integration harness asserting per-edge LedgerEntryRecord.operationCount
matches across Postgres, SQLite, Mongo, and synth edge metadata; add the
Postgres runner guard test for migrationEdges operationCount sum mismatch.

Signed-off-by: Will Madden <madden@prisma.io>
Centralize arc and connector colour resolution in migration-graph-lane-colors
for legend reuse, keep crossings dim while dashes take the served lane hue,
and add a kitchen-sink colorized test over diamond, back-arc, fan, and sink
fixtures asserting all six lane-color rules.

Signed-off-by: Will Madden <madden@prisma.io>
Print a palette- and color-aware legend to stderr when --legend is set,
including gutter lane swatches from LANE_COLOR_CYCLE when colorize is on.
Reject --legend combined with --json or --dot. Snapshot the four
unicode/ascii × color on/off legend variants and document the flag.

Signed-off-by: Will Madden <madden@prisma.io>
Pass MigrationListStyler through the shared tree renderer, fix integration
parity test types (mongo family + migrationEdges), derive multi-edge fixture
counts from one source, assert empty ledger on operationCount mismatch,
use per-space target hash in status parity test, and refresh the graph
rendering reference doc for the unified tree default.

Signed-off-by: Will Madden <madden@prisma.io>
…get + no-path wording (in-scope D6)

Signed-off-by: Will Madden <madden@prisma.io>
…h ?? contractHash`

Signed-off-by: Will Madden <madden@prisma.io>
…mary (D21)

Collapse target resolution to activeRefHash ?? contractHash, remove the
unreachable MIGRATION.DIVERGED path, and replace the no-path headline with
context-aware buildNoPathSummary wording.

Signed-off-by: Will Madden <madden@prisma.io>
…n `()`, fresh-DB no-path guard

Signed-off-by: Will Madden <madden@prisma.io>
…DB no-path guard (D22)

Signed-off-by: Will Madden <madden@prisma.io>
Signed-off-by: Will Madden <madden@prisma.io>
…t-node refs adjacent to hash, right-justified from-hash

Signed-off-by: Will Madden <madden@prisma.io>
…cent to hash; right-justify ∅ (D23)

Compute a single global maxEdgeTreePrefixWidth across all per-space blocks
so dirName columns align in multi-space renderings; place contract-node
markers/refs two spaces after the hash; right-justify the from-hash column
so ∅ sits flush against →.

Signed-off-by: Will Madden <madden@prisma.io>
…+ capitalised "Up to date"

Signed-off-by: Will Madden <madden@prisma.io>
Pad migration dirName to a global max across per-space blocks so the
from-hash column and arrow align with D8 tree-prefix alignment. Capitalise
the no-pending status headline.

Signed-off-by: Will Madden <madden@prisma.io>
…actHash for connected components

Signed-off-by: Will Madden <madden@prisma.io>
…ch list/graph (D1 parity)

Signed-off-by: Will Madden <madden@prisma.io>
Bias row-model layering toward the live-contract leaf and reorder sibling
forward edges so the layout module visits that chain first. Covers the demo
two-leaf-from-∅ case without touching migration-graph-layout.ts.

Signed-off-by: Will Madden <madden@prisma.io>
… convention + universal --legend

Signed-off-by: Will Madden <madden@prisma.io>
Replace the collapsed marker/ref legend line with two D22-aligned example
lines, hoist legend gating helpers into utils/legend.ts, and wire --legend
into migration list and migration status with the renamed human-only error.

Signed-off-by: Will Madden <madden@prisma.io>
wmadden added 3 commits June 3, 2026 19:00
…us commands

Signed-off-by: Will Madden <madden@prisma.io>
Signed-off-by: Will Madden <madden@prisma.io>
vi.mock on config-loader stops applying after other suites call
vi.doUnmock with isolate disabled; migration-graph then hits real
loadConfig with a contract.json path and fails validation. Spy on
the real export survives that ordering.

Signed-off-by: Will Madden <madden@prisma.io>
@wmadden-electric wmadden-electric force-pushed the tml-2812-render-polish-and-ledger-tests branch from 0b3ee14 to ef94189 Compare June 3, 2026 17:01
Cast the graph JSON golden loadConfig stub to LoadedConfig and type
aggregatePlannerWarnings against OrderedResolution so PerSpacePlan
warnings are visible after the control-policy merge.

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

Copy link
Copy Markdown
Contributor

Babysit: merge-ready (CI green, no conflicts, no open review threads)

Fixes pushed on this pass:

  • test(cli): stub loadConfig via spy in graph JSON goldenvi.mock on config-loader was dropped after earlier suites call vi.doUnmock with isolate: false, so migration graph --json hit real loadConfig with a contract.json path and failed validation in CI.
  • fix(cli): typecheck after rebase onto main — rebased onto main; typed the golden stub and aggregatePlannerWarnings against OrderedResolution after the control-policy merge.

Branch is rebased on current main (mergeStateStatus: CLEAN). Review is approved. Ready for operator merge when you are — not merging from automation.

@wmadden-electric wmadden-electric merged commit 2135e9f into main Jun 3, 2026
21 checks passed
@wmadden-electric wmadden-electric deleted the tml-2812-render-polish-and-ledger-tests branch June 3, 2026 17:25
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