Skip to content

tml-2912: PSL native scalar lists (author, migrate, infer end-to-end)#870

Open
SevInf wants to merge 11 commits into
mainfrom
scalar-lists-slice2
Open

tml-2912: PSL native scalar lists (author, migrate, infer end-to-end)#870
SevInf wants to merge 11 commits into
mainfrom
scalar-lists-slice2

Conversation

@SevInf

@SevInf SevInf commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

tml-2912 — PSL native scalar lists (slice 2 of native-scalar-arrays)

Flips the implicit gate slice 1 built behind: a user can now author SQL scalar lists in PSL (tags String[]) and get them end to end — native Postgres array columns (text[]), element-wise codec round-trip, authoring diagnostics, capability gating, and the headline posts.tags String[] milestone checkpoint (project AC1).

Stacked on slice 1 (#846, merged). The load-bearing change is one line in the interpreter — replacing the JSONB fallback with the element descriptor + many: true; the rest is making the authoring layer correct about lists.

Dispatches

  • D1 — interpreter flip: PSL scalar lists lower to native array storage columns (the JSONB fallback turned out to live in 3 coordinated sites, all removed).
  • D1b — make StorageColumnTypes/StorageColumnInputTypes many-aware (the insert/select builders consume the storage maps; only the domain maps were wrapped before).
  • D2 — reject incoherent list constructs (execution default / @id / autoincrement on a list) with actionable diagnostics.
  • D3 — authoring-time capability gating: a list field against a target whose adapter doesn't report scalarList (SQLite) is rejected; threads a merged CapabilityMatrix to the interpreter via the existing mergeCapabilityMatrices.
  • D4 — element-non-null CHECK (array_position(col, NULL) IS NULL) (enforce-on-all) + array-typed @default([])/@default([...]) validation; rejects a scalar default on a list.
  • D5 — end-to-end milestone (AC1 author→migrate→infer round-trip) + element fidelity (DateTime[]/Bytes[]/Decimal[]) + Mongo parity (NFR4).

Acceptance criteria

AC1 (milestone round-trip), AC6 (FR3 diagnostics), AC8 PSL-half (non-null-element CHECK), AC9 (array defaults), AC10 (capability gating), AC2/NFR1 (element fidelity), NFR4 (Mongo parity), NFR2 (no scalar regression — fixtures:check byte-clean). 9/9 PASS.

Notes for reviewers

  • Element nullability is enforce-on-all. PSL has no T?[] (element-nullable) syntax, so every PSL-authored list column carries the non-null-element CHECK; no elementNullable IR flag was needed.
  • CHECK is emitted as DDL, not via ADR-156. ADR-156 check constraints are value-set/IN(...)-only and can't express array_position(...), so D4 added a small CheckExpressionConstraint DDL node (<table>_<col>_elem_not_null).
  • Known limitation (carried to slice 3): opt-in --strict verify would false-flag the element-non-null CHECK as drift, because introspection doesn't parse it back. Default verify is non-strict and unaffected; the AC1 infer round-trip is clean (text[]String[]).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for scalar list fields across authoring, schema generation, migrations, and contract emission.
    • Scalar lists now use native array storage where supported, with updated typing and round-trip behavior.
    • Improved check-constraint handling to support both value-set and expression-based validations.
  • Bug Fixes

    • Fixed list default parsing, including quoted values and special characters.
    • Improved validation for unsupported list features and stricter schema verification for array fields.

@SevInf SevInf requested a review from a team as a code owner June 25, 2026 13:17
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR threads a CapabilityMatrix through the control stack, CLI emit, and PSL authoring pipeline to gate scalar-list (String[], etc.) fields against target/adapter support, lowering supported list fields to native array storage instead of JSONB. It also refactors SQL CHECK constraints into a discriminated valueSet/expression IR, adds element non-null CHECK enforcement for array columns, updates Postgres migration/introspection to the new payload shape, and regenerates a downstream contract.

Changes

Capability-gated native scalar-list support

Layer / File(s) Summary
Capability matrix type and control-stack/CLI wiring
packages/1-framework/1-core/framework-components/src/shared/capabilities.ts, .../exports/components.ts, .../control/control-stack.ts, packages/1-framework/1-core/config/src/contract-source-types.ts, packages/1-framework/3-tooling/cli/src/control-api/client.ts, .../operations/contract-emit.ts
Exports CapabilityMatrix, builds it on ControlStack via mergeCapabilityMatrices, adds a required capabilities field to ContractSourceContext, and passes stack.capabilities into CLI emit source contexts.
PSL interpreter/field-resolution capability gating and list defaults
packages/2-sql/2-authoring/contract-psl/src/interpreter.ts, .../provider.ts, .../psl-field-resolution.ts, .../psl-attribute-parsing.ts, .../psl-column-resolution.ts, packages/1-framework/2-authoring/psl-parser/src/resolve.ts, .../test/interpreter.diagnostics.test.ts, .../test/interpreter.capability-gating.test.ts
Gates scalar-list fields on sql.scalarList, rejects list fields with execution defaults or @id, parses array-literal @default expressions, preserves attribute argument expression ASTs, and adds diagnostics tests.
Test suite capability wiring
packages/2-sql/2-authoring/contract-psl/test/*, packages/2-mongo-family/**/test/*, packages/3-extensions/postgres/test/*, packages/3-targets/6-adapters/postgres/test/migrations/*, test/integration/test/**
Updates existing interpreter/config/RLS/namespace/integration test call sites to pass an explicit capabilities object.
Contract-ts and emitter array-column typing
packages/2-sql/2-authoring/contract-ts/src/build-contract.ts, packages/2-sql/3-tooling/emitter/src/index.ts, and their tests
Extends encodeColumnDefault/buildStorageColumn for list defaults and many: true, wraps emitted column types in ReadonlyArray.
Postgres array-literal default parsing rework
packages/3-targets/3-targets/postgres/src/core/default-normalizer.ts, its test
Replaces comma-splitting parsing with a quote-aware tokenizer handling escapes and doubled quotes.
Integration test helpers and end-to-end scenarios
test/integration/test/scalar-lists/*
Adds shared SQL/Mongo PSL authoring helpers and integration tests for scalar-list parity and Postgres migration round-trips.

CHECK constraint polymorphism (valueSet vs expression)

Layer / File(s) Summary
SqlCheckConstraintIR discriminated union
packages/2-sql/1-core/schema-ir/src/ir/sql-check-constraint-ir.ts, .../sql-table-ir.ts, .../types.ts, .../exports/types.ts
Replaces the concrete IR class with an abstract base plus SqlValueSetCheckIR/SqlExpressionCheckIR and a sqlCheckConstraintIR factory.
Contract check projection and element non-null synthesis
packages/2-sql/9-family/src/core/migrations/contract-to-schema-ir.ts, packages/3-targets/3-targets/postgres/src/core/migrations/issue-planner.ts
Adds projectContractChecks and element non-null helpers, synthesizing expression checks for many: true columns and removing them from inline CREATE TABLE constraints.
Schema verification kind-aware equality
packages/2-sql/9-family/src/core/schema-verify/*, packages/2-sql/9-family/src/exports/*, its test
Adds checkContentEqual, retypes verifyCheckConstraints, and routes verification through projectContractChecks.
Postgres AddCheckConstraintPayload plumbing
packages/3-targets/3-targets/postgres/src/core/migrations/op-factory-call.ts, .../operations/constraints.ts, .../postgres-migration.ts, .../schema-ir/postgres-table-ir.ts, .../planner-strategies.ts, tests
Introduces discriminated payload rendering and planning for CHECK constraints.
Postgres introspection parsing
packages/3-targets/6-adapters/postgres/src/core/control-adapter.ts, its test
Updates parseCheckConstraintDef to return a kind-discriminated result including canonicalized element non-null expressions.
End-to-end verification and migration payload updates
packages/3-targets/6-adapters/postgres/test/migrations/native-array-columns.integration.test.ts, examples/prisma-next-demo/migrations/app/20260422T0720_initial/migration.ts, skills/upgrade/.../instructions.md
Adds integration tests for element non-null enforcement and strict verification, updates example migration and upgrade docs to the new payload shape.

Telemetry-backend contract regeneration

Layer / File(s) Summary
Regenerated telemetry contract artifacts
apps/telemetry-backend/src/prisma/contract.d.ts, contract.json
Updates flags/extensions column codecs from pg/jsonb to pg/text and refreshes the storage hash.

Estimated code review effort: 4 (Complex) | ~75 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ControlStack
  participant CliClient
  participant PslInterpreter
  participant FieldResolution

  ControlStack->>ControlStack: mergeCapabilityMatrices(target, adapter, extensionPacks)
  CliClient->>PslInterpreter: source.load({ capabilities: stack.capabilities })
  PslInterpreter->>FieldResolution: collectResolvedFields({ capabilities })
  FieldResolution-->>PslInterpreter: ok (native array field) or PSL_SCALAR_LIST_UNSUPPORTED_TARGET
Loading
sequenceDiagram
  participant PlannerStrategies
  participant AddCheckConstraintCall
  participant ConstraintsOp
  participant Postgres

  PlannerStrategies->>PlannerStrategies: projectContractChecks + checkPayload
  PlannerStrategies->>AddCheckConstraintCall: new AddCheckConstraintCall(payload)
  AddCheckConstraintCall->>ConstraintsOp: addCheckConstraint(payload)
  ConstraintsOp->>Postgres: ALTER TABLE ... ADD CONSTRAINT ... CHECK(predicate)
Loading

Possibly related PRs

  • prisma/prisma-next#574: Shares the CapabilityMatrix/mergeCapabilityMatrices infrastructure now propagated via ControlStack/ContractSourceContext for sql.scalarList gating.
  • prisma/prisma-next#755: Modifies the same Postgres CHECK-constraint pipeline (IR/types, parseCheckConstraintDef, planner/verifier), which this PR extends with the valueSet/expression discriminated payload.
  • prisma/prisma-next#829: Touches the same demo migration addCheckConstraint/user_kind_check usage that this PR updates to the new payload shape.

Suggested reviewers: wmadden, aqrln

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.58% 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 matches the main change: end-to-end PSL native scalar lists across authoring, migration, and inference.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch scalar-lists-slice2

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.

@pkg-pr-new

pkg-pr-new Bot commented Jun 25, 2026

Copy link
Copy Markdown

Open in StackBlitz

@prisma-next/extension-author-tools

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

@prisma-next/mongo-runtime

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

@prisma-next/family-mongo

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

@prisma-next/sql-runtime

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

@prisma-next/family-sql

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

@prisma-next/extension-arktype-json

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

@prisma-next/middleware-cache

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

@prisma-next/mongo

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

@prisma-next/extension-paradedb

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

@prisma-next/extension-pgvector

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

@prisma-next/extension-postgis

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

@prisma-next/postgres

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

@prisma-next/sql-orm-client

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

@prisma-next/sqlite

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

@prisma-next/extension-supabase

npm i https://pkg.pr.new/@prisma-next/extension-supabase@870

@prisma-next/target-mongo

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

@prisma-next/adapter-mongo

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

@prisma-next/driver-mongo

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

@prisma-next/contract

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

@prisma-next/utils

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

@prisma-next/config

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

@prisma-next/errors

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

@prisma-next/framework-components

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

@prisma-next/operations

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

@prisma-next/ts-render

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

@prisma-next/contract-authoring

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

@prisma-next/ids

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

@prisma-next/psl-parser

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

@prisma-next/psl-printer

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

@prisma-next/cli

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

@prisma-next/cli-telemetry

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

@prisma-next/config-loader

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

@prisma-next/emitter

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

@prisma-next/language-server

npm i https://pkg.pr.new/@prisma-next/language-server@870

@prisma-next/migration-tools

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

prisma-next

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

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

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

@prisma-next/mongo-codec

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

@prisma-next/mongo-contract

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

@prisma-next/mongo-value

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

@prisma-next/mongo-contract-psl

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

@prisma-next/mongo-contract-ts

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

@prisma-next/mongo-emitter

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

@prisma-next/mongo-schema-ir

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

@prisma-next/mongo-query-ast

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

@prisma-next/mongo-orm

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

@prisma-next/mongo-query-builder

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

@prisma-next/mongo-lowering

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

@prisma-next/mongo-wire

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

@prisma-next/sql-contract

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

@prisma-next/sql-errors

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

@prisma-next/sql-operations

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

@prisma-next/sql-schema-ir

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

@prisma-next/sql-contract-psl

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

@prisma-next/sql-contract-ts

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

@prisma-next/sql-contract-emitter

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

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

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

@prisma-next/sql-relational-core

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

@prisma-next/sql-builder

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

@prisma-next/target-postgres

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

@prisma-next/target-sqlite

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

@prisma-next/adapter-postgres

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

@prisma-next/adapter-sqlite

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

@prisma-next/driver-postgres

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

@prisma-next/driver-sqlite

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

commit: d4c5290

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

size-limit report 📦

Path Size
postgres / no-emit 160.48 KB (+0.18% 🔺)
postgres / emit 147.57 KB (+0.13% 🔺)
mongo / no-emit 96.52 KB (0%)
mongo / emit 89.23 KB (0%)
cf-worker / no-emit 188.58 KB (+0.18% 🔺)
cf-worker / emit 173.87 KB (+0.11% 🔺)

...ifDefined(
'many',
resolvedField.many && resolvedField.valueObjectTypeName === undefined
? (true as const)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Wtf is this? Why not just many: Boolean(resolvedField.many && resolvedField.valueObjectTypeName === undefined)?

Comment thread packages/2-sql/2-authoring/contract-psl/src/psl-column-resolution.ts Outdated
@SevInf SevInf force-pushed the scalar-lists-slice2 branch from 80a4be0 to 0760919 Compare July 1, 2026 09:01
Comment thread packages/2-sql/2-authoring/contract-psl/src/psl-column-resolution.ts Outdated
Comment thread packages/2-sql/2-authoring/contract-psl/src/psl-field-resolution.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
test/integration/test/scalar-lists/psl-list-roundtrip.integration.test.ts (2)

1-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Milestone acceptance-criteria codes (AC1, AC2/NFR1, NFR1) in comments/test titles.

Same doc-maintenance concern as the Mongo-parity test: these acceptance-criteria labels are transient and will become stale after the milestone closes.

Based on learnings, the doc-maintenance rule "violations are limited to transient artifacts such as... milestone acceptance-criteria codes like AM12 / AC-13" — AC1/AC2/NFR1 match this pattern and should be flagged.

Also applies to: 116-116, 185-185

🤖 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 `@test/integration/test/scalar-lists/psl-list-roundtrip.integration.test.ts`
around lines 1 - 15, Remove the transient acceptance-criteria labels from the
`psl-list-roundtrip.integration.test.ts` documentation and test titles,
including the `AC1`, `AC2/NFR1`, and `NFR1` references in the top comment. Keep
the description focused on the scalar-list roundtrip behavior itself, and update
any related comments or titles in this test file so they no longer mention
milestone-specific codes.

Source: Learnings


131-136: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer separate expect() calls over multi-field toMatchObject().

Several assertions bundle multiple fields into a single toMatchObject() (e.g. codecId/nativeType/many at 131-135, name/typeName/list/optional at 171-176, and the three list-column checks at 209-220). A failure only reports the object diff rather than pinpointing which field broke. Line 136's separate nativeType check is also redundant once line 133 already asserts nativeType: 'text'.

Based on learnings, "prefer separate expect() assertions for each field instead of combining checks with toMatchObject() when validating multiple fields" for clearer, more actionable failure messages.

♻️ Example for lines 131-136
-      expect(tagsColumn).toMatchObject({
-        codecId: 'pg/text@1',
-        nativeType: 'text',
-        many: true,
-      });
-      expect(tagsColumn?.['nativeType']).not.toBe('jsonb');
+      expect(tagsColumn?.['codecId']).toBe('pg/text@1');
+      expect(tagsColumn?.['nativeType']).toBe('text');
+      expect(tagsColumn?.['many']).toBe(true);

Also applies to: 171-176, 209-220

🤖 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 `@test/integration/test/scalar-lists/psl-list-roundtrip.integration.test.ts`
around lines 131 - 136, The scalar list roundtrip test uses multi-field
toMatchObject() assertions in the affected blocks, which makes failures harder
to pinpoint; split these into separate expect() calls for each field in the
relevant test cases. Update the assertions around tagsColumn and the other
list-column checks to validate codecId, nativeType, many, name, typeName, list,
and optional individually, and remove the redundant nativeType-only check where
the same field is already asserted in the object match.

Source: Learnings

test/integration/test/scalar-lists/psl-list-mongo-parity.integration.test.ts (1)

1-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Milestone acceptance-criteria codes (NFR4, AC2) in comments.

These are transient acceptance-criteria references rather than stable identifiers (like TML-* tickets or ADR references).

Based on learnings, the doc-maintenance rule flags "milestone acceptance-criteria codes like AM12 / AC-13" while exempting stable TML-*/ADR references — NFR4/AC2 fall into the flagged category.

🤖 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 `@test/integration/test/scalar-lists/psl-list-mongo-parity.integration.test.ts`
around lines 1 - 18, The top-of-file comment in
psl-list-mongo-parity.integration.test.ts uses transient milestone
acceptance-criteria codes (`NFR4`, `AC2`) that should not appear in
docs/comments. Update the comment in this test file to remove those codes and
replace them with stable references only, keeping the rest of the parity
explanation intact.

Source: Learnings

🤖 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
`@test/integration/test/scalar-lists/psl-list-mongo-parity.integration.test.ts`:
- Around line 1-18: The top-of-file comment in
psl-list-mongo-parity.integration.test.ts uses transient milestone
acceptance-criteria codes (`NFR4`, `AC2`) that should not appear in
docs/comments. Update the comment in this test file to remove those codes and
replace them with stable references only, keeping the rest of the parity
explanation intact.

In `@test/integration/test/scalar-lists/psl-list-roundtrip.integration.test.ts`:
- Around line 1-15: Remove the transient acceptance-criteria labels from the
`psl-list-roundtrip.integration.test.ts` documentation and test titles,
including the `AC1`, `AC2/NFR1`, and `NFR1` references in the top comment. Keep
the description focused on the scalar-list roundtrip behavior itself, and update
any related comments or titles in this test file so they no longer mention
milestone-specific codes.
- Around line 131-136: The scalar list roundtrip test uses multi-field
toMatchObject() assertions in the affected blocks, which makes failures harder
to pinpoint; split these into separate expect() calls for each field in the
relevant test cases. Update the assertions around tagsColumn and the other
list-column checks to validate codecId, nativeType, many, name, typeName, list,
and optional individually, and remove the redundant nativeType-only check where
the same field is already asserted in the object match.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: eccfbb64-0cee-4b45-9564-37d381af9a13

📥 Commits

Reviewing files that changed from the base of the PR and between d987376 and 0760919.

📒 Files selected for processing (34)
  • apps/telemetry-backend/src/prisma/contract.d.ts
  • apps/telemetry-backend/src/prisma/contract.json
  • packages/1-framework/1-core/config/src/contract-source-types.ts
  • packages/1-framework/1-core/framework-components/src/control/control-stack.ts
  • packages/1-framework/1-core/framework-components/src/exports/components.ts
  • packages/1-framework/1-core/framework-components/src/shared/capabilities.ts
  • packages/1-framework/3-tooling/cli/src/control-api/client.ts
  • packages/1-framework/3-tooling/cli/src/control-api/operations/contract-emit.ts
  • packages/2-sql/2-authoring/contract-psl/src/interpreter.ts
  • packages/2-sql/2-authoring/contract-psl/src/provider.ts
  • packages/2-sql/2-authoring/contract-psl/src/psl-column-resolution.ts
  • packages/2-sql/2-authoring/contract-psl/src/psl-field-resolution.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.capability-gating.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.diagnostics.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.value-objects.test.ts
  • packages/2-sql/2-authoring/contract-ts/src/build-contract.ts
  • packages/2-sql/2-authoring/contract-ts/test/scalar-list-output-types.test-d.ts
  • packages/2-sql/3-tooling/emitter/src/index.ts
  • packages/2-sql/3-tooling/emitter/test/emitter-hook.storage-column-types.test.ts
  • packages/2-sql/4-lanes/relational-core/src/ast/ddl-types.ts
  • packages/2-sql/4-lanes/relational-core/src/contract-free/column.ts
  • packages/2-sql/4-lanes/relational-core/src/exports/contract-free.ts
  • packages/3-targets/3-targets/postgres/src/core/default-normalizer.ts
  • packages/3-targets/3-targets/postgres/src/core/migrations/issue-planner.ts
  • packages/3-targets/3-targets/postgres/src/core/migrations/op-factory-call.ts
  • packages/3-targets/3-targets/postgres/src/exports/migration.ts
  • packages/3-targets/3-targets/postgres/test/default-normalizer.test.ts
  • packages/3-targets/3-targets/sqlite/src/core/migrations/op-factory-call.ts
  • packages/3-targets/6-adapters/postgres/src/core/control-adapter.ts
  • packages/3-targets/6-adapters/postgres/test/migrations/native-array-columns.integration.test.ts
  • packages/3-targets/6-adapters/sqlite/src/core/control-adapter.ts
  • test/integration/test/scalar-lists/psl-list-authoring.ts
  • test/integration/test/scalar-lists/psl-list-mongo-parity.integration.test.ts
  • test/integration/test/scalar-lists/psl-list-roundtrip.integration.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/2-sql/2-authoring/contract-psl/test/interpreter.relations.test.ts (1)

15-21: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider centralizing the capabilities literal.

capabilities: { sql: { scalarList: true } } is now duplicated verbatim across a large number of test files in this package (per cross-file evidence: interpreter.extensions.test.ts, interpreter.enum.test.ts, interpreter.types.test.ts, interpreter.namespaces.test.ts, ts-psl-parity.test.ts, psl-ts-namespace-parity.test.ts, interpreter.control-policy.test.ts, etc.). Since contract-psl/test/fixtures.ts already exists as a shared test fixtures module, extracting a single exported constant (e.g., DEFAULT_SCALAR_LIST_CAPABILITIES) there and importing it would reduce copy/paste risk if the capability shape changes again.

🤖 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/2-sql/2-authoring/contract-psl/test/interpreter.relations.test.ts`
around lines 15 - 21, Centralize the repeated sql scalar-list capability literal
used in the contract-psl tests. Move the shared object from the local test setup
in interpreter.relations.test.ts into the existing shared fixtures module
contract-psl/test/fixtures.ts as a named exported constant such as
DEFAULT_SCALAR_LIST_CAPABILITIES, then import and reuse it from baseInput and
the other affected interpreter/parity tests. Keep the shape identical so callers
still pass the same capabilities value, but remove the duplicate inline literal
everywhere.
🤖 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 `@packages/2-sql/2-authoring/contract-psl/test/interpreter.relations.test.ts`:
- Around line 15-21: Centralize the repeated sql scalar-list capability literal
used in the contract-psl tests. Move the shared object from the local test setup
in interpreter.relations.test.ts into the existing shared fixtures module
contract-psl/test/fixtures.ts as a named exported constant such as
DEFAULT_SCALAR_LIST_CAPABILITIES, then import and reuse it from baseInput and
the other affected interpreter/parity tests. Keep the shape identical so callers
still pass the same capabilities value, but remove the duplicate inline literal
everywhere.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: c97aceb4-5845-4be6-9105-85894eab5086

📥 Commits

Reviewing files that changed from the base of the PR and between 0760919 and 19f0149.

📒 Files selected for processing (46)
  • packages/1-framework/1-core/config/src/contract-source-types.ts
  • packages/1-framework/2-authoring/psl-parser/src/exports/index.ts
  • packages/1-framework/2-authoring/psl-parser/src/resolve.ts
  • packages/1-framework/2-authoring/psl-parser/src/symbol-table.ts
  • packages/1-framework/3-tooling/cli/test/config-types.test.ts
  • packages/2-mongo-family/2-authoring/contract-psl/test/provider.test.ts
  • packages/2-mongo-family/2-authoring/contract-ts/test/config-types.test.ts
  • packages/2-sql/2-authoring/contract-psl/src/interpreter.ts
  • packages/2-sql/2-authoring/contract-psl/src/provider.ts
  • packages/2-sql/2-authoring/contract-psl/src/psl-attribute-parsing.ts
  • packages/2-sql/2-authoring/contract-psl/src/psl-column-resolution.ts
  • packages/2-sql/2-authoring/contract-psl/src/psl-field-resolution.ts
  • packages/2-sql/2-authoring/contract-psl/test/composed-mutation-defaults.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/fixtures.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.capability-gating.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.control-policy.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.defaults.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.diagnostics.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.enum.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.extensions.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.namespaces.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.polymorphism.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.relations.many-to-many.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.relations.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.types.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.value-objects.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/psl-ts-namespace-parity.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/ts-psl-parity.test.ts
  • packages/2-sql/2-authoring/contract-ts/test/config-types.test.ts
  • packages/3-extensions/postgres/test/psl-namespace-qualifier-routing.test.ts
  • packages/3-targets/3-targets/postgres/test/psl-policy-authoring.test.ts
  • packages/3-targets/6-adapters/postgres/test/migrations/rls-lifecycle-e2e.integration.test.ts
  • packages/3-targets/6-adapters/postgres/test/migrations/rls-migration-plan.integration.test.ts
  • packages/3-targets/6-adapters/postgres/test/migrations/rls-walking-skeleton-psl.integration.test.ts
  • test/integration/test/authoring/cli.emit-parity-fixtures.test.ts
  • test/integration/test/authoring/parity/ts-psl-parity.real-packs.test.ts
  • test/integration/test/authoring/psl-index-type-options.integration.test.ts
  • test/integration/test/authoring/psl.pgvector-dbinit.test.ts
  • test/integration/test/authoring/side-by-side-contracts.test.ts
  • test/integration/test/cli.emit-command.additional.test.ts
  • test/integration/test/cli.emit-contract.test.ts
  • test/integration/test/scalar-lists/psl-list-authoring.ts
  • test/integration/test/scalar-lists/psl-list-mongo-parity.integration.test.ts
  • test/integration/test/scalar-lists/psl-list-roundtrip.integration.test.ts
  • test/integration/test/value-objects/value-objects.integration.test.ts
✅ Files skipped from review due to trivial changes (3)
  • packages/2-mongo-family/2-authoring/contract-ts/test/config-types.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.relations.many-to-many.test.ts
  • packages/3-extensions/postgres/test/psl-namespace-qualifier-routing.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/2-sql/2-authoring/contract-psl/src/provider.ts
  • test/integration/test/scalar-lists/psl-list-mongo-parity.integration.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.capability-gating.test.ts
  • packages/2-sql/2-authoring/contract-psl/test/interpreter.value-objects.test.ts
  • test/integration/test/scalar-lists/psl-list-roundtrip.integration.test.ts
  • test/integration/test/scalar-lists/psl-list-authoring.ts

Comment thread packages/1-framework/2-authoring/psl-parser/src/resolve.ts Outdated
Comment thread packages/1-framework/2-authoring/psl-parser/src/resolve.ts
SevInf added a commit that referenced this pull request Jul 1, 2026
…#870

The PR touches one packages/3-extensions/ test file (threading the now-
required capabilities matrix into a test ContractSourceContext), which trips
the check:upgrade-coverage per-pr-declaration gate. Record it as an incidental
substrate diff: the affected types are framework-internal contract-emission
seams the control stack always populates; no extension-author API changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/1-framework/2-authoring/psl-parser/src/resolve.ts (1)

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

Prefer ifDefined over ternary-based conditional spreads.

Both ...(name !== undefined ? { name } : {}) and the new ...(expression !== undefined ? { expression } : {}) match the ternary-spread anti-pattern the codebase has previously preferred to avoid in favor of ifDefined.

Based on learnings, "prefer ifDefined from prisma-next/utils/defined for conditional object spreads instead of inline ternary-based spreads... to avoid '{...cond ? {a: b} : {}}'-style patterns."

♻️ Proposed refactor
+import { ifDefined } from '`@prisma-next/utils/defined`';
...
     args.push({
       kind: name !== undefined ? 'named' : 'positional',
-      ...(name !== undefined ? { name } : {}),
+      ...ifDefined('name', name),
       value: renderExpression(expression),
-      ...(expression !== undefined ? { expression } : {}),
+      ...ifDefined('expression', expression),
       span: nodePslSpan(arg.syntax, sourceFile),
     });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/1-framework/2-authoring/psl-parser/src/resolve.ts` around lines 73 -
80, Replace the ternary-based conditional spreads in resolve.ts with the shared
ifDefined helper from prisma-next/utils/defined. In the args.push object inside
the expression handling logic, keep the existing kind/value/span fields but use
ifDefined for the optional name and expression properties instead of inline
`...(cond ? { ... } : {})` spreads. This should preserve the same behavior while
matching the codebase’s preferred pattern.

Source: Learnings

🤖 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 `@packages/1-framework/2-authoring/psl-parser/src/resolve.ts`:
- Around line 73-80: Replace the ternary-based conditional spreads in resolve.ts
with the shared ifDefined helper from prisma-next/utils/defined. In the
args.push object inside the expression handling logic, keep the existing
kind/value/span fields but use ifDefined for the optional name and expression
properties instead of inline `...(cond ? { ... } : {})` spreads. This should
preserve the same behavior while matching the codebase’s preferred pattern.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 6cc50a32-f062-4cbd-aa0c-ff92ce98b17e

📥 Commits

Reviewing files that changed from the base of the PR and between 34d9a92 and e682b72.

📒 Files selected for processing (3)
  • packages/1-framework/2-authoring/psl-parser/src/resolve.ts
  • packages/2-sql/2-authoring/contract-psl/src/psl-attribute-parsing.ts
  • packages/2-sql/2-authoring/contract-psl/src/psl-column-resolution.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/2-sql/2-authoring/contract-psl/src/psl-attribute-parsing.ts

Comment thread packages/2-sql/4-lanes/relational-core/src/ast/ddl-types.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
packages/3-targets/6-adapters/postgres/src/core/control-adapter.ts (2)

893-900: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Stale comment: scope note no longer matches the parser.

This comment still says only = ANY (ARRAY[...]) and IN (...) shapes are recognized, but parseCheckConstraintDef now also recognizes the array_position(col, NULL) IS NULL element-non-null expression shape.

✏️ Suggested comment update
-    // Scope: only parses the `= ANY (ARRAY[...])` and `IN (...)` shapes
-    // that this slice emits. Arbitrary SQL predicates are left as-is
-    // and will not produce check IR entries (they are silently skipped).
+    // Scope: only parses the `= ANY (ARRAY[...])`, `IN (...)`, and
+    // `array_position(col, NULL) IS NULL` shapes that this slice emits.
+    // Arbitrary SQL predicates are left as-is and will not produce check
+    // IR entries (they are silently skipped).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/3-targets/6-adapters/postgres/src/core/control-adapter.ts` around
lines 893 - 900, Update the stale scope comment near parseCheckConstraintDef to
reflect all supported CHECK predicate shapes, not just = ANY (ARRAY[...]) and IN
(...). Add the array_position(col, NULL) IS NULL form to the parser scope note
so the comment matches the current behavior in the control-adapter logic.

1119-1141: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Stale comment: "two shapes" / "column + value set" no longer accurate.

Same drift as the query comment above — the loop now branches on valueSet vs expression kinds.

✏️ Suggested comment update
-      // Process check constraints — parse each predicate into column + value set.
-      // Only the two shapes emitted by this slice are recognised; free-form
-      // predicates are silently skipped (they won't produce check IR entries).
+      // Process check constraints — parse each predicate into a valueSet or
+      // expression check. Only the shapes emitted by this slice are
+      // recognised; free-form predicates are silently skipped (they won't
+      // produce check IR entries).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/3-targets/6-adapters/postgres/src/core/control-adapter.ts` around
lines 1119 - 1141, Update the stale inline comment in control-adapter.ts around
checksForTable parsing so it matches the current logic in
parseCheckConstraintDef and the valueSet/expression branches. Replace the
outdated “two shapes” / “column + value set” wording with a comment that
reflects both supported parsed kinds, and keep the wording aligned with the
existing identifiers checksForTable, parseCheckConstraintDef, valueSet, and
expression.
packages/2-sql/9-family/src/core/migrations/contract-to-schema-ir.ts (1)

233-266: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Postgres-specific SQL syntax hardcoded in the shared "9-family" layer.

elementNonNullCheckExpression's array_position(...) IS NULL predicate is a Postgres physical detail (as the docstring itself admits), but it's synthesized unconditionally inside projectContractChecks/convertTable, which lives in the SQL-family-shared layer, not a target-specific adapter. The same convertTable signature already accepts expandNativeType/renderDefault adapter hooks for exactly this kind of target-specific customization — the new check-expression synthesis doesn't follow that pattern and would need to be re-derived if a non-Postgres SQL target is ever added to this family.

Is another SQL dialect target planned for the "2-sql" family in the near term, or is Postgres expected to remain the sole SQL target? If the former, consider threading an injectable check-expression renderer analogous to NativeTypeExpander/DefaultRenderer.
As per coding guidelines, "Don't branch on target; use adapters: .agents/rules/no-target-branches.mdc."

🤖 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/2-sql/9-family/src/core/migrations/contract-to-schema-ir.ts` around
lines 233 - 266, The check-constraint projection in projectContractChecks is
hardcoding a Postgres-only expression inside the shared SQL-family layer. Move
the element-non-null check generation behind an injectable adapter, similar to
NativeTypeExpander and DefaultRenderer in convertTable, and thread it through
projectContractChecks/convertTable so target-specific renderers can supply the
expression instead of synthesizing array_position(...) unconditionally. Keep the
existing check name wiring via elementNonNullCheckName, but make
elementNonNullCheckExpression target-provided rather than shared-layer fixed.
packages/2-sql/9-family/src/core/schema-verify/verify-helpers.ts (1)

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

Silent fallback on unrecognized check kind.

liveCheckLabel returns '' if check is neither SqlValueSetCheckIR nor SqlExpressionCheckIR. With only two subclasses currently, this is unreachable today, but if a third check kind is added later without updating this helper, mismatch/missing messages would silently show an empty label instead of surfacing the gap.

🛡️ Optional: fail loudly instead of returning an empty label
 function liveCheckLabel(check: SqlCheckConstraintIR): string {
   if (check instanceof SqlValueSetCheckIR) return check.permittedValues.join(', ');
   if (check instanceof SqlExpressionCheckIR) return check.expression;
-  return '';
+  throw new Error(`Unrecognized check constraint kind for "${check.name}"`);
 }
🤖 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/2-sql/9-family/src/core/schema-verify/verify-helpers.ts` around
lines 698 - 702, The liveCheckLabel helper currently falls back to an empty
string for unknown SqlCheckConstraintIR variants, which can hide missing support
when new check types are added. Update liveCheckLabel in verify-helpers.ts to
fail loudly on unrecognized kinds instead of returning a silent default, so any
future SqlValueSetCheckIR/SqlExpressionCheckIR mismatch is surfaced during
development.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/2-sql/9-family/src/core/migrations/contract-to-schema-ir.ts`:
- Around line 214-216: The generated check name from elementNonNullCheckName can
exceed PostgreSQL’s 63-byte identifier limit, causing truncation and drift
between projection and introspection. Update this helper to deterministically
bound the returned name to Postgres-safe length, using a stable hash/truncation
scheme so the emitted constraint name always matches what introspection will
see. Keep the fix localized to elementNonNullCheckName and ensure any callers
relying on the name continue to get a stable, reproducible result.

In
`@packages/3-targets/3-targets/postgres/test/migrations/planner.check-constraints.test.ts`:
- Around line 447-459: In the planner check-constraints test for
checkConstraintPlanCallStrategy, make the assertion require a successful match
before verifying there are zero calls. Update the test around the existing
result handling so it explicitly fails on a no_match outcome and only checks
result.calls after confirming the strategy matched; this ensures regressions in
element-check matching are caught. Use the checkConstraintPlanCallStrategy and
result.kind branches in the current test to locate the change.

---

Nitpick comments:
In `@packages/2-sql/9-family/src/core/migrations/contract-to-schema-ir.ts`:
- Around line 233-266: The check-constraint projection in projectContractChecks
is hardcoding a Postgres-only expression inside the shared SQL-family layer.
Move the element-non-null check generation behind an injectable adapter, similar
to NativeTypeExpander and DefaultRenderer in convertTable, and thread it through
projectContractChecks/convertTable so target-specific renderers can supply the
expression instead of synthesizing array_position(...) unconditionally. Keep the
existing check name wiring via elementNonNullCheckName, but make
elementNonNullCheckExpression target-provided rather than shared-layer fixed.

In `@packages/2-sql/9-family/src/core/schema-verify/verify-helpers.ts`:
- Around line 698-702: The liveCheckLabel helper currently falls back to an
empty string for unknown SqlCheckConstraintIR variants, which can hide missing
support when new check types are added. Update liveCheckLabel in
verify-helpers.ts to fail loudly on unrecognized kinds instead of returning a
silent default, so any future SqlValueSetCheckIR/SqlExpressionCheckIR mismatch
is surfaced during development.

In `@packages/3-targets/6-adapters/postgres/src/core/control-adapter.ts`:
- Around line 893-900: Update the stale scope comment near
parseCheckConstraintDef to reflect all supported CHECK predicate shapes, not
just = ANY (ARRAY[...]) and IN (...). Add the array_position(col, NULL) IS NULL
form to the parser scope note so the comment matches the current behavior in the
control-adapter logic.
- Around line 1119-1141: Update the stale inline comment in control-adapter.ts
around checksForTable parsing so it matches the current logic in
parseCheckConstraintDef and the valueSet/expression branches. Replace the
outdated “two shapes” / “column + value set” wording with a comment that
reflects both supported parsed kinds, and keep the wording aligned with the
existing identifiers checksForTable, parseCheckConstraintDef, valueSet, and
expression.
🪄 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: 75719089-bc75-4683-95e4-a53cc7d09976

📥 Commits

Reviewing files that changed from the base of the PR and between e682b72 and 5e5ad05.

📒 Files selected for processing (23)
  • examples/prisma-next-demo/migrations/app/20260422T0720_initial/migration.ts
  • packages/2-sql/1-core/schema-ir/src/exports/types.ts
  • packages/2-sql/1-core/schema-ir/src/ir/sql-check-constraint-ir.ts
  • packages/2-sql/1-core/schema-ir/src/ir/sql-table-ir.ts
  • packages/2-sql/1-core/schema-ir/src/types.ts
  • packages/2-sql/9-family/src/core/migrations/contract-to-schema-ir.ts
  • packages/2-sql/9-family/src/core/schema-verify/verify-helpers.ts
  • packages/2-sql/9-family/src/core/schema-verify/verify-sql-schema.ts
  • packages/2-sql/9-family/src/exports/control.ts
  • packages/2-sql/9-family/src/exports/schema-verify.ts
  • packages/2-sql/9-family/test/schema-verify.check-constraints.test.ts
  • packages/3-targets/3-targets/postgres/src/core/migrations/issue-planner.ts
  • packages/3-targets/3-targets/postgres/src/core/migrations/op-factory-call.ts
  • packages/3-targets/3-targets/postgres/src/core/migrations/operations/constraints.ts
  • packages/3-targets/3-targets/postgres/src/core/migrations/planner-strategies.ts
  • packages/3-targets/3-targets/postgres/src/core/migrations/postgres-migration.ts
  • packages/3-targets/3-targets/postgres/src/core/schema-ir/postgres-table-ir.ts
  • packages/3-targets/3-targets/postgres/test/migrations/op-factory-call.test.ts
  • packages/3-targets/3-targets/postgres/test/migrations/planner.check-constraints.test.ts
  • packages/3-targets/6-adapters/postgres/src/core/control-adapter.ts
  • packages/3-targets/6-adapters/postgres/test/control-adapter.check-constraints.test.ts
  • packages/3-targets/6-adapters/postgres/test/migrations/native-array-columns.integration.test.ts
  • skills/upgrade/prisma-next-upgrade/upgrades/0.14-to-0.15/instructions.md
✅ Files skipped from review due to trivial changes (1)
  • packages/2-sql/9-family/src/exports/control.ts

Comment on lines +214 to +216
export function elementNonNullCheckName(tableName: string, columnName: string): string {
return `${tableName}_${columnName}_elem_not_null`;
}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for existing length-truncation handling in sibling name generators
rg -n -A5 'function defaultIndexName' packages/2-sql/9-family/src/core/migrations/contract-to-schema-ir.ts
rg -n 'NAMEDATALEN|63' packages/2-sql packages/3-targets/3-targets/postgres --type=ts

Repository: prisma/prisma-next

Length of output: 713


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the target file and inspect the relevant section.
ast-grep outline packages/2-sql/9-family/src/core/migrations/contract-to-schema-ir.ts --view expanded || true
sed -n '180,245p' packages/2-sql/9-family/src/core/migrations/contract-to-schema-ir.ts

echo '--- sql-utils ---'
sed -n '1,140p' packages/3-targets/3-targets/postgres/src/core/sql-utils.ts

echo '--- tests ---'
sed -n '1,120p' packages/3-targets/3-targets/postgres/test/sql-utils.test.ts

Repository: prisma/prisma-next

Length of output: 12622


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find all name generators in the migration IR file and nearby checks.
rg -n 'function .*Name|checkName|indexName|constraint' packages/2-sql/9-family/src/core/migrations/contract-to-schema-ir.ts

Repository: prisma/prisma-next

Length of output: 1106


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for integration tests around long identifiers and generated constraint names.
rg -n -A3 -B3 'elementNonNullCheckName|defaultIndexName|exceeds PostgreSQL' packages -g '*test*' -g '*spec*'

Repository: prisma/prisma-next

Length of output: 840


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the Postgres-specific identifier helpers for exact handling.
sed -n '1,180p' packages/3-targets/3-targets/postgres/src/core/sql-utils.ts

Repository: prisma/prisma-next

Length of output: 3669


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the exact lines around the elementNonNullCheckName implementation.
nl -ba packages/2-sql/9-family/src/core/migrations/contract-to-schema-ir.ts | sed -n '206,222p'

Repository: prisma/prisma-next

Length of output: 196


Bound generated check names to PostgreSQL’s 63-byte limit. elementNonNullCheckName can produce names Postgres truncates, so the stored constraint name can differ from the expected one and trigger false drift. Hash/truncate the name here to keep projection and introspection aligned.

🤖 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/2-sql/9-family/src/core/migrations/contract-to-schema-ir.ts` around
lines 214 - 216, The generated check name from elementNonNullCheckName can
exceed PostgreSQL’s 63-byte identifier limit, causing truncation and drift
between projection and introspection. Update this helper to deterministically
bound the returned name to Postgres-safe length, using a stable hash/truncation
scheme so the emitted constraint name always matches what introspection will
see. Keep the fix localized to elementNonNullCheckName and ensure any callers
relying on the name continue to get a stable, reproducible result.

SevInf and others added 8 commits July 2, 2026 14:24
…slice 2 D1)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…ce 2 D2)

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…pability (slice 2 D3)

Thread a merged CapabilityMatrix (built via mergeCapabilityMatrices over
[target, adapter, ...extensionPacks], the same merge enrichContract performs)
from the ControlStack through ContractSourceContext into the PSL interpreter,
and reject a scalar-list field whose target does not report sql.scalarList with
PSL_SCALAR_LIST_UNSUPPORTED_TARGET. An absent matrix means do not gate, so
non-adapter authoring paths stay valid; the CLI emit path always populates it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
… D1b)

Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…r PSL lists (slice 2 D4)

Part A (FR13, AC8 PSL half): every native scalar-array column gets a
deterministic CHECK (array_position(col, NULL) IS NULL) constraint named
<table>_<column>_elem_not_null, emitted at the migration/DDL layer in the
postgres issue-planner createTable path via a new CheckExpressionConstraint
DDL node. Introspection-based verify skips non-enum check predicates, so the
constraint round-trips without false drift and is invisible to infer.

Part B (FR14, AC9): PSL array-typed @default lowering. @default([]) -> empty
literal array (DEFAULT *{}*); @default(["a","b"]) -> literal array encoded
element-wise against the element codec (build-contract encodeColumnDefault is
now many-aware). A scalar literal default on a list field is rejected at
authoring time with PSL_LIST_DEFAULT_NOT_ARRAY (closes slice-1 D2 carry-in).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…ice 2 D5)

AC1: a `posts.tags String[]` schema authored in PSL emits a native array
storage column (pg/text@1, many:true, not jsonb), migrates onto a fresh
Postgres database as text[] with no manual edits, and round-trips through
`contract infer` back to a `tags String[]` PSL field.

AC2/NFR1: DateTime[]/Bytes[]/Decimal[] list fields authored in PSL (not
hand-built typed contracts) insert and select rows whose decoded element
values deep-equal the originals, proving per-element codec application
through the whole authored path.

NFR4: the same list schema yields matching observable semantics on SQL and
Mongo — both author cleanly, both generate a ReadonlyArray<...> domain type
over the string element codec, and list element values round-trip. The live
Mongo decode runs on mongodb-memory-server (CI; nixos binary download is
local env-noise).

Also hardens the introspection-side array-literal parser
(parseArrayLiteralBody in default-normalizer.ts) so a quoted element
containing a comma or an escaped quote round-trips through the migration-diff
layer without spurious drift (slice-1 carry-in).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…ing test (rebase on #864)

Main #864 made SqlNamespace abstract and createNamespace required on
InterpretPslDocumentToSqlContractInput. Thread createTestSqlNamespace
through the three D3 capability-gating interpret calls, matching the
sibling interpreter tests updated by that refactor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Respond to reviewer feedback on the scalar-list authoring path:

- Simplify the `many` emission to the file's own
  `...(rf.many ? { many: true as const } : {})` idiom; `many` stays
  omitted (never `false`) for non-list fields, keeping canonical
  emission byte-stable.

- Consume the parser's structured expression AST for list-field
  `@default([...])` instead of re-parsing a stringified form. The
  PSL parser now decodes each attribute-arg expression into a
  `ResolvedExpr` discriminated union (string/number/boolean/array/
  object/call/identifier) exposed on `ResolvedAttributeArg`, and the
  SQL column resolver reads it directly. Deletes the hand-rolled
  `splitTopLevelArrayElements` tokenizer and the array-literal string
  parsing; array/scalar/function-default behavior is preserved,
  including quoted elements containing commas.

- Make the adapter capability matrix required end-to-end so the
  scalar-list gate has no test-only undefined branch and fails closed:
  an empty matrix rejects scalar lists. `ContractSourceContext`,
  `InterpretPslDocumentToSqlContractInput`, and the internal
  resolution inputs now require `capabilities`; production already
  threads it via the control stack. Test call sites pass an explicit
  matrix; the former "does not gate when no matrix is threaded" case
  becomes a fail-closed empty-matrix rejection.

- Drop transient milestone codes from the scalar-list integration
  test comments and titles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
SevInf and others added 3 commits July 2, 2026 14:24
…#870

The PR touches one packages/3-extensions/ test file (threading the now-
required capabilities matrix into a test ContractSourceContext), which trips
the check:upgrade-coverage per-pr-declaration gate. Record it as an incidental
substrate diff: the affected types are framework-internal contract-emission
seams the control stack always populates; no extension-author API changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…teArg (slice 2 N1)

Replace the decoded ResolvedExpr union with the parser ExpressionAst node
directly on ResolvedAttributeArg, per code-owner review. List-default parsing
narrows via the AST classes (ArrayLiteralAst/StringLiteralExprAst/...) instead
of a bespoke decoded shape. The stringified value is kept, as it is still
consumed by scalar/function-call/relation-name/authoring-arg parsing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…nd ORM client

Add a sibling to the DateTime[]/Bytes[]/Decimal[] roundtrip covering the plain
scalar element types: author `tags String[]`/`scores Int[]` in PSL, migrate onto
real Postgres, insert, and SELECT back, asserting element-wise decode for
pg/text@1 and pg/int4@1.

Add an ORM-client read-back test: over the same PSL-authored contract, read the
native array columns through orm().<ns>.<model>.select(...).all(), proving the
ORM projects and decodes scalar many:true columns as JS arrays (not just to-many
relations). The PSL path yields a generic Contract<SqlStorage>, so accessors are
index signatures; the narrow row-type array inference is covered by the emitted
contract path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
@SevInf SevInf force-pushed the scalar-lists-slice2 branch from 283c5ba to d4c5290 Compare July 2, 2026 14:24
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