Skip to content

Chore/prisma next 0.14 upgrade#608

Merged
calvinbrewer merged 16 commits into
mainfrom
chore/prisma-next-0.14-upgrade
Jul 9, 2026
Merged

Chore/prisma next 0.14 upgrade#608
calvinbrewer merged 16 commits into
mainfrom
chore/prisma-next-0.14-upgrade

Conversation

@calvinbrewer

@calvinbrewer calvinbrewer commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Upgraded Prisma Next support to version 0.14, including updated example and migration workflows.
    • Example and docs now use the public schema in ORM access paths.
  • Bug Fixes

    • Improved example test setup and teardown so local auth/config files are handled more reliably.
    • Updated migration checks to be more stable with parameterized validation.
  • Documentation

    • Refreshed setup instructions, example commands, and local Postgres port references.

calvinbrewer and others added 14 commits July 9, 2026 03:23
…ive type assertions

Biome reflows these assertions to multi-line, which strands the
directive on the line before the one where tsc reports TS2344. Move
the directive inside the generic so it sits directly above the
erroring line and survives formatting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hecking

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per-transition changes (extension-author upgrade instructions 0.8→0.9):
- strip fromContract/toContract from the seed migration manifest
  (strip-inline-contracts codemod)

Additional 0.9 SPI churn not covered by the instructions:
- accept the widened InsertValue union in the bulk-encrypt routing-key
  stamp (insert rows and update/on-conflict sets now carry
  PreparedParamRef | DefaultValueExpr variants)
- validateContract moved: @prisma-next/sql-contract/validate is gone;
  use validateSqlContractFully from /validators (single-argument)
- SqlStorage requires namespaces: construct test fixture storage via
  new SqlStorage(...) so the constructor defaults the namespace map
- adapter lower() emits structured LoweredParam entries; unwrap
  {kind:'literal', value} in operator-lowering assertions
  (literalParamValue helper)
- named-type storage entries are stamped kind:'codec-instance'

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per-transition changes (extension-author upgrade instructions 0.9→0.10):
- storage.types kind-stamping: no-op for this package (no storage.types
  entries on disk or constructed in source; verified with the codemod's
  --check mode)

Additional 0.10 SPI churn not covered by the instructions:
- the storage IR is namespace-enveloped: tables live under
  storage.namespaces.<ns> (late-bound __unbound__ here) instead of
  flat storage.tables; the structural validator rejects the old shape
  - deriveStackSchemas walks every namespace's tables
  - all contract fixtures and storage reads updated to the new shape
- FieldEventContext gained a required namespaceId coordinate; all
  direct codec-hook invocations in tests pass '__unbound__'
- re-emitted src/contract.{json,d.ts} via prisma-next contract emit
  (storageHash changed with the namespace envelope) and re-pinned the
  baseline migration: end-contract.{json,d.ts}, migration.ts describe()
  to-hash, self-emitted ops.json/migration.json, migrations/refs/head.json

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All five 0.10→0.11 extension-author changes are no-ops for this package
(no handcrafted Contract type literals, no facade, no Mongo, no
.insert()/.withValues() call sites).

Additional 0.11 SPI churn: update/on-conflict set values widened to
AnyExpression; the bulk-encrypt routing-key stamp accepts the wider
union (it only acts on kind === 'param-ref').

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per-transition changes (extension-author upgrade instructions 0.11→0.12):
- strip-migration-labels-hints: codemod removed labels/hints from the
  seed migration manifest and recomputed migrationHash
- extension-public-default-baseline: re-emitted the contract space —
  the default namespace flips __unbound__/postgres-unbound-schema →
  public/postgres-schema — and re-pinned the baseline migration
  (end-contract.{json,d.ts}, migration.ts to-hash, self-emitted
  ops.json/migration.json, refs/head.json)
- domain plane: contract.json now carries domain.namespaces instead of
  flat models; roots entries are { model, namespace } objects; all
  handcrafted contract fixtures replace models:{} with
  domain.namespaces.<ns>.models

Additional 0.12 SPI churn not covered by the instructions:
- SqlMiddlewareContext (via RuntimeMiddlewareContext) gained a required
  planExecutionId — middleware test contexts stamp a fixed id
- SqlStorageInput.namespaces is now Readonly<Record<string, SqlNamespace>>;
  test fixtures construct entries via buildSqlNamespace(...)
- the PSL interpreter emits authored tables into the target-owned
  default namespace (public on Postgres); asStorage test helpers walk
  all namespaces instead of hard-coding __unbound__
- assertDescriptorSelfConsistency now recomputes through the family
  canonicalization hooks; the descriptor test passes
  sqlContractCanonicalizationHooks explicitly

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per-transition changes (extension-author upgrade instructions 0.12→0.13):
- storage-namespace-envelope-re-emit: re-emitted the contract space —
  storage entries move under storage.namespaces.<ns>.entries.<kind> —
  and re-pinned the baseline migration (end-contract.{json,d.ts},
  migration.ts to-hash, self-emitted ops.json/migration.json,
  refs/head.json). The 0.13 emitter also injects an empty __unbound__
  namespace slot alongside public; 0.14 removes it (TML-2916).
- sqlite-create-table-method, strip-empty-type-params, and
  codec-ref-resolver namespaceId threading: no-ops for this package
  (no SQLite migrations, no empty typeParams, no direct codec-ref
  construction)

Additional 0.13 SPI churn not covered by the instructions:
- SqlNamespaceTablesInput moved tables into the entries envelope:
  buildSqlNamespace({ id, entries: { table } }); contract fixtures and
  namespace reads updated
- TargetPackRef gained required defaultNamespaceId ('public' for the
  Postgres stub in the PSL interpreter tests)
- interpretPslDocumentToSqlContract requires composedExtensionContracts;
  tests pass the cipherstash contract keyed by its space id

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per-transition changes (extension-author upgrade instructions 0.13→0.14):
- migration-op-factories-to-methods: ran the codemod; the seed
  migration's only op factory is rawSql, which survives in 0.14 —
  import churn only
- create-runtime-removed: code was unaffected (createRuntime appeared
  only in doc comments, now updated to new PostgresRuntimeImpl)
- re-emitted the contract space (the 0.14 emitter drops the spurious
  empty __unbound__ namespace slot, TML-2916, and no longer emits
  ContractModelDefinitions) and re-pinned the baseline migration
- uuid presets, flat builder accessors, namespace-entries dot access,
  enum surface, namespaced TypeMaps/TableProxy,
  ContractModelDefinitions: no-ops for this package
- planFieldEventOperations op calls can lower lazily; descriptor tests
  await Promise.all(ops.map(c => c.toOp()))

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…geset for the 0.14 upgrade

Prisma Next 0.14 removed the flat orm.<Model> accessors; the README
examples now go through db.orm.public.User.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Design for authoring EQL v3 columns in @cipherstash/prisma-next via concrete
per-domain constructors that map 1:1 to the public.* Postgres domains, with
capabilities encoded in the type system (no boolean-option surface, no
resolver). Reflects protect-ffi 0.28: public.* domains + eql_v3.* operator
functions, first-class BigInt family, DOMAIN_REGISTRY as source of truth,
separate v2/v3 entry points, and concrete per-domain types threaded end-to-end.
Add the design-A implementation plan and remediate the review findings.

Real bugs fixed:
- cipherstashBetween is now a self-parenthesised conjunction '(gte AND lte)'
  so any generic NOT/OR composition is safe (Postgres binds NOT tighter than
  AND; the 9c82f50 bug class). Operator-lowering tests pin the full
  parenthesised skeleton for all operators and assert NOT (gte AND lte).
- v3ToDriver restores bigintSafeReplacer; a stray bigint in a malformed
  envelope serialises instead of throwing. Pinned by a test.
- codec-runtime encode test now exercises the real envelope/.expose() contract
  (the old test fed a plain object with no .expose(), so it never passed).
- constants-v3 drift test replaced the masking cast with a pinned literal tuple
  (as const satisfies) + bidirectional Exclude guard + runtime equality check.

Type spine (the core of v3):
- Authoring factories are typed per-domain via v3Authored(types.X): distinct
  return type per domain, literal codecId/nativeType/capabilities (never
  string/unknown). Mandatory column-types.test-d.ts pins non-assignability +
  literal field types. Derivation preserves the concrete column type.

Separate v2/v3 entry points (decision 1b):
- v3 registers its own extension descriptor with a distinct
  CIPHERSTASH_V3_SPACE_ID/VERSION, keeping the same cipherstash* method names,
  and is never co-registered with v2 (the flat OperationRegistry rejects the
  duplicate method). Task 7 replaces the two-client dispatch with a v3-only
  cipherstashFromStackV3; a v2 codec id in a v3 contract is a hard error.

Plan under docs/superpowers/plans/ (locally git-excluded; force-added).

Remaining (follow-up): encrypted ORDER BY (asc/desc -> eql_v3.ord_term),
shared-primitive lift to eql/v3/, bulk-middleware edge-case tests, live
plaintext-oracle suite, genuine property tests, minor type cleanups.
The plan and spec were drafted against @prisma-next/* 0.8.0; the package
is now pinned at 0.14.0. Changes:

- new 'Prisma Next 0.14 ground truth' section: namespace-enveloped
  contract shape (storage.namespaces.<ns>.entries.table, domain plane,
  public default namespace), the manual contract-emit/re-pin loop (self-
  emit writes only ops.json + migration.json), current head hash,
  validateSqlContractFully fixture shape, structured LoweredParam,
  PSL-interpreter harness requirements (defaultNamespaceId,
  composedExtensionContracts), FieldEventContext namespaceId, lazily
  awaitable toOp(), planExecutionId on middleware contexts,
  createRuntime removal (rawSql survives), canonicalization hooks for
  descriptor self-consistency
- Task 7 + Task 9: V3ContractShape / deriveStackSchemasV3 / fixtures
  walk the 0.14 namespace envelope instead of flat storage.tables
- Task 8: v3 baseline describe() chains from the v2 head hash; Step 4
  rewritten as the real 0.14 emit loop with the invariant-only-edge
  question called out for verification
- Task 3/6: point new tests at the 0.14-shaped harnesses on this branch
- Task 10: replace the mixed-v2-v3-in-one-client live test (contradicted
  decision 1b and the spec) with side-by-side v2/v3 clients; the live
  suites go through cipherstashFromStackV3
- spec: revision pointer 2026-07-09c

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…veStackSchemas

The 0.12→0.13 upgrade step moved storage namespaces to the
entries.<kind> envelope but deriveStackSchemas kept reading
namespace.tables — its unit fixtures were updated in lockstep, so the
suite stayed green while derivation returned [] against a real
0.14-emitted contract.json (cipherstashFromStack would throw 'no
cipherstash columns found'). Caught by typechecking the example app
against its re-emitted contract. Fixtures now mirror the real emitted
shape.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- deps: every @prisma-next/* pinned at 0.14.0
- prisma-next.config.ts: prismaContract takes the target PACK ref
  (@prisma-next/target-postgres/pack, carrying defaultNamespaceId)
  instead of the control descriptor
- contract re-emitted in the 0.14 shape (public namespace, entries
  envelope, domain plane); migrations regenerated from scratch —
  fresh app initial migration + the cipherstash extension space
  re-materialised from the upgraded workspace package (head
  sha256:1e86a016…)
- ORM/builder access namespace-qualified: db.orm.public.User,
  db.sql.public.users (src/index.ts + every e2e suite)
- the CLI renamed 'migration apply' to 'migrate': package.json script,
  e2e global-setup, and READMEs updated (--yes for non-interactive
  runs)
- e2e walkthrough test (e2e/tests/prisma-example-readme.e2e.test.ts):
  snapshot/restore examples/prisma/.env instead of deleting it in
  teardown — the old teardown destroyed a developer's real
  credentials file

Full example e2e suite green against the docker harness: 36 passed,
1 skipped (known jsonbPathExists limitation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@calvinbrewer calvinbrewer requested a review from a team as a code owner July 9, 2026 10:56
@changeset-bot

changeset-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 85175c2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@cipherstash/prisma-next Minor
@cipherstash/prisma-next-example Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR upgrades @cipherstash/prisma-next to Prisma Next 0.14.0, migrating all extension contracts from a flat models/storage.tables shape to a namespace-enveloped domain/storage.namespaces structure. deriveStackSchemas and bulk-encrypt middleware AST typings are updated accordingly, example app/tests switch to db.orm.public.User accessors, and migration contracts are regenerated with new hashes. A new EQL v3 design spec is also added.

Changes

Prisma Next 0.14 Upgrade

Layer / File(s) Summary
Design spec and changeset
docs/superpowers/specs/2026-07-08-eql-v3-prisma-next-design.md, .changeset/prisma-next-0-14.md
Adds the EQL v3 Prisma-next design spec and a changeset documenting the 0.14 upgrade, contract shape changes, and required consumer updates.
Dependency bumps
examples/prisma/package.json, packages/prisma-next/package.json
Bumps all @prisma-next/* dependencies/devDependencies from 0.8.0 to 0.14.0.
Contract/middleware typing updates
packages/prisma-next/src/stack/derive-schemas.ts, packages/prisma-next/src/middleware/bulk-encrypt.ts, packages/prisma-next/src/exports/*.ts, examples/prisma/migrations/cipherstash/contract.d.ts
deriveStackSchemas walks storage.namespaces.*.entries.table; bulk-encrypt middleware widens AST value union types; doc examples updated for PostgresRuntimeImpl/Migration class usage.
Package generated contract regeneration
packages/prisma-next/src/contract.{d.ts,json}, packages/prisma-next/migrations/20260601T0000_install_eql_bundle/*, packages/prisma-next/migrations/refs/head.json
Regenerates contract types/JSON and the baseline install-eql-bundle migration to the new namespace-enveloped shape with new profile/storage hashes.
Package unit test updates
packages/prisma-next/test/*.ts
Updates tests to build contracts via storage.namespaces.*.entries.table, adds namespaceId/planExecutionId context fields, awaits async toOp() lowering, and unwraps lowered params via new literalParamValue helper.
Example app config/README/migration regeneration
examples/prisma/prisma-next.config.ts, examples/prisma/README.md, examples/prisma/.env.example, examples/prisma/docker-compose.yml, examples/prisma/migrations/**, examples/prisma/src/prisma/contract.{d.ts,json}, examples/prisma/test/e2e/README.md
Switches config target to postgresPack, changes docker Postgres port to 54338, regenerates cipherstash and app migration contracts/ops/migration files to namespace shape.
Example app and e2e ORM accessor migration
examples/prisma/src/index.ts, examples/prisma/test/e2e/*.e2e.test.ts, examples/prisma/test/e2e/global-setup.ts, packages/prisma-next/README.md
Switches all db.orm.User/db.sql.users calls to db.orm.public.User/db.sql.public.users; updates global-setup credential check and migration command to prisma-next migrate --yes.
README e2e .env snapshot/restore
e2e/tests/prisma-example-readme.e2e.test.ts
Adds device-code auth detection and includes .env in transient snapshot/restore instead of force-deleting it.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related PRs

  • cipherstash/stack#444: Updates deriveStackSchemas namespace traversal and bulkEncryptMiddleware typing on the same extension release artifacts.
  • cipherstash/stack#467: Modifies the same middleware test context builders (makeMiddlewareCtx, createCtx) touched here.
  • cipherstash/stack#472: Directly modifies the same e2e README walkthrough test's auth detection and .env snapshot/teardown handling.

Suggested reviewers: freshtonic

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: upgrading Prisma Next to 0.14.
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.
✨ 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 chore/prisma-next-0.14-upgrade

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

❤️ Share

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

calvinbrewer and others added 2 commits July 9, 2026 04:59
…gres port

- the e2e global-setup and the README-walkthrough test now accept the
  ~/.cipherstash profile written by 'stash auth login' (device-code
  flow stores device.json, not auth.json) — no .env with CS_* values
  is required for local runs; the stack client resolves the profile
- move the bundled Postgres from port 5544 to 54338 (matching the
  54329-style e2e harness port) — 5544 is a common enough choice to
  collide with other local containers, which cascaded into
  migrate/start failures in the walkthrough

Both suites green on profile auth alone: example e2e 36 passed /
1 skipped; README walkthrough 7/7 steps exit 0 (including pnpm start).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
docs/superpowers/specs/2026-07-08-eql-v3-prisma-next-design.md (1)

80-80: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add language specifiers to fenced code blocks.

Three code blocks lack a language identifier, flagged by markdownlint (MD040). Line 80 lists TypeScript factory names (ts), lines 297 and 310 show SQL operator templates (sql).

📝 Proposed fix
-```
+```ts
 types.Text, types.TextEq, types.TextMatch, types.TextOrd, types.TextOrdOre, types.TextSearch
-```
+```sql
 eql_v3.eq({{self}}, {{arg0}}::jsonb)
-```
+```sql
 (eql_v3.gte({{self}}, {{arg0}}::jsonb) AND eql_v3.lte({{self}}, {{arg1}}::jsonb))

Also applies to: 297-297, 310-310

🤖 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/superpowers/specs/2026-07-08-eql-v3-prisma-next-design.md` at line 80,
Add language identifiers to the fenced code blocks in the spec so markdownlint
MD040 passes: update the TypeScript factory-name block in the documentation to
use the proper ts fence, and update both SQL operator template blocks to use sql
fences. Use the existing fenced-code sections around the referenced ts and
eql_v3 template examples to locate and fix them without changing the content
inside the blocks.

Source: Linters/SAST tools

packages/prisma-next/test/psl-interpretation-numeric.test.ts (1)

66-85: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the duplicated asStorage helper to a shared test utility.

The same asStorage function (flattening storage.namespaces into a flat tables map) is duplicated verbatim in psl-interpretation-numeric.test.ts, psl-interpretation-other-types.test.ts, and psl-interpretation.test.ts. If the storage IR shape changes again, all three copies must be updated independently. Extracting it to a shared helper (e.g., in operator-lowering.helpers.ts or a new test/psl-helpers.ts) would eliminate this risk.

♻️ Suggested extraction

Create a shared helper, e.g. in test/psl-helpers.ts:

import type { StorageView } from './psl-interpretation.types' // or define inline

export function asStorage(storage: unknown): StorageView {
  const s = storage as {
    readonly namespaces?: Record<
      string,
      { entries?: { table?: StorageView['tables'] } }
    >
    readonly types?: StorageView['types']
  }
  const tables: StorageView['tables'] = {}
  for (const ns of Object.values(s.namespaces ?? {})) {
    Object.assign(tables, ns.entries?.table)
  }
  return {
    tables,
    ...(s.types !== undefined ? { types: s.types } : {}),
  }
}

Then replace the inline copies in all three test files with an import.

🤖 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/prisma-next/test/psl-interpretation-numeric.test.ts` around lines 66
- 85, The duplicated asStorage helper should be moved into a shared test utility
so all PSL interpretation tests use the same implementation. Extract the
namespace-flattening logic from asStorage into a common helper (for example in a
shared test helpers module) and then update psl-interpretation-numeric.test.ts,
psl-interpretation-other-types.test.ts, and psl-interpretation.test.ts to import
and use it. Keep the helper’s behavior identical, including flattening
storage.namespaces into tables and preserving types when present.
packages/prisma-next/test/derive-schemas.test.ts (1)

31-50: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding a multi-namespace test case.

All tests use a single __unbound__ namespace. A test with two or more namespaces (each containing distinct tables, and optionally a same-named table to verify merge/collision behavior) would exercise the Object.assign flattening loop in deriveStackSchemas and protect against the silent overwrite risk flagged in derive-schemas.ts.

🧪 Suggested multi-namespace test
 it('merges tables from multiple namespaces', () => {
   const contract = {
     storage: {
       namespaces: {
         __unbound__: {
           entries: {
             table: {
               users: { columns: { email: { codecId: 'cipherstash/string@1' } } },
             },
           },
         },
         public: {
           entries: {
             table: {
               posts: { columns: { title: { codecId: 'cipherstash/string@1' } } },
             },
           },
         },
       },
     },
   }
   const schemas = deriveStackSchemas(contract)
   expect(schemas).toHaveLength(2)
   expect(schemas.map(s => s.name).sort()).toEqual(['posts', 'users'])
 })
🤖 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/prisma-next/test/derive-schemas.test.ts` around lines 31 - 50, Add a
multi-namespace test in derive-schemas.test.ts that exercises deriveStackSchemas
with two or more namespaces instead of only __unbound__. Build distinct tables
per namespace and include an optional same-named table to verify the
Object.assign flattening behavior and detect any silent overwrite or merge
issues. Use the existing deriveStackSchemas test setup as the anchor and extend
the fixture structure under namespaces to cover this collision scenario.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@docs/superpowers/specs/2026-07-08-eql-v3-prisma-next-design.md`:
- Line 80: Add language identifiers to the fenced code blocks in the spec so
markdownlint MD040 passes: update the TypeScript factory-name block in the
documentation to use the proper ts fence, and update both SQL operator template
blocks to use sql fences. Use the existing fenced-code sections around the
referenced ts and eql_v3 template examples to locate and fix them without
changing the content inside the blocks.

In `@packages/prisma-next/test/derive-schemas.test.ts`:
- Around line 31-50: Add a multi-namespace test in derive-schemas.test.ts that
exercises deriveStackSchemas with two or more namespaces instead of only
__unbound__. Build distinct tables per namespace and include an optional
same-named table to verify the Object.assign flattening behavior and detect any
silent overwrite or merge issues. Use the existing deriveStackSchemas test setup
as the anchor and extend the fixture structure under namespaces to cover this
collision scenario.

In `@packages/prisma-next/test/psl-interpretation-numeric.test.ts`:
- Around line 66-85: The duplicated asStorage helper should be moved into a
shared test utility so all PSL interpretation tests use the same implementation.
Extract the namespace-flattening logic from asStorage into a common helper (for
example in a shared test helpers module) and then update
psl-interpretation-numeric.test.ts, psl-interpretation-other-types.test.ts, and
psl-interpretation.test.ts to import and use it. Keep the helper’s behavior
identical, including flattening storage.namespaces into tables and preserving
types when present.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f4747251-b262-4ac3-9919-038351d14df7

📥 Commits

Reviewing files that changed from the base of the PR and between 8ac66a8 and 85175c2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (68)
  • .changeset/prisma-next-0-14.md
  • docs/superpowers/plans/2026-07-08-eql-v3-prisma-next.md
  • docs/superpowers/specs/2026-07-08-eql-v3-prisma-next-design.md
  • e2e/tests/prisma-example-readme.e2e.test.ts
  • examples/prisma/.env.example
  • examples/prisma/README.md
  • examples/prisma/docker-compose.yml
  • examples/prisma/migrations/app/20260513T1735_initial/end-contract.d.ts
  • examples/prisma/migrations/app/20260513T1735_initial/migration.json
  • examples/prisma/migrations/app/20260513T1735_initial/migration.ts
  • examples/prisma/migrations/app/20260709T1034_initial/end-contract.d.ts
  • examples/prisma/migrations/app/20260709T1034_initial/end-contract.json
  • examples/prisma/migrations/app/20260709T1034_initial/migration.json
  • examples/prisma/migrations/app/20260709T1034_initial/migration.ts
  • examples/prisma/migrations/app/20260709T1034_initial/ops.json
  • examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/contract.json
  • examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/migration.json
  • examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/ops.json
  • examples/prisma/migrations/cipherstash/contract.d.ts
  • examples/prisma/migrations/cipherstash/contract.json
  • examples/prisma/migrations/cipherstash/refs/head.json
  • examples/prisma/package.json
  • examples/prisma/prisma-next.config.ts
  • examples/prisma/src/index.ts
  • examples/prisma/src/prisma/contract.d.ts
  • examples/prisma/src/prisma/contract.json
  • examples/prisma/test/e2e/README.md
  • examples/prisma/test/e2e/bigint.e2e.test.ts
  • examples/prisma/test/e2e/bool.e2e.test.ts
  • examples/prisma/test/e2e/date.e2e.test.ts
  • examples/prisma/test/e2e/global-setup.ts
  • examples/prisma/test/e2e/json.e2e.test.ts
  • examples/prisma/test/e2e/mixed.e2e.test.ts
  • examples/prisma/test/e2e/num.e2e.test.ts
  • examples/prisma/test/e2e/str-range.e2e.test.ts
  • packages/prisma-next/README.md
  • packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts
  • packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json
  • packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json
  • packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts
  • packages/prisma-next/migrations/20260601T0000_install_eql_bundle/ops.json
  • packages/prisma-next/migrations/refs/head.json
  • packages/prisma-next/package.json
  • packages/prisma-next/src/contract.d.ts
  • packages/prisma-next/src/contract.json
  • packages/prisma-next/src/exports/middleware.ts
  • packages/prisma-next/src/exports/migration.ts
  • packages/prisma-next/src/exports/runtime.ts
  • packages/prisma-next/src/middleware/bulk-encrypt.ts
  • packages/prisma-next/src/stack/derive-schemas.ts
  • packages/prisma-next/test/abort.test.ts
  • packages/prisma-next/test/bulk-encrypt-middleware.test.ts
  • packages/prisma-next/test/cipherstash-codec-numeric.test.ts
  • packages/prisma-next/test/cipherstash-codec-other-codecs.test.ts
  • packages/prisma-next/test/cipherstash-codec-string.test.ts
  • packages/prisma-next/test/cipherstash-codec.test.ts
  • packages/prisma-next/test/derive-schemas.test.ts
  • packages/prisma-next/test/descriptor.test.ts
  • packages/prisma-next/test/from-stack-divergence.test.ts
  • packages/prisma-next/test/helpers.test.ts
  • packages/prisma-next/test/operation-types.types.test-d.ts
  • packages/prisma-next/test/operator-lowering-equality.test.ts
  • packages/prisma-next/test/operator-lowering-text-search.test.ts
  • packages/prisma-next/test/operator-lowering.helpers.ts
  • packages/prisma-next/test/operator-lowering.test.ts
  • packages/prisma-next/test/psl-interpretation-numeric.test.ts
  • packages/prisma-next/test/psl-interpretation-other-types.test.ts
  • packages/prisma-next/test/psl-interpretation.test.ts
💤 Files with no reviewable changes (4)
  • examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/contract.json
  • examples/prisma/migrations/app/20260513T1735_initial/end-contract.d.ts
  • examples/prisma/migrations/app/20260513T1735_initial/migration.json
  • examples/prisma/migrations/app/20260513T1735_initial/migration.ts

Copilot AI 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.

Pull request overview

Upgrades the @cipherstash/prisma-next package (and the Prisma example) to Prisma Next 0.14.0, regenerating contract/migration artifacts and adapting runtime/tests/examples to Prisma Next’s newer contract/storage and adapter-lowering shapes (including namespace-qualified ORM access via public).

Changes:

  • Bump all @prisma-next/* deps used by @cipherstash/prisma-next and the Prisma example to 0.14.0, regenerating contract.{json,d.ts} and baseline migration artifacts.
  • Update contract/table handling and tests for the namespace-enveloped storage IR and structured lowered params (LoweredParam).
  • Update example code, docs, and E2E harness to use db.orm.public.* and prisma-next migrate --yes.

Reviewed changes

Copilot reviewed 59 out of 69 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/prisma-next/test/psl-interpretation.test.ts Updates PSL interpretation tests for namespace-enveloped storage IR + contract contracts map.
packages/prisma-next/test/psl-interpretation-other-types.test.ts Same as above for non-string encrypted types.
packages/prisma-next/test/psl-interpretation-numeric.test.ts Same as above for numeric encrypted types.
packages/prisma-next/test/operator-lowering.test.ts Updates operator lowering assertions for structured lowered params.
packages/prisma-next/test/operator-lowering.helpers.ts Switches contract validation + adds literalParamValue helper for LoweredParam.
packages/prisma-next/test/operator-lowering-text-search.test.ts Updates text-search lowering assertions to unwrap literal params.
packages/prisma-next/test/operator-lowering-equality.test.ts Updates equality lowering assertions to unwrap literal params.
packages/prisma-next/test/operation-types.types.test-d.ts Adjusts @ts-expect-error placement to match dtslint expectations.
packages/prisma-next/test/helpers.test.ts Updates helper tests for namespace-enveloped storage + structured params.
packages/prisma-next/test/from-stack-divergence.test.ts Updates divergence tests for namespace-enveloped contract storage shape.
packages/prisma-next/test/descriptor.test.ts Updates descriptor tests for namespace-enveloped storage + canonicalization hooks.
packages/prisma-next/test/derive-schemas.test.ts Updates derive-schema tests for namespace-enveloped storage.
packages/prisma-next/test/cipherstash-codec.test.ts Updates codec hook tests for new SqlStorage/namespace shapes and async op lowering.
packages/prisma-next/test/cipherstash-codec-string.test.ts Adds namespaceId into codec hook test context.
packages/prisma-next/test/cipherstash-codec-other-codecs.test.ts Adds namespaceId into codec hook test contexts for non-string codecs.
packages/prisma-next/test/cipherstash-codec-numeric.test.ts Adds namespaceId into numeric codec hook test contexts.
packages/prisma-next/test/bulk-encrypt-middleware.test.ts Updates middleware ctx construction to include planExecutionId.
packages/prisma-next/test/abort.test.ts Updates middleware ctx construction to include planExecutionId.
packages/prisma-next/src/stack/derive-schemas.ts Updates schema derivation to read tables from storage.namespaces[*].entries.table.
packages/prisma-next/src/middleware/bulk-encrypt.ts Updates AST typing used by the bulk-encrypt routing-key stamping.
packages/prisma-next/src/exports/runtime.ts Updates runtime wiring docs for Postgres runtime construction pattern.
packages/prisma-next/src/exports/migration.ts Updates migration docs for Prisma Next 0.14 Migration instance methods.
packages/prisma-next/src/exports/middleware.ts Updates middleware docs for Postgres runtime construction pattern.
packages/prisma-next/src/contract.json Regenerated extension contract for Prisma Next 0.14 canonical shape.
packages/prisma-next/src/contract.d.ts Regenerated extension contract typings for Prisma Next 0.14 canonical shape.
packages/prisma-next/README.md Updates README examples to use db.orm.public.*.
packages/prisma-next/package.json Pins @prisma-next/* dependencies/devDependencies to 0.14.0.
packages/prisma-next/migrations/refs/head.json Updates baseline migration head hash for regenerated storage hash.
packages/prisma-next/migrations/20260601T0000_install_eql_bundle/ops.json Formatting-only regen/normalization of ops JSON.
packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.ts Updates baseline migration “to” hash to the regenerated storage hash.
packages/prisma-next/migrations/20260601T0000_install_eql_bundle/migration.json Regenerated migration metadata (hash + compacted shape).
packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.json Regenerated end-contract JSON to Prisma Next 0.14 canonical shape.
packages/prisma-next/migrations/20260601T0000_install_eql_bundle/end-contract.d.ts Regenerated end-contract typings to Prisma Next 0.14 canonical shape.
examples/prisma/test/e2e/str-range.e2e.test.ts Updates example E2E tests to use db.orm.public.*.
examples/prisma/test/e2e/README.md Updates harness docs to use prisma-next migrate.
examples/prisma/test/e2e/num.e2e.test.ts Updates example E2E tests to use db.orm.public.*.
examples/prisma/test/e2e/mixed.e2e.test.ts Updates example E2E tests to use db.orm.public.*.
examples/prisma/test/e2e/json.e2e.test.ts Updates example E2E tests to use db.orm.public.* / db.sql.public.*.
examples/prisma/test/e2e/global-setup.ts Improves harness credential detection and switches to prisma-next migrate --yes.
examples/prisma/test/e2e/date.e2e.test.ts Updates example E2E tests to use db.orm.public.*.
examples/prisma/test/e2e/bool.e2e.test.ts Updates example E2E tests to use db.orm.public.*.
examples/prisma/test/e2e/bigint.e2e.test.ts Updates example E2E tests to use db.orm.public.*.
examples/prisma/src/prisma/contract.json Regenerated example app contract JSON to Prisma Next 0.14 canonical shape.
examples/prisma/src/prisma/contract.d.ts Regenerated example app contract typings to Prisma Next 0.14 canonical shape.
examples/prisma/src/index.ts Updates example runtime code to use db.orm.public.*.
examples/prisma/README.md Updates ports and migration command docs for Prisma Next 0.14 workflow.
examples/prisma/prisma-next.config.ts Updates prismaContract target arg to use the target pack.
examples/prisma/package.json Pins example dependencies to Prisma Next 0.14 and switches to prisma-next migrate --yes.
examples/prisma/migrations/cipherstash/refs/head.json Updates baseline ref head hash (formatting + new hash).
examples/prisma/migrations/cipherstash/contract.json Regenerated/minified baseline contract JSON in new canonical shape.
examples/prisma/migrations/cipherstash/contract.d.ts Updates guidance comment for contract consumption under 0.14.
examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/ops.json Formatting-only regen/normalization of ops JSON.
examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/migration.json Regenerated migration metadata (hash + compacted shape).
examples/prisma/migrations/cipherstash/20260601T0000_install_eql_bundle/contract.json Removes older per-migration embedded contract (deleted).
examples/prisma/migrations/app/20260709T1034_initial/ops.json Regenerates migration checks to use parameterized SQL.
examples/prisma/migrations/app/20260709T1034_initial/migration.ts New app migration using Prisma Next 0.14 migration APIs.
examples/prisma/migrations/app/20260709T1034_initial/migration.json New compact migration metadata for the regenerated app migration.
examples/prisma/migrations/app/20260709T1034_initial/end-contract.json Regenerated end-contract JSON to Prisma Next 0.14 canonical shape.
examples/prisma/migrations/app/20260709T1034_initial/end-contract.d.ts New regenerated end-contract typings to Prisma Next 0.14 canonical shape.
examples/prisma/migrations/app/20260513T1735_initial/migration.ts Removes older app migration using deprecated factory APIs (deleted).
examples/prisma/migrations/app/20260513T1735_initial/migration.json Removes older verbose migration JSON (deleted).
examples/prisma/migrations/app/20260513T1735_initial/end-contract.d.ts Removes older end-contract typings (deleted).
examples/prisma/docker-compose.yml Updates example Postgres port to 54338 and aligns docs.
examples/prisma/.env.example Updates DATABASE_URL to match new example Postgres port.
e2e/tests/prisma-example-readme.e2e.test.ts Improves auth detection (auth.json/device.json) and preserves .env across README walkthrough.
.changeset/prisma-next-0-14.md Adds changeset documenting the Prisma Next 0.14 upgrade and key behavior changes.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if (!namespaces) return []
const tables: Record<string, StorageTableView> = {}
for (const namespace of Object.values(namespaces)) {
Object.assign(tables, namespace.entries?.table)
}
const tables: StorageView['tables'] = {}
for (const ns of Object.values(s.namespaces ?? {})) {
Object.assign(tables, ns.entries?.table)
}
const tables: StorageView['tables'] = {}
for (const ns of Object.values(s.namespaces ?? {})) {
Object.assign(tables, ns.entries?.table)
}
const tables: StorageView['tables'] = {}
for (const ns of Object.values(s.namespaces ?? {})) {
Object.assign(tables, ns.entries?.table)
// CipherStash profile written by `stash auth login` (device-code flow;
// `~/.cipherstash`). The stack client resolves both — this gate only
// exists to fail fast with an actionable message when neither is present.
const hasProfile = existsSync(join(homedir(), '.cipherstash'))
@coderdan

coderdan commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Code review: Prisma Next 0.14 upgrade

Reviewed the hand-written source in this branch (generated contracts, migrations, and the lockfile excluded). 15 findings below, most severe first.

A note on method: I verified the 0.14 API claims against the published tarballs rather than the working tree, because node_modules here still resolves @prisma-next/* at 0.8.0 until someone runs pnpm install. That mattered — it refuted two plausible-looking findings (PostgresRuntimeImpl is publicly exported from @prisma-next/postgres/runtime; the widened bulk-encrypt value type fails loudly, not silently, via collectTargets) and confirmed a third (migration apply really is gone from the 0.14 CLI).


1. deriveStackSchemas silently drops encrypted tables on a cross-namespace name collision

packages/prisma-next/src/stack/derive-schemas.ts:103

0.14's namespace envelope makes public.users and audit.users representable for the first time. Object.assign(tables, namespace.entries?.table) flattens them back into one bare-name map — last namespace wins, the other's EncryptedTable is dropped with no error.

This doesn't fail loudly downstream, which is what makes it serious. sdk-adapter.ts (registry.set(table.tableName) / lookup()) and bulk-encrypt.ts (routing from ast.table.name) are also bare-name keyed, so an insert into the dropped table still resolves — to the surviving table's entry — and encrypts under the wrong index config. That is precisely the "SDK index set silently disagrees with the installed EQL configuration" footgun this module's docstring says it exists to eliminate.

0.14's TableSource carries namespaceId, so the fix has somewhere to go: key on (namespace, table), or reject a bare-name collision loudly.

2. exports/migration.ts doc sample is half-migrated to 0.14

packages/prisma-next/src/exports/migration.ts:19

The call form moved to this.createTable({...}), but the column literals still use the pre-0.14 keys:

{ name: 'email', typeSql: 'eql_v2_encrypted', defaultSql: '', nullable: false }

0.14's DdlColumn (verified in @prisma-next/sql-relational-core@0.14.0) is { name; type; notNull?; primaryKey?; default?: AnyDdlColumnDefault; codecRef? }. The sample is missing the required type, carries three excess properties, and passes defaultSql: '' (a string) where default expects a structured AnyDdlColumnDefault. Copying it yields TS2739/TS2353.

This PR's own generated migration gets it right (examples/prisma/migrations/app/20260709T1034_initial/migration.ts:19 uses col('email', 'eql_v2_encrypted', { notNull: true, codecRef: {...} })), so the package's public guidance now contradicts its own emitted code.

3. packages/prisma-next/README.md:68 still documents the removed migration apply

I enumerated the 0.14 CLI's help strings: migration plan | check | list | new | show | status | graph | log, plus a top-level migrate. There is no migration apply.

This README's ORM examples were updated to db.orm.public.User, but line 68's npx prisma-next migration apply was left behind — while the example's own package.json script was changed to prisma-next migrate --yes in this same PR. A user following the quickstart hits an unknown-subcommand error and never installs the EQL bundle.

4. stash init prints the removed command

packages/cli/src/commands/init/steps/install-eql.ts:40 (and the doc comment at :33)

Skipping stash eql install — Prisma Next installs the EQL bundle via prisma-next migration apply

Same root cause as #3, in a file this PR otherwise edits. Two more copies live outside the diff and share it:

  • packages/cli/src/commands/init/providers/prisma-next.ts:20 emits ${prismaNext} migration plan && ${prismaNext} migration apply
  • packages/cli/src/commands/init/providers/__tests__/prisma-next.test.ts:16 asserts toContain('migration apply') — so CI stays green while shipping the broken instruction

5. .env in TRANSIENT_PATHS widens the window where an interrupted run destroys a real credentials file

e2e/tests/prisma-example-readme.e2e.test.ts:79

beforeAll snapshots, then calls wipeTransientOutputs(), which now rmSync-deletes examples/prisma/.env immediately — before any walkthrough step. The only copy lives in an OS-tmpdir mkdtemp snapshot restored by afterAll.

Previously .env wasn't in TRANSIENT_PATHS, so it survived untouched until the walkthrough's own cp .env.example .env overwrote it partway through. Now any interruption across the whole 600s beforeAll budget — Ctrl-C, a vitest hook timeout, a docker compose up / pnpm install failure that kills the process, OOM — leaves the developer's .env deleted and unrestored. The exposed user is exactly the one who un-skips the suite: authConfigured true via ~/.cipherstash, plus a real local .env.

Related, and pre-existing: snapDir is declared let snapDir: string with no initializer, so if snapshotTransientOutputs() itself throws, afterAll calls restoreTransientOutputs(undefined) and join(undefined, ...) throws ERR_INVALID_ARG_TYPE, masking the original error.

6. The credential fail-fast gate is effectively dead

examples/prisma/test/e2e/global-setup.ts:82

existsSync(join(homedir(), '.cipherstash')) is true on any machine that has ever run the stash CLI — that directory also holds workspaces/, config, and logs. A developer with no CS_* env vars and no completed login passes the gate, so setup proceeds to prisma-next migrate --yes and live SDK round-trips, dying deep with an opaque auth error rather than the actionable message the gate exists to print.

The sibling probe in e2e/tests/prisma-example-readme.e2e.test.ts:26 gets this right by testing for the specific auth.json / device.json files. The two checks should agree.

7. __proto__ table key hits the prototype setter

packages/prisma-next/src/stack/derive-schemas.ts:101

The flatten target is a plain {}. contract.json is loaded with JSON.parse, which materialises "__proto__" as an own enumerable data property; Object.assign then performs [[Set]], invoking Object.prototype's __proto__ accessor. The table is silently dropped and the map's prototype is mutated. The old code read storage.tables directly, where Object.entries would have surfaced such a key normally — the merge is what introduces this.

Reachability is low (needs a Postgres table literally named __proto__), but Object.create(null) costs nothing and this is a crypto-adjacent module.

8. A Linear issue ID is committed to a public repo

docs/superpowers/plans/2026-07-08-eql-v3-prisma-next.md:48

The parenthetical at the end of that line contains an internal Linear issue code. (Not quoting it here, for the same reason.) CLAUDE.md:134: "Skills must not contain Linear issue IDs; they're public. GitHub issue numbers are fine." It's the only Linear-style code among the diff's added lines. Replace with a GitHub issue number or drop it.

9–10. Two files no longer pass the formatter

  • examples/prisma/src/index.ts:121 and :215 — the db.orm.public.User rename pushed both past the width limit (87 and 85 chars)
  • examples/prisma/test/e2e/global-setup.ts:122 — shortening the spawn args left a multi-line call biome now wants collapsed

I confirmed main's versions of both files format clean, so this branch introduces the drift. Biome isn't wired into CI (only a local code:fix script), so nothing breaks — it's one pnpm code:fix away.

11. derive-schemas.test.ts has zero coverage for the collision in #1

packages/prisma-next/test/derive-schemas.test.ts:31

makeContract hardcodes a single __unbound__ namespace, and none of the 8 tests exercises two namespaces or a cross-namespace table-name collision. The silent overwrite ships green. A fixture like { public: { entries: { table: { users: {...} } } }, audit: { entries: { table: { users: {...} } } } } would pin whichever behaviour you choose.

12. descriptor.test.ts no longer asserts namespace placement

packages/prisma-next/test/descriptor.test.ts:58

The rewrite flattens with Object.values(namespaces).flatMap(...) and asserts only table names. If the descriptor regresses and files eql_v2_configuration under the wrong namespace (e.g. __unbound__ rather than the target-owned public the surrounding comment claims), the assertion still passes. Namespace placement is exactly what 0.14 introduced and this PR migrates to — worth pinning.

13. asStorage is copy-pasted into three test files

packages/prisma-next/test/psl-interpretation-other-types.test.ts:70, plus psl-interpretation.test.ts:86 and psl-interpretation-numeric.test.ts:69

An identical 16-line asStorage + StorageView shim, each re-deriving the production Object.assign(tables, ns.entries?.table) flatten. The next contract-shape change forces three lockstep edits, and each copy independently discards namespace information from its assertions. test/operator-lowering.helpers.ts already exists as the natural home.

14. Non-interactive stash init installs EQL into the database with no confirmation

packages/cli/src/commands/init/steps/install-eql.ts:53

With no TTY (CI, an agent, a pipe), proceed is hardcoded true and installCommand({ scaffoldConfig: 'ensure' }) runs against whatever state.databaseUrl resolved to. If that picks up a staging or production DATABASE_URL from the ambient environment, stash init mutates it unprompted.

install-eql.test.ts:58 asserts p.confirm is not called and installCommand is called once, with no counterpart test for an opt-out path — so nothing guards the destructive default. Contrast build-schema.ts, whose non-interactive default is the safe one (keep the existing file). An explicit --yes / --no-eql flag would make this opt-in.

15. Three init steps hand-roll the same non-interactive branch

packages/cli/src/commands/init/steps/install-deps.ts:65

install-deps.ts:65-73 and install-eql.ts:51-59 are near-identical, and build-schema.ts:96-121 repeats the shape around a p.select. config/tty.ts exposes only the isInteractive() primitive.

The copies have already drifted in the way that matters: build-schema defaults to the safe choice, install-eql to the destructive one (#14). A single confirmOrDefault({ message, initialValue, nonInteractiveDefault, note }) in config/tty.ts would collapse both confirm sites and force the default to be named explicitly at each call.


🤖 Generated with Claude Code

@calvinbrewer calvinbrewer merged commit e9e81e6 into main Jul 9, 2026
12 checks passed
@calvinbrewer calvinbrewer deleted the chore/prisma-next-0.14-upgrade branch July 9, 2026 14:30
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.

4 participants