TML-2770: migration log reads the ledger as one flat chronological table#704
Conversation
Omitting space returns the whole ledger table from SQL and Mongo adapters. The control client no longer defaults to APP_SPACE_ID when readLedger() is called with no argument. Signed-off-by: Will Madden <madden@prisma.io>
Sorts ledger rows by appliedAt with deterministic tie-breaks, formats local/UTC/ISO timestamps, and renders optional space columns. Signed-off-by: Will Madden <madden@prisma.io>
Drop findPath/on-disk reconstruction. migration log calls readLedger() unscoped, prints a flat chronological table (local time, --utc for human UTC, ISO-UTC JSON array), and shows a space column only when multiple spaces contribute rows. Signed-off-by: Will Madden <madden@prisma.io>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (6)
📝 WalkthroughWalkthroughThis PR makes ChangesLedger-based migration log refactoring
Sequence DiagramsequenceDiagram
participant CLI as Migration Log CLI
participant Client as Control API Client
participant Adapter as Storage Adapter
participant DB as Database
CLI->>Client: executeMigrationLogCommand()
activate Client
Client->>Adapter: readLedger(driver, undefined) // omitted space => all spaces
activate Adapter
Adapter->>DB: Query ledger rows (space filter conditional)
activate DB
DB-->>Adapter: LedgerEntryRecord[]
deactivate DB
Adapter-->>Client: LedgerEntryRecord[]
deactivate Adapter
Client-->>CLI: LedgerEntryRecord[]
deactivate Client
CLI->>CLI: sortLedgerEntries & formatLedgerAppliedAt
CLI->>CLI: renderMigrationLogTable or serializeLedgerEntriesForJson
CLI-->>User: formatted table or JSON
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
@prisma-next/extension-author-tools
@prisma-next/mongo-runtime
@prisma-next/family-mongo
@prisma-next/sql-runtime
@prisma-next/family-sql
@prisma-next/extension-arktype-json
@prisma-next/middleware-cache
@prisma-next/mongo
@prisma-next/extension-paradedb
@prisma-next/extension-pgvector
@prisma-next/extension-postgis
@prisma-next/postgres
@prisma-next/sql-orm-client
@prisma-next/sqlite
@prisma-next/target-mongo
@prisma-next/adapter-mongo
@prisma-next/driver-mongo
@prisma-next/contract
@prisma-next/utils
@prisma-next/config
@prisma-next/errors
@prisma-next/framework-components
@prisma-next/operations
@prisma-next/ts-render
@prisma-next/contract-authoring
@prisma-next/ids
@prisma-next/psl-parser
@prisma-next/psl-printer
@prisma-next/cli
@prisma-next/cli-telemetry
@prisma-next/emitter
@prisma-next/migration-tools
prisma-next
@prisma-next/vite-plugin-contract-emit
@prisma-next/mongo-codec
@prisma-next/mongo-contract
@prisma-next/mongo-value
@prisma-next/mongo-contract-psl
@prisma-next/mongo-contract-ts
@prisma-next/mongo-emitter
@prisma-next/mongo-schema-ir
@prisma-next/mongo-query-ast
@prisma-next/mongo-orm
@prisma-next/mongo-query-builder
@prisma-next/mongo-lowering
@prisma-next/mongo-wire
@prisma-next/sql-contract
@prisma-next/sql-errors
@prisma-next/sql-operations
@prisma-next/sql-schema-ir
@prisma-next/sql-contract-psl
@prisma-next/sql-contract-ts
@prisma-next/sql-contract-emitter
@prisma-next/sql-lane-query-builder
@prisma-next/sql-relational-core
@prisma-next/sql-builder
@prisma-next/target-postgres
@prisma-next/target-sqlite
@prisma-next/adapter-postgres
@prisma-next/adapter-sqlite
@prisma-next/driver-postgres
@prisma-next/driver-sqlite
commit: |
size-limit report 📦
|
…reads Signed-off-by: Will Madden <madden@prisma.io>
Wire MigrationListStyler into the ledger table renderer so migration log human output matches graph/list token colors, with width math on unstyled strings and identity styler as the default for plain output. Signed-off-by: Will Madden <madden@prisma.io>
… node hashes Signed-off-by: Will Madden <madden@prisma.io>
- Drop the clack '│ ' prefix by emitting via ui.output instead of ui.log - Add 'Applied at / Space / Migration / Change / Ops' heading row + dim divider; column widths include heading text - Pull palette back: only the migration name is bold; from→to colors match the graph palette; everything else is bright default-fg Signed-off-by: Will Madden <madden@prisma.io>
Pad each cell with a leading space and reduce inter-column gap from three spaces to one so the table matches the migration list layout density. Signed-off-by: Will Madden <madden@prisma.io>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/1-framework/3-tooling/cli/src/control-api/client.ts (1)
454-458: ⚡ Quick winConsider using
ifDefinedfor conditional property spread.Line 457 uses a ternary to conditionally include the
spaceproperty. Per codebase patterns, preferifDefinedfrom@prisma-next/utils/defined:♻️ Refactor to use ifDefined
+import { ifDefined } from '`@prisma-next/utils/defined`'; + /** Reads the per-migration journal; omit `space` to return every space. */ async readLedger(space?: string): Promise<readonly LedgerEntryRecord[]> { const { driver, familyInstance } = await this.ensureConnected(); - return familyInstance.readLedger(space === undefined ? { driver } : { driver, space }); + return familyInstance.readLedger({ driver, ...ifDefined('space', space) }); }Based on learnings: prefer
ifDefinedfromprisma-next/utils/definedfor conditional object spreads.🤖 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/control-api/client.ts` around lines 454 - 458, The readLedger method should use the ifDefined helper to conditionally spread the space property instead of the ternary; import ifDefined from '`@prisma-next/utils/defined`' and change the call to familyInstance.readLedger to pass an object like { driver, ...ifDefined('space', space) } (or equivalent usage per the helper's API) so the space key is only included when defined; keep the await this.ensureConnected() and the rest of readLedger unchanged.
🤖 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-log-table.ts`:
- Around line 98-100: columnWidth currently uses value.length which
miscalculates visual widths for Unicode; update columnWidth (function
columnWidth) to compute max using a visual width function (e.g., stringWidth)
instead of .length, and ensure the module providing stringWidth is imported
where columnWidth is defined (or call the existing padVisible/ stringWidth
helper if present) so the returned column widths match padVisible's measurements
and fix table alignment.
---
Nitpick comments:
In `@packages/1-framework/3-tooling/cli/src/control-api/client.ts`:
- Around line 454-458: The readLedger method should use the ifDefined helper to
conditionally spread the space property instead of the ternary; import ifDefined
from '`@prisma-next/utils/defined`' and change the call to
familyInstance.readLedger to pass an object like { driver, ...ifDefined('space',
space) } (or equivalent usage per the helper's API) so the space key is only
included when defined; keep the await this.ensureConnected() and the rest of
readLedger 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: 2588eb1c-5179-49c4-aea5-6b7436081e0f
⛔ Files ignored due to path filters (1)
projects/migration-graph-rendering/slices/log-reads-ledger/spec.mdis excluded by!projects/**
📒 Files selected for processing (17)
packages/1-framework/1-core/framework-components/src/control/control-instances.tspackages/1-framework/3-tooling/cli/src/commands/migration-log.tspackages/1-framework/3-tooling/cli/src/control-api/client.tspackages/1-framework/3-tooling/cli/src/utils/formatters/migration-log-table.tspackages/1-framework/3-tooling/cli/test/commands/migration-log.test.tspackages/1-framework/3-tooling/cli/test/commands/read-commands-json-golden.test.tspackages/1-framework/3-tooling/cli/test/control-api/client.test.tspackages/1-framework/3-tooling/cli/test/utils/formatters/migration-log-table.test.tspackages/2-mongo-family/9-family/src/core/control-adapter.tspackages/2-sql/9-family/src/core/control-adapter.tspackages/2-sql/9-family/src/core/control-instance.tspackages/3-mongo-target/2-mongo-adapter/src/core/marker-ledger.tspackages/3-mongo-target/2-mongo-adapter/src/core/mongo-control-adapter.tspackages/3-mongo-target/2-mongo-adapter/test/marker-ledger.test.tspackages/3-targets/6-adapters/postgres/src/core/control-adapter.tspackages/3-targets/6-adapters/sqlite/src/core/control-adapter.tspackages/3-targets/6-adapters/sqlite/test/migrations/runner.ledger.test.ts
Symmetric cell padding: one leading and one trailing space around each value; dividers span the full cell width (maxValueWidth + 2). Signed-off-by: Will Madden <madden@prisma.io>
Use stringWidth in columnWidth so wide migration names line up with padVisible, and spread optional readLedger space via ifDefined. Signed-off-by: Will Madden <madden@prisma.io>
… migration tree ## At a-glance Connected database with one migration still to run: ``` $ prisma-next migration status --no-color app: ○ 3b2d98d (contract) (main) │↑ 20260305_add_avatar 73e3abe → 3b2d98d ⧗ pending ○ 73e3abe (db) │✓ 20260303_add_phone ef9de27 → 73e3abe ✓ applied ○ ef9de27 │✓ 20260301_init ∅ → ef9de27 ✓ applied ○ ∅ 1 pending — run `prisma-next migrate --to 3b2d98d` ``` Hypothetical origin (`--from` drops applied glyphs and the `(db)` marker; pending is still the shortest path from that origin to the target): ``` $ prisma-next migration status --no-color --from ef9de27 --to 3b2d98d app: ○ 3b2d98d (contract) (main) │↑ 20260305_add_avatar 73e3abe → 3b2d98d ⧗ pending ○ 73e3abe │↑ 20260303_add_phone ef9de27 → 73e3abe ⧗ pending ○ ef9de27 │↑ 20260301_init ∅ → ef9de27 ○ ∅ 2 pending — run `prisma-next migrate --to 3b2d98d` ``` *(Representative output — same tree renderer and overlay rules exercised in CLI unit tests and integration journeys.)* ## The decision `migration status` now overlays **applied** and **pending** state on the **shared migration graph tree**, giving you one picture of every on-disk migration's relationship to the database. **Applied** means a ledger row exists for that migration hash (green `✓`). **Pending** means the edge sits on the shortest path from the DB marker (or `--from` origin) to the app contract and is not yet applied (yellow `⧗`). Everything else stays plain — the full on-disk graph, not a pruned subgraph. The old summary-only view and the dagre renderer are deleted; `migration graph` defaults to this tree (the experimental `--tree` flag is gone). ## How we got here ### One tree renderer for the read family `migration graph` already renders contract topology as a condensed annotated tree (`buildMigrationGraphRows` → `buildMigrationGraphLayout` → `renderMigrationGraphTree`). `list` and `status` are meant to draw that same map with different overlays — package facts vs DB state — not maintain parallel layout code. ### `status` used to look different Previously, `migration status` fed a dagre layout and a compact summary that did not match `graph` or `list`. Applied edges were inferred from a graph walk from ∅ to the marker instead of the ledger, which could disagree with what actually ran. ### Overlay on the shared renderer Status computation builds `edgeAnnotationsByHash` (`applied` / `pending`) and passes them into the same tree renderer `graph` uses. The `(db)` marker reuses the existing `dbHash` node overlay. Multi-space projects get one tree section per space (with a `spaceId:` heading when there is more than one), matching `list` / `graph`. ### `--from` for hypotheticals `--from <hash>` overrides the path origin so you can ask offline: "what if my DB were at this contract?" Applied overlays are suppressed (they only make sense against the real DB); pending still follows the shortest path from the hypothetical origin to the target (`--to` or the app contract). ### Dagre removed; tree is the default With `status` on the shared tree, dagre had no remaining consumer. `migration graph` always renders the tree; `--ascii`, `--legend`, `--dot`, and `--json` remain. `@dagrejs/dagre` and its mapper/types/tests are removed. ## What this PR does not change - **`--json` wire shape** — still mirrors `migration list` plus per-migration `status`, with `markerHash` / `targetHash` per space (documented in the slice spec). - **`migration list`** — separate slice ([prisma#706](prisma#706)); this PR only shares the renderer hook `list` will populate later. - **`migration log`** — separate slice ([prisma#704](prisma#704)); flat ledger history, not tree-shaped. ## Linked issue Refs [TML-2748](https://linear.app/prisma-company/issue/TML-2748) (resolves applied-from-ledger [TML-2130](https://linear.app/prisma-company/issue/TML-2130)). ## Testing performed - `pnpm --filter @prisma-next/cli build` - `pnpm --filter @prisma-next/cli typecheck` - `pnpm --filter @prisma-next/cli test` (status overlay, graph default-tree, dagre removal) - `rg 'dagre|graphRenderer|migrationGraphToRenderInput' packages/1-framework/3-tooling/cli/` → empty - `pnpm lint:deps` ## Checklist - [x] All commits are signed off (`git commit -s`) - [x] I read CONTRIBUTING.md and the change is scoped to one logical concern - [x] Tests are updated - [x] PR title is `TML-2748: …` - [x] n/a — internal CLI surface; skill update not required for this slice ## Alternatives considered - **Keep dagre as a `--graph` alternative renderer** — rejected. Two renderers split the family visually; dagre never matched the tree's information density for branching histories. - **Three states (applied / pending / unreachable)** — rejected. Two states cover every real workflow; unreachable edges stay plain on the full list. - **Reimplement layout inside `status`** — rejected. Share `graph` / `list` layout via the existing engine and differ only in overlays. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added applied/pending status indicators to migration visualizations. * **Bug Fixes** * Improved migration status output with per-space reporting and clearer status summaries. * **Chores** * Removed unused dependency. * Simplified migration graph rendering pipeline. * Removed `--tree` CLI option; graph now renders in optimized format by default. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Will Madden <madden@prisma.io>
At a glance
Representative output (multi-space ledger; column alignment from unit-test golden on this branch). At the terminal the divider row is dim; timestamps below use local time with offset.
The decision
migration lognow reads the per-migration ledger journal directly from the connected database and presents it as a single flat chronological table — every applied migration across every space, ordered byappliedAt(oldest first). Human output on a TTY renders local time with a numeric timezone offset;--utcswitches human output to UTC;--json, non-TTY pipes, and other machine paths emit ISO-8601 UTC (…Z) for stable tooling. This replaces the previousmigration logbehaviour wholesale: no on-disk graph, nofindPathreconstruction, no per-space sections.Narrative
Ledger journal (TML-2769, merged)
The on-apply ledger was restructured in #665 into a per-migration journal: one row per applied edge with
space,migrationName,migrationHash,from/to,operationCount, andappliedAt. That shape is whatstatusmatches against and whatlogis meant to surface.migration logas the human-facing journal viewloganswers “what actually ran against this database, and when?” It callsreadLedger()with no space filter so the adapter returns the whole table, sorts globally byappliedAt, and prints aligned rows (Applied at· optionalSpace·Migration·Change·Ops). The ledger is conceptually flat, so the command stays flat too — not space-scoped likelist/graph/status. TheSpacecolumn appears only when more than one space contributes rows. Rollbacks and re-applies are repeated uniform rows;from → to(with∅for a null origin) carries the story without classifying event kinds.Timestamp formatting
TTY humans get local time plus offset (
2026-06-01 10:00:00 +02:00).--utcis human-only and prints UTC with aZsuffix. Machine output (--json, or any non-TTY pipe) is always ISO-8601 UTC regardless of--utc, so scripts never inherit the operator’s timezone.Styling
from → touses the shared migration-list colour palette so hash transitions read the same way as inmigration graphandmigration status. Column layout is a dedicated flat table renderer (not the shared tree).What this PR does not change
readLedgercontract — already landed in TML-2769 (#665); this PR only widens the read to be space-optional and wireslogto it.migration list— separate slice (#706).migration status— separate slice (#705).Alternatives considered
graph/list/status) — rejected. The ledger is a flat table; sectioning would re-group flat data and break global chronological ordering across spaces.ZISO timestamps on--json/ pipes.Linked issue
Refs TML-2770. Builds on ledger journal TML-2769 (#665).
Testing performed
pnpm --filter @prisma-next/cli... buildpnpm --filter @prisma-next/cli typecheckmigration-log-table,migration-log,readLedgerclient, JSON golden testsrunner.ledger, Mongomarker-ledgerpnpm lint:depsSkill update
n/a — internal CLI behaviour change; no published skill documents
migration logtable layout yet.Checklist
git commit -s)TML-2770: …formSummary by CodeRabbit
New Features
Changes