Skip to content

TML-3289: Raw SQL in a TypeScript contract is a sql… value, as it is in PSL - #30558

Merged
wmadden-electric merged 3 commits into
mainfrom
tml-3289-sql-expression-ts
Oct 9, 2026
Merged

wmadden-electric merged 3 commits into
mainfrom
tml-3289-sql-expression-ts

Conversation

@wmadden-electric

@wmadden-electric wmadden-electric commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

At a glance

import { check, defineContract, field, model, policyUpdate, rlsEnabled, role, sql } from '@prisma/orm-postgres/contract-builder';

const owner = sql`"userId"::uuid = auth.uid()`;

const Post = model('Post', { fields: { /* id, userId, title, archivedAt */ } }).sql(({ cols, constraints }) => ({
  indexes: [
    constraints.index([cols.userId], {
      name: 'post_user_active',
      where: sql`
        "archivedAt" IS NULL
          AND "userId" <> ''
      `,
    }),
  ],
  checks: [check({ expression: sql`length("title") > 0`, name: 'post_title_nonempty' })],
}));

export const contract = defineContract({
  models: { Post },
  entities: [
    rlsEnabled(Post),
    policyUpdate(Post, {
      name: 'post_owner_write',
      roles: [role('authenticated')],
      using: owner,
      withCheck: sql`${owner} AND "archivedAt" IS NULL`,
    }),
  ],
});

Every field that holds raw SQL takes a sql value. where: '"archivedAt" IS NULL' no longer compiles. The contract this emits is byte for byte the contract the same schema emits when written in PSL:

@@index([userId], where: sql`
  "archivedAt" IS NULL
    AND "userId" <> ''
`, name: "post_user_active")

Decision

In the TypeScript contract builder, raw SQL is a SqlExpression, made with the sql template tag. Every builder field that holds raw SQL accepts only that type:

  • constraints.index(...): where and expression
  • check(...): expression
  • fullTextIndex(...): where
  • policySelect, policyInsert, policyUpdate, policyDelete, policyAll: using and withCheck
  • .default(...), which already took sql values

A string in any of them is a type error. This is the TypeScript half of ADR 268; #30550 did the PSL half.

Why

Contracts hold SQL that Prisma does not parse. An index's where, an expression index, a CHECK constraint, a row-level-security policy's using and withCheck, and a raw column default are SQL text. The contract stores the text, and the migration planner places it inside DDL, such as CREATE INDEX … WHERE (<text>).

PSL writes that SQL as a sql literal, and cleans up its text. Since #30550, every one of those places in PSL takes a sql`...` literal and nothing else. A literal may span several lines, so PSL canonicalizes its text before storing it: it removes the indentation shared by every line, drops blank lines at the start and end, and removes carriage returns. The indented PSL above stores "archivedAt" IS NULL\n AND "userId" <> ''.

A TypeScript string does not get that clean-up. A template string keeps its indentation and its surrounding blank lines. So the same SQL, written in PSL and in TypeScript, stored different text in contract.json, and the two contracts had different storage hashes. ADR 129 promises that PSL and TypeScript emit identical contracts for the same schema; multi-line SQL broke that promise.

The sql tag canonicalizes with the same function PSL uses. sql`...` returns a SqlExpression whose text is canonical. The SqlExpression constructor canonicalizes too, so no way of making a value skips it. A NUL character or a text over 64 KiB throws CONTRACT.SQL_EXPRESSION_INVALID, with the same message PSL gives.

sql values compose; other values do not. TypeScript contracts reuse a predicate across policies, as owner does above. The tag accepts other sql values inside ${…} and joins their text in. Each later line of an inserted value takes the indentation of the template line it sits on, so the joined text is what the author sees, and the result is canonicalized once more as a whole. Anything else inside ${…}, such as a string or a number, throws CONTRACT.SQL_EXPRESSION_INTERPOLATION. This differs from the query lane's db.raw.sql, which binds ${…} values as query parameters: contract SQL becomes DDL, and DDL has no parameters.

.default() keeps the checks that only defaults need. A default must not be now() or autoincrement() written as raw SQL, and must not contain ;, a comment, $$ or SELECT, because of how defaults are rendered and compared. Those checks used to live in the tag. The tag now also serves policies, whose predicates often hold EXISTS (SELECT …), so the checks moved into .default(), which throws CONTRACT.DEFAULT_INVALID.

JavaScript that is not type-checked still gets a clear error. When the builder turns a model into contract storage, it reads each raw-SQL field through requireSqlExpression. Anything that is not a sql value throws CONTRACT.ARGUMENT_INVALID, naming the object and the field: Policy "post_owner_write" using must be a sql`...` value. An unnamed index is named by its model (Index on "Post" where), and an unnamed full-text index by its fields (Full-text index on fields "title", "body" where).

A value from a second copy of the package still works. SqlExpression is a class, so a hand-written { text: 'x' } is not assignable to it. The class carries a Symbol.for marker, so a value made by another installed copy of the package is still recognized. readSqlExpression rebuilds such a value with this copy's constructor, so its text is canonicalized too, in every field and inside ${…}.

contract print and contract infer handle defaults whose text would not read back. A default whose string constant holds, for example, a carriage return would change if printed as a sql literal and read back. contract print refuses such a default, as it already refuses such an index or policy. contract infer still prints it, because a skipped default would be dropped by the next migration, and adds a note to check its string constants.

The contract format does not change. The contract still stores the text as a string; only the builder's field types change.

What changes for users

  • TypeScript contract authors: wrap raw SQL in sql`…`. The upgrade instructions in upgrade-instructions/pending/sql-expression-literals-ts/app/ list every field. A text that was not canonical before, such as an indented multi-line template string, is stored differently once, so the storage hash changes once; the instructions say to run migration plan once. Index, check and policy names do not change.
  • Error codes: CONTRACT.DEFAULT_SQL_INTERPOLATION is replaced by CONTRACT.SQL_EXPRESSION_INTERPOLATION; CONTRACT.SQL_EXPRESSION_INVALID is new. Both are in the error reference.
  • Extension authors: policy handles and descriptors hold SqlExpression. sql, SqlExpression, isSqlExpression, readSqlExpression and requireSqlExpression come from @internal/sql-contract/sql-expression; the contract-builder facades re-export sql and the SqlExpression type. The SQL family registers sql/expression through one value, sqlExpressionRegistration. See upgrade-instructions/pending/sql-expression-literals-ts/extension/.
  • Unchanged: migration files keep taking strings (TML-3297, the next slice, lets them take sql values too). The deprecated .defaultSql() and .default({ kind: 'function', expression }) still take a string and store it without canonicalization; TML-3286 removes .defaultSql() at 8.0.0.

Where to look

  • packages/2-sql/1-core/contract/src/sql-expression.ts: the class, the tag, readSqlExpression and requireSqlExpression.
  • packages/2-sql/2-authoring/contract-ts/src/contract-dsl.ts and contract-lowering.ts: the field types, .default(), and reading each field. In contract-lowering.ts, a sql value is checked before a deferred index expression's render, because 'render' in someString throws a TypeError.
  • packages/3-extensions/postgres/src/contract/rls.ts and full-text-index.ts; packages/3-targets/3-targets/postgres/src/core/authoring.ts: policies and full-text indexes.
  • test/integration/test/authoring/parity/sql-expressions/: the same schema in PSL and TypeScript, with indented multi-line text, tab indentation, a backslash, an escaped backtick, an interpolated predicate holding a -- comment, a multi-line predicate interpolated into an indented withCheck, a full-text index with several weight groups, and a raw default. The test checks both emit the same contract.json.
  • Docs: ADR 268 and ADRs 129, 234, 236, 243, 244 and 254, the error reference, the contract-ts README, and the prisma-8 skill reference contract.md.
  • projects/sql-expression-literals/ holds the project's spec, design, plan, reviews, manual QA and status. It is deleted at project close-out.

Testing performed

  • pnpm build, pnpm typecheck, pnpm lint, pnpm lint:deps, pnpm lint:casts, pnpm lint:throws, pnpm check:error-reference, pnpm lint:framework-vocabulary, pnpm lint:skills, pnpm fixtures:check (no contract.json change in any existing fixture), pnpm test:scripts, and pnpm check:upgrade-coverage --mode pr against the merge base.
  • pnpm test:packages: everything passes except three tarball tests, which cannot install a package on the author's machine because the npm registry refuses it; CI runs them.
  • Integration files run one by one: test/authoring/** (including the parity fixture), test/sql-builder/**, test/psl-print/**, the RLS tests, and the SQL expression literals CLI journey. The full integration suite runs in CI.
  • Each new test was shown to fail when the behaviour it covers is removed.
  • Manual QA: the slice 3 script in projects/sql-expression-literals/manual-qa.md reads every refusal above as a user sees it, at run time and at compile time. The run of 2026-10-08 matches every expected result.
  • Three rounds of design and code review; the reports are in projects/sql-expression-literals/slice-reviews/3/, 3-round-2/ and 3-round-3/, and every finding is fixed.

Skill update

The prisma-8 skill reference contract.md writes TypeScript raw SQL as sql values. The upgrade instructions for app and extension authors are in upgrade-instructions/pending/sql-expression-literals-ts/.

Linked issue

Refs TML-3289, part of the Linear project SQL expression literals.

Checklist

  • All commits are signed off (git commit -s) per the DCO.
  • I read CONTRIBUTING.md and the change is scoped to one logical concern.
  • Tests are updated.
  • The PR title is in TML-NNNN: <sentence-case title> form.
  • The Skill update section above is filled in.

Alternatives considered

  • Accept strings beside sql values. Rejected. There would be two ways to write the same thing, and a string skips canonicalization, so PSL and TypeScript would again store different text for multi-line SQL.
  • A plain object { text } instead of a class. Rejected. A hand-written object would pass the type check without being canonicalized, and .default() could not tell it from a JSON default value.
  • Keep the default checks in the tag. Rejected. The tag now serves every place, and "no SELECT" would refuse valid policy predicates such as EXISTS (SELECT …).
  • Let ${…} take strings and numbers. Rejected. That is building SQL by string concatenation, which the typed value exists to prevent, and DDL has no parameters to bind them to.
  • Skip, in contract infer, a default whose text would not read back, as for an index. Rejected. A skipped default is dropped by the next migration; printing it with a note keeps it and tells the user what to check.

Agent: lagertha-65

🤖 Generated with Claude Code

@wmadden-electric
wmadden-electric requested a review from a team as a code owner October 1, 2026 07:26
@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Review in 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: prisma/orm/.coderabbit.yml

Review profile: CHILL

Plan: Advanced

Run ID: cd6b67fe-3173-4e92-9b81-04d5388bbf9e


📥 Commits

Reviewing files that changed from the base of the PR and between 15b08df and 59aef6b.


⛔ Files ignored due to path filters (21)
  • projects/sql-expression-literals/design.md is excluded by !projects/**
  • projects/sql-expression-literals/dispatches/3-implementer-brief.md is excluded by !projects/**
  • projects/sql-expression-literals/dispatches/3-pr-body-draft.md is excluded by !projects/**
  • projects/sql-expression-literals/dispatches/3-review-fixes-brief.md is excluded by !projects/**
  • projects/sql-expression-literals/dispatches/3-round-2-fixes-brief.md is excluded by !projects/**
  • projects/sql-expression-literals/dispatches/3-round-2-fixes-findings.md is excluded by !projects/**
  • projects/sql-expression-literals/dispatches/4-implementer-brief.md is excluded by !projects/**
  • projects/sql-expression-literals/dispatches/4-review-fixes-brief.md is excluded by !projects/**
  • projects/sql-expression-literals/dispatches/4-round-2-fixes-brief.md is excluded by !projects/**
  • projects/sql-expression-literals/handover.md is excluded by !projects/**
  • projects/sql-expression-literals/manual-qa.md is excluded by !projects/**
  • projects/sql-expression-literals/plan.md is excluded by !projects/**
  • projects/sql-expression-literals/slice-reviews/3-round-2/code-review.md is excluded by !projects/**
  • projects/sql-expression-literals/slice-reviews/3-round-2/system-design-review.md is excluded by !projects/**
  • projects/sql-expression-literals/slice-reviews/3/code-review.md is excluded by !projects/**
  • projects/sql-expression-literals/slice-reviews/3/system-design-review.md is excluded by !projects/**
  • projects/sql-expression-literals/slice-reviews/4-round-2/code-review.md is excluded by !projects/**
  • projects/sql-expression-literals/slice-reviews/4-round-2/system-design-review.md is excluded by !projects/**
  • projects/sql-expression-literals/slice-reviews/4/code-review.md is excluded by !projects/**
  • projects/sql-expression-literals/slice-reviews/4/system-design-review.md is excluded by !projects/**
  • projects/sql-expression-literals/status.md is excluded by !projects/**

📒 Files selected for processing (1)
  • packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.



📝 Walkthrough

Walkthrough

The TypeScript sql tag now creates canonical SqlExpression values and supports SQL-value interpolation. Contract-builder raw SQL fields, PostgreSQL policies, indexes, defaults, inference, printing, tests, and documentation use the new representation.

Changes

SQL expression authoring

Layer / File(s) Summary
SQL expression value and registration
packages/2-sql/1-core/contract/src/sql-expression.ts, packages/2-sql/1-core/contract/src/contract-errors.ts, packages/1-framework/..., packages/2-sql/9-family/...
The sql tag creates immutable, canonical SqlExpression values. It supports SQL-value interpolation, validates text limits, exposes recognition helpers, and publishes frozen registration data.
TypeScript builder fields and lowering
packages/2-sql/2-authoring/contract-ts/src/contract-dsl.ts, packages/2-sql/2-authoring/contract-ts/src/contract-lowering.ts, packages/2-sql/2-authoring/contract-ts/src/exports/contract-builder.ts, packages/2-sql/2-authoring/contract-ts/test/*
Index predicates and expressions, check expressions, and defaults use SqlExpression. Lowering validates values and normalizes deferred index rendering.
PostgreSQL index and policy fields
packages/3-extensions/postgres/src/contract/*, packages/3-extensions/postgres/src/exports/contract-builder.ts, packages/3-targets/3-targets/postgres/src/core/authoring.ts, packages/3-extensions/postgres/test/*
Full-text-index predicates and RLS policy predicates use SqlExpression. Contract construction rejects invalid runtime values, and PostgreSQL lowering extracts SQL text.
Column default read-back handling
packages/3-targets/3-targets/postgres/src/core/psl-infer/*, packages/3-targets/3-targets/postgres/src/core/psl-print/*, packages/3-targets/3-targets/postgres/test/psl-*
Inference adds notes for non-round-trippable column defaults. Printing refuses those defaults with CONTRACT.PRINT_UNSUPPORTED.
Parity fixtures and migration guidance
test/integration/test/*, upgrade-instructions/pending/sql-expression-literals-ts/*, docs/architecture/*, docs/reference/error-reference.md, skills/prisma-8/references/contract.md
Fixtures and examples use SQL expressions for raw SQL fields. Upgrade instructions and references describe input requirements, canonicalization, errors, storage-hash effects, and default handling.

Priority: ⬇️ Low

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

Change: Feature

Suggested reviewers: sevinf


Merge Risk

Merge Risk: ⚪ Minimal · up to 59aef

The SQL-expression behavior follows its documented canonicalization contract, and the selected test-fixture update does not change runtime behavior. No merge-blocking risk is indicated.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 59aef

This breaking authoring change reaches database policies and defaults, but the inspected paths retain explicit raw-SQL authoring and reject ordinary interpolated values. No introduced security vulnerability was established. Upgrade compatibility and SQL round-trip behavior still require care.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The security-sensitive reach of the changed authoring representation includes predicates controlling access to contract-selected PostgreSQL tables. Effective exposure depends on which contracts are subsequently applied and with what database authority; that deployment scope was not established.

Security Findings and Attack Paths

  • inferred — No newly reachable attacker-data-to-policy path was established in the inspected composition and lowering chain. Ordinary interpolation values are rejected, while explicitly authored SQL remains permitted. The previous arbitrary-string capability is supported by changed-range evidence, not an independently hydrated base implementation.

Trust Boundaries and Controls

  • observed — SqlExpression recognition uses a globally registered symbol marker, and requireSqlExpression checks that marker rather than independently revalidating text. This is an authoring representation check, not authentication, provenance verification, or SQL sanitization. The public constructor accepts raw SQL text subject to tagged-literal canonicalization rules.

Resilience and Maintainability Implications

  • inferred — Construction and validation failures abort the inspected representation-building paths without applying database state. Strict serialization refusals help contain silent SQL-control drift, whereas advisory inference output still requires attention before reuse in a deployment workflow.



🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.69% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 49 functions across 50 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: TypeScript raw SQL inputs now use sql-tagged values, matching PSL.


✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR


  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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

@pkg-pr-new

pkg-pr-new Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@prisma/orm-extension-arktype-json

npm i https://pkg.pr.new/@prisma/orm-extension-arktype-json@30558

@prisma/orm-extension-middleware-cache

npm i https://pkg.pr.new/@prisma/orm-extension-middleware-cache@30558

@prisma/orm-extension-paradedb

npm i https://pkg.pr.new/@prisma/orm-extension-paradedb@30558

@prisma/orm-extension-pgvector

npm i https://pkg.pr.new/@prisma/orm-extension-pgvector@30558

@prisma/orm-extension-postgis

npm i https://pkg.pr.new/@prisma/orm-extension-postgis@30558

@prisma/orm-extension-supabase

npm i https://pkg.pr.new/@prisma/orm-extension-supabase@30558

@prisma/orm-family-mongo

npm i https://pkg.pr.new/@prisma/orm-family-mongo@30558

@prisma/orm-family-sql

npm i https://pkg.pr.new/@prisma/orm-family-sql@30558

@prisma/orm-framework

npm i https://pkg.pr.new/@prisma/orm-framework@30558

@prisma/orm-mongo

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

@prisma/orm-postgres

npm i https://pkg.pr.new/@prisma/orm-postgres@30558

@prisma/orm-sqlite

npm i https://pkg.pr.new/@prisma/orm-sqlite@30558

@prisma/orm-target-mongo

npm i https://pkg.pr.new/@prisma/orm-target-mongo@30558

@prisma/orm-target-postgres

npm i https://pkg.pr.new/@prisma/orm-target-postgres@30558

@prisma/orm-target-sqlite

npm i https://pkg.pr.new/@prisma/orm-target-sqlite@30558

@prisma/orm-toolchain

npm i https://pkg.pr.new/@prisma/orm-toolchain@30558

commit: 15a7739

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size
postgres / no-emit 240.27 KB (+0.26% 🔺)
postgres / emit 211.77 KB (+0.19% 🔺)
mongo / no-emit 199.35 KB (0%)
mongo / emit 177.2 KB (-0.01% 🔽)
cf-worker / no-emit 299.99 KB (+0.26% 🔺)
cf-worker / emit 268.13 KB (+0.16% 🔺)

github-merge-queue Bot pushed a commit that referenced this pull request Oct 6, 2026
…30619)

## Linked issue

[TML-3483](https://linear.app/prisma-company/issue/TML-3483). Related:
[TML-3020](https://linear.app/prisma-company/issue/TML-3020) (the eleven
older duplicated numbers, not touched here).

## Summary

`main` had three files named `ADR 258 - …`, so "see ADR 258" did not say
which decision it meant:

```text
ADR 258 - List cardinality has independent container and element nullability.md
ADR 258 - A model names its storage verbatim, and a rename is an operation.md
ADR 258 - A collection keeps its class through the chain.md
```

The same happened to 207, 255 and 259 in the last two weeks. The ADR
that merged first keeps the number. Each ADR that merged later moves to
a new number:

| Was | Now | ADR |
| --- | --- | --- |
| 255 | 262 | Block specs bind top-level block values |
| 207 | 263 | A serverless Postgres connection has the same query
interface as a `postgres()` client |
| 258 | 264 | A model names its storage verbatim, and a rename is an
operation |
| 258 | 265 | A collection keeps its class through the chain |
| 259 | 266 | The cache middleware passes data to its store, and the
store decides how to cache |

The new numbers start at 262 because `main` now has ADR 260 and open
pull requests already use 256 and 261.

The query fragments ADR (259) cited "ADR 260" for the collection-scopes
ADR in #30428, which has not merged. ADR 260 on `main` is now
the `afterTransaction` stage, so that citation is removed.
#30428 can add it back with its final number.

Every link and every mention of a moved ADR in docs, package READMEs and
project files now uses the new number. Mentions in project research
notes that describe the numbering at an earlier point in time are left
as they were. `ADR-INDEX.md` gains rows for three ADRs it was missing:
259 (query fragments), 264 (storage names) and 265 (collection class).

## Open pull requests this affects

These pull requests change a file that moves here. Git should carry
their changes to the new file name, but links they add to the old name
need updating:

- #30550 and #30613 change ADR 255 (block specs),
now ADR 262.
- #30428 changes ADR 258 (collection class), now ADR 265.

These open pull requests add an ADR whose number is already taken, so
they clash again when they merge:

- ADR 256: #30535 (language-server file watching). `main`
already has ADR 256 (mutation-default generators).
- ADR 260: #30428 (collection scopes) and #30550 /
#30558 (raw SQL is a value of `sql-expression`). `main`
already has ADR 260 (`afterTransaction` stage).

## Testing performed

- Checked every Markdown link in the changed files that points to an
ADR. All resolve. Two links that were already broken on `main` (ADR 009
and ADR 015) are unrelated and unchanged.
- Searched the repository for the old file names and the old numbers.
The only remaining matches are the earlier-point-in-time research notes
described above.

## Skill update

n/a — internal only.

## Upgrade instructions


[`upgrade-instructions/pending/adr-numbers-unique/extension/`](upgrade-instructions/pending/adr-numbers-unique/extension/instructions.md)
declares `changes: []`. The three package READMEs change only their ADR
links, so extension authors have nothing to do.

## Checklist

- [x] All commits are signed off (`git commit -s`) per the
[DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco).
- [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is
scoped to one logical concern.
- [x] Tests are updated (or `n/a` if the change is doc-only / refactor
with no behavioural delta). n/a, docs only.
- [x] The PR title is in `TML-NNNN: <sentence-case title>` form.
- [x] The **Skill update** section above is filled in.

## Notes for the reviewer

Alternatives considered:

- **Move whichever ADR has fewer references.** For 255 that would move
relation ordering instead of block specs, and no open pull request
touches it. Rejected because a rule based on merge order is simple to
apply the next time, and an earlier fix (ADR 255 to ADR 256 for
mutation-default generators) already moved the later ADR.
- **Also renumber the eleven older duplicates (159 to 224).** They have
been cited by number for months, and deciding which ADR each bare
mention means needs reading each citation. That stays with TML-3020.

Agent: etain-65


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Updated ADR references across guides, architecture documentation, and
package READMEs to reflect current numbering.
* Added ADRs to the architecture index and corrected links covering
serverless PostgreSQL connections, block specifications, storage naming,
query collections, and cache middleware.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Base automatically changed from tml-3288-sql-expression-places to main October 8, 2026 17:01
@wmadden-electric wmadden-electric changed the title TML-3289: The TypeScript contract builder takes sql values for raw SQL TML-3289: Raw SQL in a TypeScript contract is a sql… value, as it is in PSL Oct 8, 2026
…s in PSL

The same change as #30558, on one commit from main. #30558 was built on stacked branches and carried 300 commits, more than the DCO check can read.

Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
@wmadden-electric
wmadden-electric force-pushed the tml-3289-sql-expression-ts branch from 57f4c40 to 7bdab1c Compare October 9, 2026 12:15
@wmadden-electric
wmadden-electric added this pull request to the merge queue Oct 9, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 9, 2026
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
@wmadden-electric
wmadden-electric added this pull request to the merge queue Oct 9, 2026
Merged via the queue into main with commit 24258f3 Oct 9, 2026
24 checks passed
@wmadden-electric
wmadden-electric deleted the tml-3289-sql-expression-ts branch October 9, 2026 13:19
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.

2 participants