versions
- @prisma/orm-postgres 8.0.0-rc.8
- prisma CLI 8.0.0-rc.8 (dist bundle, skills sync 2026-09-02)
what we hit
A migration package whose from == to (a true contract self-edge) is refused by migration check with MIGRATION.CHECK_NOOP_SELF_EDGE unless it carries an op with operationClass: "data".
But the shipped skill reference (skills/prisma-8/references/... invariants module doc, deriveProvidedInvariants) states the two axes are orthogonal:
Both data-class ops (data-transforms, e.g. backfills) and additive-class opaque DDL (e.g. cipherstash's vendored EQL bundle via installEqlBundleOp) may declare invariantIds: ... an extension can ship additive non-IR-derivable DDL (the only way the planner can know the bundle is already applied is via the invariantId on the marker) without needing to mis-classify it as data-class.
installEqlBundleOp is cited as exactly the sanctioned shape: an additive, non-IR-derivable DDL self-edge whose invariantId records marker bookkeeping. Under the checker's current rule (pkg.ops.some(op => op.operationClass === "data") in the bundle), that package would be flagged NOOP_SELF_EDGE.
our concrete case (not hypothetical)
Two real migrations on a live project, authored the documented way (rawSql with precheck/execute/postcheck + invariantId, self-emitted ops.json + migration.json with populated providedInvariants):
ALTER TYPE public.lane_liveness_state ADD VALUE 'capture_declared_off' (+ COMMENT ON TYPE)
CREATE OR REPLACE VIEW public.cli_session_liveness AS ... (view bodies are not contract-visible, so a view-body replace can never be a from != to edge)
Both are pure additive DDL. Neither has any honest data component (a view replace has nothing to backfill), so neither can carry a data-class op, so both are refused as "true no-op self-edges" — which they are not: they change database objects and have real postchecks.
A parallel package in the same repo that DOES have a backfill passes, confirming the checker's data-only gate.
ask
Either:
- Relax the self-edge check to accept self-edges whose ops carry
invariantIds (the documented marker-bookkeeping case), or
- Document that additive-only self-edges are unsupported and give the sanctioned pattern for non-IR-derivable DDL that has no data component (e.g. view-body replaces, enum-value additions that must precede dependent DDL in a separate transaction).
repro sketch
prisma migration new --name x --from <current-hash> # no contract change
# migration.ts: one rawSql({ operationClass: "additive", invariantId: "ns:thing", precheck, execute, postcheck })
prisma migration check
# -> MIGRATION.CHECK_NOOP_SELF_EDGE
versions
what we hit
A migration package whose
from == to(a true contract self-edge) is refused bymigration checkwithMIGRATION.CHECK_NOOP_SELF_EDGEunless it carries an op withoperationClass: "data".But the shipped skill reference (
skills/prisma-8/references/...invariants module doc,deriveProvidedInvariants) states the two axes are orthogonal:installEqlBundleOpis cited as exactly the sanctioned shape: an additive, non-IR-derivable DDL self-edge whoseinvariantIdrecords marker bookkeeping. Under the checker's current rule (pkg.ops.some(op => op.operationClass === "data")in the bundle), that package would be flaggedNOOP_SELF_EDGE.our concrete case (not hypothetical)
Two real migrations on a live project, authored the documented way (rawSql with precheck/execute/postcheck + invariantId, self-emitted ops.json + migration.json with populated providedInvariants):
ALTER TYPE public.lane_liveness_state ADD VALUE 'capture_declared_off'(+ COMMENT ON TYPE)CREATE OR REPLACE VIEW public.cli_session_liveness AS ...(view bodies are not contract-visible, so a view-body replace can never be a from != to edge)Both are pure additive DDL. Neither has any honest data component (a view replace has nothing to backfill), so neither can carry a data-class op, so both are refused as "true no-op self-edges" — which they are not: they change database objects and have real postchecks.
A parallel package in the same repo that DOES have a backfill passes, confirming the checker's data-only gate.
ask
Either:
invariantIds (the documented marker-bookkeeping case), orrepro sketch