Skip to content

Rename migration verify -> migration emit#353

Merged
wmadden merged 8 commits intomainfrom
tml-2219-a-cli-emit-unification
Apr 19, 2026
Merged

Rename migration verify -> migration emit#353
wmadden merged 8 commits intomainfrom
tml-2219-a-cli-emit-unification

Conversation

@wmadden
Copy link
Copy Markdown
Contributor

@wmadden wmadden commented Apr 19, 2026

Stack

Summary

migration verify becomes migration emit and now does the actual work
(in-process dynamic-import the migration.ts file, run the emit helper,
write attested ops.json) rather than just validating an external emit
step. migration plan always runs emit inline as part of plan, dropping
the needsDataMigration branch the descriptor planner used to expose.

Adds the emit capability to TargetMigrationsCapability and extracts an
emitMigration helper that centrally owns attestation. The mongo family
plugs in via mongoEmit, which dynamic-imports the class-flow
migration.ts and writes ops.json.

This PR is structural cleanup that unblocks the class-flow scaffolding
work in PR B and the data transforms work in PR C. No new authoring
strategies or new operation kinds.

Structured error codes

Code Helper
PN-MIG-2002 errorMigrationFileMissing
PN-MIG-2003 errorMigrationInvalidDefaultExport
PN-MIG-2004 errorMigrationPlanNotArray

Migration identity

migrationId is now storage-only (a content hash of operations); the framework
attestMigration helper is the single owner.

Known caveat: Migration.run still writes drafts at this PR's tip (fixed in PR B)

ADR 196 (added in PR D and present here) describes Migration.run as the
canonical self-emitting path that produces an attested migration.json
byte-identical to what the CLI emit path produces. At PR A's tip, that
is aspirational.
The current migration-base.ts implementation:

  • writes migration.json with migrationId: null (a draft), not an
    attested manifest;
  • does not call computeMigrationId or attestMigration;
  • does not preserve a pre-existing migration.json's contract bookends,
    hints, or labels (it overwrites with a fresh draft envelope built from
    describe()).

So in PR A, the CLI emit path (the emitMigration helper) is the only
path that attests; the shebang/node migration.ts path leaves a draft.
The "single source of truth for migrationId" invariant the JSDoc on
emit? advertises is correct in code today because Migration.run
doesn't attest yet — not because the design enforces single ownership.

PR B (class-flow scaffolding spine) brings Migration.run up to the
ADR-described behavior: it will read any pre-existing manifest, compute
migrationId in-process via computeMigrationId, and write an attested
migration.json. At that point both emit paths converge on byte-identical
artifacts and the ADR's "no draft state ever reaches disk" invariant
holds end-to-end.

ADR 196 is being landed in PR A so the design intent is on disk while
the implementation catches up across A → B. Reviewers should read the
ADR as the target state, not as a description of A's tip.

Test plan

  • CI green
  • migration emit and migration plan exercised via CLI command tests
  • Integration journey tests updated from verify to emit terminology

Reviewer note

Base of this PR is PR D (tml-2219-d-adrs-and-subsystem-docs), not
main. GitHub will retarget to main automatically once D merges.

Summary by CodeRabbit

  • New Features

    • New prisma-next migration emit command for evaluating, emitting ops.json and attesting migrations; migration authoring now accepts class or factory default-exports that expose plan().
  • Documentation

    • Updated architecture and CLI docs/help to describe the emit workflow and revised command names/links.
  • Refactor

    • Migration identity now computed from manifest content and ops (contract/hints excluded).
  • Bug Fixes / UX

    • Improved migration error reporting with new MIG-domain codes and clearer guidance.
  • Tests

    • Added/updated end-to-end and unit tests covering emit flow and migration error cases.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: f3d18952-572f-4750-b4c5-a0f904102c81

📥 Commits

Reviewing files that changed from the base of the PR and between 75531bc and 4fffb38.

📒 Files selected for processing (5)
  • docs/architecture docs/adrs/ADR 196 - In-process emit for class-flow targets.md
  • docs/architecture docs/subsystems/7. Migration System.md
  • packages/1-framework/3-tooling/cli/src/commands/migration-apply.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-new.ts
  • packages/1-framework/3-tooling/migration/src/migration-ts.ts
✅ Files skipped from review due to trivial changes (4)
  • packages/1-framework/3-tooling/migration/src/migration-ts.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-apply.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-new.ts
  • docs/architecture docs/subsystems/7. Migration System.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/architecture docs/adrs/ADR 196 - In-process emit for class-flow targets.md

📝 Walkthrough

Walkthrough

The PR replaces the "migration verify" attestation workflow with a new "migration emit" command that executes migration.ts in-process to generate and persist operations. Two default-export shapes are supported (Migration subclasses and factory functions returning objects with plan()); new MIG-domain error codes and target emit capability were added.

Changes

Cohort / File(s) Summary
Error System Expansion
packages/1-framework/1-core/errors/src/migration.ts, packages/1-framework/1-core/errors/src/exports/migration.ts, packages/1-framework/1-core/errors/test/migration.test.ts, packages/1-framework/1-core/errors/src/control.ts, packages/1-framework/1-core/errors/package.json, packages/1-framework/1-core/errors/tsdown.config.ts
Added MIG domain to CliStructuredError; new migration error factories for missing file, invalid default export, and non-array plan() results; added public export subpath @prisma-next/errors/migration.
CLI Command Replacement
packages/1-framework/3-tooling/cli/src/commands/migration-emit.ts, packages/1-framework/3-tooling/cli/src/commands/migration-verify.ts, packages/1-framework/3-tooling/cli/src/cli.ts, packages/1-framework/3-tooling/cli/package.json, packages/1-framework/3-tooling/cli/README.md
Removed migration-verify command; introduced migration-emit command and interfaces; updated CLI wiring, help text, and package exports to use migration-emit.
Migration Emission Infrastructure
packages/1-framework/3-tooling/cli/src/lib/migration-emit.ts, packages/1-framework/3-tooling/cli/test/lib/migration-emit.test.ts
New emitMigration() helper that dispatches between descriptor-flow (resolveDescriptors) and class/emit-flow (migrations.emit), validates migration.ts presence, writes ops, and attests.
Target Capabilities & Types
packages/1-framework/1-core/framework-components/src/control-migration-types.ts
Added optional emit() capability to TargetMigrationsCapability and removed needsDataMigration from planWithDescriptors success result; updated docs to indicate emit-time decisions.
CLI Command Integrations
packages/1-framework/3-tooling/cli/src/commands/migration-apply.ts, packages/1-framework/3-tooling/cli/src/commands/migration-new.ts, packages/1-framework/3-tooling/cli/src/commands/migration-plan.ts, packages/1-framework/3-tooling/cli/src/commands/migration-show.ts, packages/1-framework/3-tooling/cli/src/commands/migration-status.ts
Replaced user-facing guidance from migration verifymigration emit; migration-plan refactored to call emitMigration() after scaffold instead of descriptor-only flow.
CLI Formatters & Utilities
packages/1-framework/3-tooling/cli/src/utils/formatters/migrations.ts, packages/1-framework/3-tooling/cli/src/utils/formatters/help.ts, packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts
Replaced verify formatter/types with emit-specific ones; updated help link mapping; re-exported migration error factories for CLI consumption.
Attestation & Identity Logic
packages/1-framework/3-tooling/migration/src/attestation.ts, packages/1-framework/3-tooling/migration/test/attestation.test.ts, packages/1-framework/3-tooling/migration/src/migration-ts.ts
computeMigrationId now excludes fromContract/toContract and hints from hashed inputs — identity is content-addressed on manifest envelope (excluding contracts/hints) and ops only; tests updated accordingly.
Postgres Target Changes
packages/3-targets/3-targets/postgres/src/core/migrations/descriptor-planner.ts, packages/3-targets/3-targets/postgres/src/exports/control.ts, packages/3-targets/3-targets/postgres/test/migrations/descriptor-planner.scenarios.*
Removed needsDataMigration from DescriptorPlannerValue and planWithDescriptors success result; tests and scenarios no longer assert that flag.
Mongo Family Emit Capability
packages/2-mongo-family/9-family/src/core/mongo-emit.ts, packages/2-mongo-family/9-family/src/core/mongo-target-descriptor.ts, packages/2-mongo-family/9-family/test/mongo-emit.test.ts, packages/2-mongo-family/9-family/package.json
Added mongoEmit implementation wired into target descriptor: dynamic import of <dir>/migration.ts, supports Migration subclass or factory function returning object with plan(), validates plan() returns array, writes ops.json, and emits MIG-domain structured errors on invalid shapes.
Docs & Architecture
docs/architecture/adrs/ADR 027 - Error Envelope Stable Codes.md, docs/architecture/adrs/ADR 196 - In-process emit for class-flow targets.md, docs/architecture/subsystems/7. Migration System.md
Updated ADRs and subsystem docs to reflect new MIG codes and the in-process emit pipeline supporting both class and factory default-export shapes and plan()-based contract.
CLI Tests & Coverage
packages/1-framework/3-tooling/cli/test/commands/migration-emit.test.ts, packages/1-framework/3-tooling/cli/test/commands/migration-plan-command.test.ts, packages/1-framework/3-tooling/cli/test/commands/migration-verify.test.ts, packages/1-framework/3-tooling/cli/test/output.migration-commands.test.ts, packages/1-framework/3-tooling/cli/tsdown.config.ts, packages/1-framework/3-tooling/cli/vitest.config.ts
Added comprehensive migration-emit and plan tests; removed migration-verify tests; updated tsdown/coverage entries to include emit command.
E2E & Journey Tests
test/integration/test/cli-journeys/data-transform.e2e.test.ts, test/integration/test/cli-journeys/schema-evolution-migrations.e2e.test.ts, test/integration/test/utils/journey-test-helpers.ts
Updated integration journeys to use migration emit and replaced runMigrationVerify helper with runMigrationEmit.
Examples / Manifests
examples/mongo-demo/migrations/20260409T1030_migration/migration.json
Updated migrationId hash in example migration manifest to reflect changed content-addressed identity computation.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CLI
    participant EmitLib as emitMigration
    participant Target as Target.migrations
    participant MigrationTS
    participant Attestation

    User->>CLI: migration emit --dir <dir>
    CLI->>EmitLib: emitMigration(dir, ctx)
    EmitLib->>MigrationTS: hasMigrationTs(dir)?
    MigrationTS-->>EmitLib: true

    alt Target provides emit()
        EmitLib->>Target: emit({ dir, frameworkComponents })
        Target->>MigrationTS: dynamic import migration.ts
        MigrationTS-->>Target: exports.default

        alt default export is Migration subclass
            Target->>MigrationTS: new MigrationExport()
            Target->>MigrationTS: instance.plan()
        else default export is factory function
            Target->>MigrationTS: default()
            MigrationTS-->>Target: { plan() }
            Target->>MigrationTS: object.plan()
        end

        MigrationTS-->>Target: MigrationPlanOperation[]
        Target-->>EmitLib: operations
    else Target provides resolveDescriptors
        EmitLib->>Target: resolveDescriptors(...)
        Target-->>EmitLib: operations
    end

    EmitLib->>Attestation: attestMigration(dir)
    Attestation-->>EmitLib: migrationId
    EmitLib-->>CLI: { operations, migrationId }
    CLI-->>User: Emitted ops.json and attested migration
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 From verify's old dance to emit's new gleam,
migrations now shape-shift—class or factory stream,
plan() calls gather ops written neat,
ops.json saved, migrationId hashed and complete,
hop, hop—errors now wear the MIG badge with vim!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: renaming the CLI command from 'migration verify' to 'migration emit', which is the main structural/behavioral focus of the PR.

✏️ 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-2219-a-cli-emit-unification

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 Apr 19, 2026

Open in StackBlitz

@prisma-next/mongo-runtime

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

@prisma-next/family-mongo

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

@prisma-next/sql-runtime

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

@prisma-next/family-sql

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

@prisma-next/middleware-telemetry

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/middleware-telemetry@353

@prisma-next/mongo

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

@prisma-next/extension-paradedb

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

@prisma-next/extension-pgvector

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

@prisma-next/postgres

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

@prisma-next/sql-orm-client

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

@prisma-next/sqlite

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

@prisma-next/target-mongo

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

@prisma-next/adapter-mongo

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

@prisma-next/driver-mongo

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

@prisma-next/contract

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

@prisma-next/utils

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

@prisma-next/config

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

@prisma-next/errors

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

@prisma-next/framework-components

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

@prisma-next/operations

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

@prisma-next/contract-authoring

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

@prisma-next/ids

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

@prisma-next/psl-parser

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

@prisma-next/psl-printer

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

@prisma-next/cli

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

@prisma-next/emitter

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

@prisma-next/migration-tools

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

prisma-next

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

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

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

@prisma-next/runtime-executor

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/runtime-executor@353

@prisma-next/mongo-codec

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

@prisma-next/mongo-contract

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

@prisma-next/mongo-value

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

@prisma-next/mongo-contract-psl

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

@prisma-next/mongo-contract-ts

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

@prisma-next/mongo-emitter

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

@prisma-next/mongo-schema-ir

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

@prisma-next/mongo-query-ast

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

@prisma-next/mongo-orm

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

@prisma-next/mongo-pipeline-builder

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-pipeline-builder@353

@prisma-next/mongo-lowering

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

@prisma-next/mongo-wire

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

@prisma-next/sql-contract

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

@prisma-next/sql-errors

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

@prisma-next/sql-operations

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

@prisma-next/sql-schema-ir

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

@prisma-next/sql-contract-psl

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

@prisma-next/sql-contract-ts

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

@prisma-next/sql-contract-emitter

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

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

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

@prisma-next/sql-relational-core

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

@prisma-next/sql-builder

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

@prisma-next/target-postgres

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

@prisma-next/target-sqlite

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

@prisma-next/adapter-postgres

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

@prisma-next/adapter-sqlite

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

@prisma-next/driver-postgres

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

@prisma-next/driver-sqlite

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

commit: 4fffb38

@wmadden wmadden force-pushed the tml-2219-d-adrs-and-subsystem-docs branch from 122903f to 4be4a19 Compare April 19, 2026 09:50
@wmadden wmadden force-pushed the tml-2219-a-cli-emit-unification branch from 3bde981 to 53f8188 Compare April 19, 2026 11:41
@wmadden wmadden force-pushed the tml-2219-d-adrs-and-subsystem-docs branch from 4be4a19 to 7482e70 Compare April 19, 2026 12:06
@wmadden wmadden force-pushed the tml-2219-a-cli-emit-unification branch from 53f8188 to ba88a32 Compare April 19, 2026 12:08
@wmadden wmadden changed the title refactor(cli,framework): unify migration plan to run emit inline; rename verify->emit Rename migration verify -> migration emit, add class-based migration scaffolding Apr 19, 2026
@wmadden wmadden changed the title Rename migration verify -> migration emit, add class-based migration scaffolding Rename migration verify -> migration emit Apr 19, 2026
@wmadden wmadden force-pushed the tml-2219-a-cli-emit-unification branch from ba88a32 to c97e2dc Compare April 19, 2026 15:58
Base automatically changed from tml-2219-d-adrs-and-subsystem-docs to main April 19, 2026 16:25
wmadden added 7 commits April 19, 2026 18:25
…ame verify->emit

migration verify becomes migration emit and now does the actual work
(in-process dynamic-import the migration.ts file, run the emit helper,
write attested ops.json) rather than just validating an external emit
step. migration plan always runs emit inline as part of plan, dropping
the needsDataMigration branch that the descriptor planner used to expose.

Adds the emit capability to TargetMigrationsCapability and extracts an
emitMigration helper that centrally owns attestation. The mongo family
plugs in via mongoEmit, which dynamic-imports the class-flow
migration.ts and writes ops.json.

Structured error codes for emit-path validation:
- PN-MIG-2002 errorMigrationFileMissing
- PN-MIG-2003 errorMigrationInvalidDefaultExport
- PN-MIG-2004 errorMigrationPlanNotArray

Migration identity is now storage-only (migrationId derived from a
content hash of the operations); the framework attestMigration helper
extracts that single owner.

This PR is structural cleanup that unblocks the class-flow scaffolding
work in PR B and the data transforms work in PR C. No new authoring
strategies or new operation kinds.

Part 2/4 of tml-2219; depends on #352.
…rors

The CliStructuredError class only supported CLI and RUN domains, so
MIG-domain errors produced wrong envelope prefixes and failed the is()
type guard.  Widen the domain union, fix toEnvelope() prefix mapping,
and wire the new src/exports/migration.ts entry point into tsdown +
package.json exports.
Corrections already made in ba88a32 but not yet committed separately:
- F01: fix emit-helper header docstring (attestation ownership)
- F02: tighten CLI test to mock errorMigrationFileMissing / PN-MIG-2002
- F07: document migrationId identity algorithm and link ADR 199
- F08: replace defensive throw with assert in emitDescriptorFlow
- F09: accept function-shape default exports in mongoEmit
- F10: remove dead "an array" branch from describeValue
- F11: collapse emit thunk in mongo-target-descriptor
Cover all five dispatcher branches directly: missing migration.ts
(PN-MIG-2002), descriptor-flow routing, class-flow routing, tiebreak
when both capabilities present, and unsupported-target error.  Also
verify attestMigration call ordering in the class flow.
Replace the tautological no-op envelope test with a proper mocked
command test that exercises executeMigrationPlanCommand when
fromHash === toStorageHash, and add an emit-after-scaffold ordering
test that locks the AC2 behaviour change at the test level.
The else branch in mongoEmit treated the exported function as returning
raw operations. The intended design is a factory function returning an
object with a plan() method — both shapes now funnel through plan().

Error message and fix hint for PN-MIG-2003 updated to mention both
accepted shapes (class subclass + factory function).
ADR 027, ADR 196, and Migration System subsystem doc now describe both
accepted migration.ts shapes: class subclass and factory function
returning { plan() }. Aligns with the implementation fix in mongoEmit.
@wmadden wmadden force-pushed the tml-2219-a-cli-emit-unification branch from 50911ef to 75531bc Compare April 19, 2026 16:29
Copy link
Copy Markdown

@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: 3

Caution

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

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

103-107: ⚠️ Potential issue | 🟡 Minor

Stale verify wording remains in scaffolding docs.

This block still says “On verify,” which is now outdated after the command rename.

📝 Suggested doc fix
- * On verify, this file is re-evaluated to produce the final ops.
+ * On emit, this file is re-evaluated to produce the final ops.

As per coding guidelines: **/*.{md,ts,tsx}: Keep docs current (READMEs, rules, links) and prefer links to canonical docs over long comments.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/1-framework/3-tooling/migration/src/migration-ts.ts` around lines
103 - 107, Update the top-of-file scaffolding comment in migration-ts.ts:
replace the outdated phrase "On verify," with a current, command-agnostic
wording such as "When running the migration verification command" and, if you
know the new command name, append it in parentheses (for example "(use
<new-command-name>)"); edit the comment block that begins with "Scaffolds a
migration.ts file..." so the documentation no longer references the old "verify"
command.
🧹 Nitpick comments (3)
packages/1-framework/3-tooling/cli/test/lib/migration-emit.test.ts (1)

61-75: Prefer await expect(...).rejects.toMatchObject() for async error assertions.

Since emitMigration is an async function that throws, use the async rejection assertion pattern instead of manual try/catch.

♻️ Proposed refactor
-  it('throws errorMigrationFileMissing when hasMigrationTs returns false', async () => {
-    mocks.hasMigrationTs.mockResolvedValue(false);
-
-    let thrown: unknown;
-    try {
-      await emitMigration(DIR, makeCtx() as unknown as Parameters<EmitMigration>[1]);
-    } catch (error) {
-      thrown = error;
-    }
-
-    expect(thrown).toMatchObject({
-      code: '2002',
-      message: 'migration.ts not found',
-    });
-  });
+  it('throws errorMigrationFileMissing when hasMigrationTs returns false', async () => {
+    mocks.hasMigrationTs.mockResolvedValue(false);
+
+    await expect(
+      emitMigration(DIR, makeCtx() as unknown as Parameters<EmitMigration>[1]),
+    ).rejects.toMatchObject({
+      code: '2002',
+      message: 'migration.ts not found',
+    });
+  });

The same pattern applies to lines 144-164 for errorTargetMigrationNotSupported.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/1-framework/3-tooling/cli/test/lib/migration-emit.test.ts` around
lines 61 - 75, Replace the manual try/catch assertions with Jest's async
rejection pattern: for the test that calls emitMigration when
mocks.hasMigrationTs.mockResolvedValue(false), change the assertion to await
expect(emitMigration(DIR, makeCtx() as unknown as
Parameters<EmitMigration>[1])).rejects.toMatchObject({ code: '2002', message:
'migration.ts not found' }); and apply the same change for the other test that
expects errorTargetMigrationNotSupported (lines 144-164) so both use await
expect(...).rejects.toMatchObject(...) instead of capturing thrown via
try/catch; keep references to emitMigration, hasMigrationTs, and the error shape
the same.
packages/1-framework/3-tooling/cli/test/commands/migration-emit.test.ts (1)

133-144: Prefer await expect(...).rejects.toThrow() over manual try/catch in async tests.

The error propagation tests use manual try/catch blocks, but the coding guidelines prefer expect().toThrow() patterns. Since executeCommand is async and the structured errors are thrown/rejected, use await expect(...).rejects.toMatchObject(...).

♻️ Proposed refactor for async error assertions
-    it('propagates class-flow emit errors (e.g. invalid default export) structurally', async () => {
-      mockMigrationCapableConfig();
-      mocks.emitMigrationMock.mockRejectedValue(
-        errorMigrationInvalidDefaultExport('migrations/20260101_test', 'undefined'),
-      );
-
-      const command = createMigrationEmitCommand();
-      try {
-        await executeCommand(command, ['--dir', 'migrations/20260101_test', '--json']);
-      } catch {
-        /* expected non-zero exit */
-      }
-
-      const jsonLine = consoleOutput.find((line) => line.trimStart().startsWith('{'));
+    it('propagates class-flow emit errors (e.g. invalid default export) structurally', async () => {
+      mockMigrationCapableConfig();
+      mocks.emitMigrationMock.mockRejectedValue(
+        errorMigrationInvalidDefaultExport('migrations/20260101_test', 'undefined'),
+      );
+
+      const command = createMigrationEmitCommand();
+      await executeCommand(command, ['--dir', 'migrations/20260101_test', '--json']);
+
+      const jsonLine = consoleOutput.find((line) => line.trimStart().startsWith('{'));

The same refactor pattern applies to lines 153-157, 175-179, and 194-198.

Note: If executeCommand catches errors internally and returns exit codes (rather than throwing), this pattern may be intentional. Verify how executeCommand handles errors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/1-framework/3-tooling/cli/test/commands/migration-emit.test.ts`
around lines 133 - 144, Replace the manual try/catch used to assert that
executeCommand(command, ['--dir', 'migrations/20260101_test', '--json']) fails
with the preferred Jest async assertion: use await
expect(executeCommand(...)).rejects.toMatchObject(...) (or
.rejects.toThrow/.toMatchObject with the structured error) so the test asserts
the rejection directly; do the same refactor for the other occurrences mentioned
(around lines 153-157, 175-179, 194-198) and first verify that executeCommand
actually rejects on error (if it returns an exit code instead, adjust the test
to assert that return value instead).
packages/2-mongo-family/9-family/test/mongo-emit.test.ts (1)

188-313: Replace manual try/catch rejection assertions with expect(...).rejects pattern.

Line 188 onward repeats manual error capture in several tests; using promise rejection assertions is shorter and more consistent with test guidance.

♻️ Proposed refactor pattern
-      let thrown: unknown;
-      try {
-        await mongoEmit({ dir: pkgDir, frameworkComponents: [] });
-      } catch (error) {
-        thrown = error;
-      }
-
-      expect(CliStructuredError.is(thrown)).toBe(true);
-      expect(thrown).toMatchObject({
+      await expect(
+        mongoEmit({ dir: pkgDir, frameworkComponents: [] }),
+      ).rejects.toMatchObject({
         code: '2004',
         domain: 'MIG',
         meta: { dir: pkgDir },
       });
+  async function expectMigError(code: '2002' | '2003' | '2004') {
+    await expect(
+      mongoEmit({ dir: pkgDir, frameworkComponents: [] }),
+    ).rejects.toMatchObject({
+      code,
+      domain: 'MIG',
+      meta: { dir: pkgDir },
+    });
+  }

As per coding guidelines: **/*.{test,spec}.{js,ts,tsx} and **/*.test.ts require using throw/rejection expectations instead of manual try/catch blocks in tests.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/2-mongo-family/9-family/test/mongo-emit.test.ts` around lines 188 -
313, Tests repeatedly capture thrown errors via try/catch then assert
CliStructuredError.is(thrown) and thrown matches an object; replace each manual
capture (around calls to mongoEmit({...}) in these cases) with the promise
rejection assertion form: await expect(mongoEmit({ dir: pkgDir,
frameworkComponents: [] })).rejects.toMatchObject({ code: '2004'|'2003'|'2002',
domain: 'MIG', meta: { dir: pkgDir } }); and (where you still need the
CliStructuredError.is check) use a rejects predicate or a second rejects
assertion that validates CliStructuredError.is on the rejection; update the
tests that reference the migration.ts variants (function-form, missing file,
non-constructor, non-Migration subclass, class-plan not array) to use this
pattern instead of the try/catch + thrown variable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/architecture` docs/subsystems/7. Migration System.md:
- Around line 122-123: The class-based migration example is inconsistent: it
documents that subclasses of Migration should override plan() and describe(),
but the example implements operations; update the example to implement plan()
(returning the array of migration operations) and remove/replace any operations
implementation so it matches the contract described for the Migration subclass
(also update the other example instances referenced around the same section,
e.g., the paragraph covering lines 134-138, to use plan() instead of operations
and ensure describe() still returns the from/to manifest metadata).

In `@packages/1-framework/3-tooling/cli/src/commands/migration-apply.ts`:
- Line 293: The remediation command string currently interpolates the migration
path using migrationsRelative and matchingDraft.dirName without quoting, which
breaks when the path contains spaces; update the template to wrap the
interpolated path in single quotes (e.g. change `--dir
${migrationsRelative}/${matchingDraft.dirName}` to `--dir
'${migrationsRelative}/${matchingDraft.dirName}'`) so the generated `prisma-next
migration emit` command is copy-safe.

In `@packages/1-framework/3-tooling/cli/src/commands/migration-new.ts`:
- Line 251: The printed command currently injects the raw path (${value.dir})
into the template literal, which breaks for paths with spaces; update the
template to quote and properly escape the dir value (e.g., change `--dir
${value.dir}` to `--dir "${value.dir}"` or use JSON.stringify(value.dir) in the
template) so the emitted line `\nEdit migration.ts, then run \`prisma-next
migration emit --dir ${value.dir}\` to attest.` becomes safe to copy/paste.

---

Outside diff comments:
In `@packages/1-framework/3-tooling/migration/src/migration-ts.ts`:
- Around line 103-107: Update the top-of-file scaffolding comment in
migration-ts.ts: replace the outdated phrase "On verify," with a current,
command-agnostic wording such as "When running the migration verification
command" and, if you know the new command name, append it in parentheses (for
example "(use <new-command-name>)"); edit the comment block that begins with
"Scaffolds a migration.ts file..." so the documentation no longer references the
old "verify" command.

---

Nitpick comments:
In `@packages/1-framework/3-tooling/cli/test/commands/migration-emit.test.ts`:
- Around line 133-144: Replace the manual try/catch used to assert that
executeCommand(command, ['--dir', 'migrations/20260101_test', '--json']) fails
with the preferred Jest async assertion: use await
expect(executeCommand(...)).rejects.toMatchObject(...) (or
.rejects.toThrow/.toMatchObject with the structured error) so the test asserts
the rejection directly; do the same refactor for the other occurrences mentioned
(around lines 153-157, 175-179, 194-198) and first verify that executeCommand
actually rejects on error (if it returns an exit code instead, adjust the test
to assert that return value instead).

In `@packages/1-framework/3-tooling/cli/test/lib/migration-emit.test.ts`:
- Around line 61-75: Replace the manual try/catch assertions with Jest's async
rejection pattern: for the test that calls emitMigration when
mocks.hasMigrationTs.mockResolvedValue(false), change the assertion to await
expect(emitMigration(DIR, makeCtx() as unknown as
Parameters<EmitMigration>[1])).rejects.toMatchObject({ code: '2002', message:
'migration.ts not found' }); and apply the same change for the other test that
expects errorTargetMigrationNotSupported (lines 144-164) so both use await
expect(...).rejects.toMatchObject(...) instead of capturing thrown via
try/catch; keep references to emitMigration, hasMigrationTs, and the error shape
the same.

In `@packages/2-mongo-family/9-family/test/mongo-emit.test.ts`:
- Around line 188-313: Tests repeatedly capture thrown errors via try/catch then
assert CliStructuredError.is(thrown) and thrown matches an object; replace each
manual capture (around calls to mongoEmit({...}) in these cases) with the
promise rejection assertion form: await expect(mongoEmit({ dir: pkgDir,
frameworkComponents: [] })).rejects.toMatchObject({ code: '2004'|'2003'|'2002',
domain: 'MIG', meta: { dir: pkgDir } }); and (where you still need the
CliStructuredError.is check) use a rejects predicate or a second rejects
assertion that validates CliStructuredError.is on the rejection; update the
tests that reference the migration.ts variants (function-form, missing file,
non-constructor, non-Migration subclass, class-plan not array) to use this
pattern instead of the try/catch + thrown variable.
🪄 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: 70b5a2f8-3c08-4b50-ab86-975d80d32b85

📥 Commits

Reviewing files that changed from the base of the PR and between 0b49f0c and 75531bc.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (47)
  • docs/architecture docs/adrs/ADR 027 - Error Envelope Stable Codes.md
  • docs/architecture docs/adrs/ADR 196 - In-process emit for class-flow targets.md
  • docs/architecture docs/subsystems/7. Migration System.md
  • examples/mongo-demo/migrations/20260409T1030_migration/migration.json
  • packages/1-framework/1-core/errors/package.json
  • packages/1-framework/1-core/errors/src/control.ts
  • packages/1-framework/1-core/errors/src/exports/migration.ts
  • packages/1-framework/1-core/errors/src/migration.ts
  • packages/1-framework/1-core/errors/test/migration.test.ts
  • packages/1-framework/1-core/errors/tsdown.config.ts
  • packages/1-framework/1-core/framework-components/src/control-migration-types.ts
  • packages/1-framework/3-tooling/cli/README.md
  • packages/1-framework/3-tooling/cli/package.json
  • packages/1-framework/3-tooling/cli/src/cli.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-apply.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-emit.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-new.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-plan.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-show.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-status.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-verify.ts
  • packages/1-framework/3-tooling/cli/src/lib/migration-emit.ts
  • packages/1-framework/3-tooling/cli/src/utils/cli-errors.ts
  • packages/1-framework/3-tooling/cli/src/utils/formatters/help.ts
  • packages/1-framework/3-tooling/cli/src/utils/formatters/migrations.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-e2e.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-emit.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-plan-command.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-verify.test.ts
  • packages/1-framework/3-tooling/cli/test/lib/migration-emit.test.ts
  • packages/1-framework/3-tooling/cli/test/output.migration-commands.test.ts
  • packages/1-framework/3-tooling/cli/tsdown.config.ts
  • packages/1-framework/3-tooling/cli/vitest.config.ts
  • packages/1-framework/3-tooling/migration/src/attestation.ts
  • packages/1-framework/3-tooling/migration/src/migration-ts.ts
  • packages/1-framework/3-tooling/migration/test/attestation.test.ts
  • packages/2-mongo-family/9-family/package.json
  • packages/2-mongo-family/9-family/src/core/mongo-emit.ts
  • packages/2-mongo-family/9-family/src/core/mongo-target-descriptor.ts
  • packages/2-mongo-family/9-family/test/mongo-emit.test.ts
  • packages/3-targets/3-targets/postgres/src/core/migrations/descriptor-planner.ts
  • packages/3-targets/3-targets/postgres/src/exports/control.ts
  • packages/3-targets/3-targets/postgres/test/migrations/descriptor-planner.scenarios.md
  • packages/3-targets/3-targets/postgres/test/migrations/descriptor-planner.scenarios.test.ts
  • test/integration/test/cli-journeys/data-transform.e2e.test.ts
  • test/integration/test/cli-journeys/schema-evolution-migrations.e2e.test.ts
  • test/integration/test/utils/journey-test-helpers.ts
💤 Files with no reviewable changes (5)
  • packages/3-targets/3-targets/postgres/src/exports/control.ts
  • packages/3-targets/3-targets/postgres/test/migrations/descriptor-planner.scenarios.test.ts
  • packages/1-framework/3-tooling/cli/test/commands/migration-verify.test.ts
  • packages/3-targets/3-targets/postgres/src/core/migrations/descriptor-planner.ts
  • packages/1-framework/3-tooling/cli/src/commands/migration-verify.ts

Comment thread docs/architecture docs/subsystems/7. Migration System.md
Comment thread packages/1-framework/3-tooling/cli/src/commands/migration-apply.ts Outdated
Comment thread packages/1-framework/3-tooling/cli/src/commands/migration-new.ts Outdated
- Fix class-flow example: replace `get operations()` with `plan()` to
  match the Migration base class contract (subsystem doc + ADR 196)
- Quote interpolated dir paths in remediation commands so they are
  copy-safe when the path contains spaces (migration-apply, migration-new)
- Replace stale "On verify" wording with "On emit" in migration-ts.ts
@wmadden wmadden merged commit 21a24dd into main Apr 19, 2026
16 checks passed
@wmadden wmadden deleted the tml-2219-a-cli-emit-unification branch April 19, 2026 18:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant