Skip to content

Merge main into prisma-8 - #1416

Merged
borisno2 merged 35 commits into
prisma-8from
claude/integrate-main-into-prisma-8
Sep 11, 2026
Merged

Merge main into prisma-8#1416
borisno2 merged 35 commits into
prisma-8from
claude/integrate-main-into-prisma-8

Conversation

@borisno2

@borisno2 borisno2 commented Sep 11, 2026

Copy link
Copy Markdown
Member

Merges main into prisma-8 so the release pull request can follow. Targets prisma-8, not main.

prisma-8 is an orphan branch

git merge-base origin/main origin/prisma-8 returns nothingprisma-8's root commit 4e7bd4b3 has no parent. A plain git merge would have refused, and --allow-unrelated-histories would have treated every file prisma-8 deleted as an add.

The content fork point is 93a3736b (spec(prisma-8): the architecture spec handed off from the Wayfinder map (#1119)) — exactly 28 main commits back, matching the brief, and the candidate with the smallest diff to prisma-8's root tree (69 files vs 73 for the next candidate). The merge was therefore computed explicitly against that base with git merge-recursive 93a3736b -- HEAD origin/main, then committed with both real parents.

A side effect worth noting: prisma-8 now contains main's history, so the follow-up prisma-8main release PR is an ordinary merge rather than a second unrelated-histories problem.

The real overlap

84 files changed on main since the fork, 855 on prisma-8; 55 overlap. 27 conflicted. The rest auto-merged — and four of those auto-merges were wrong, which is where the damage was.

The access-control judgement (#1385)

Property: a non-sudo caller must not reach rows outside the parent's subtree through a nested relationship write, and must not name a target row it cannot read.

main closed this by gating: refuse nested set/updateMany/deleteMany for non-sudo callers (NestedRelationInputError), and gate disconnect's target-row form behind the target list's operation.query access. sudo() was exempt from all of it.

prisma-8 closed it by deletion: ADR-0050 removes nested relation input from the write payload entirely. packages/core/src/context/relationship-input.ts refuses create, update, delete, connectOrCreate, disconnect, set, updateMany and deleteManyeight kinds against main's three — and refuseNestedRelationInput has no sudo branch at all.

Verdict: the property survives, strictly stronger. prisma-8 refuses a superset of main's kinds and refuses them unconditionally. main's own commit anticipated this in its docblock, calling its fix "an interim fail-closed fix mirroring ADR-0050's own refusal shape" for work "that major deletes wholesale".

main's disconnect gate is moot, not dropped. disconnect does not exist here; the replacement is assigning null to the relationship field, which lowerRelationInput lowers to a foreign-key column on the row being written. No target row is named, so there is no target-list read to gate — the unlinking carries the enclosing write's own operation and field-level access. The reachability query main's gate reused survives as reachable() and is extended on this line to the direct foreign-key spelling (authorId), closing the probing oracle #1331 that main does not have.

Verified by running, not by reading. In the merged tree secured-write.test.ts drives all eight kinds end-to-end through context.db.Post.create"nested <kind> is refused, and nothing is written" — and "a sudo payload naming a synthetic back-relation is refused too" pins the sudo half. relationship-input.test.ts, related-actions.test.ts and relation-connect.test.ts pass alongside them (core: 87 files, 1623 tests).

Two structural facts back the "unconditional" claim: refuseNestedRelationInput has no _isSudo branch, and write-pipeline.ts calls it at :182 (the caller's inputData) and :339 (the post-hook resolvedData), neither guarded — so a resolveInput hook that assembles nested relation input is refused too, which main's dispatch-time check did not cover.

packages/core/src/index.test.ts and packages/cli/src/generator/types.test.ts carry negative assertions that the deleted fragment/CustomDB names cannot quietly return; both pass, which is independent evidence the merge did not resurrect the old surface.

main's nested-operations.ts is deleted. Its re-export of NestedRelationInputError from packages/core/src/index.ts had auto-merged into a duplicate export of a deleted module, alongside prisma-8's own export of the same name from relationship-input.ts; the dead one is removed.

The second contested change: the hook context (#1176 / #1179)

The two branches made directly contradictory decisions here, independently.

  • main's ADR-0066: a hook's context is the full StackContextsudo(), withSession(), transaction(), serverAction — bound to the write's own transaction.
  • prisma-8's ADR-0052: StackBaseContext ("nothing that can start a transaction or change who is asking") is what a hook sees; StackContext is for server actions and page components. This is emitted per app by the generator as BaseContext vs Context, baked into the contract and into all twelve converted examples.

Resolved in favour of prisma-8. Reverting the split to satisfy main would be a rewrite of the prisma-8 spec, not a merge resolution. This is a capability reduction, never a security regression — a narrower hook context grants strictly less.

Because this was a deliberate drop rather than a silent one, three documents that asserted main's behaviour and auto-merged into this tree were corrected rather than left to rot:

main's _resolveOutputChain threading through sudo()/withSession() is moot by construction, not dropped: the only contexts exposing those operations are request contexts, whose chain is empty; the contexts carrying a non-empty chain (hook contexts) do not expose them.

The third: _count field-level read access (#1120 / #1111)

main added a fieldAccess parameter to resolveCountAccessEntryForList, gating a _count on the counting list's own field-level read access. prisma-8's copy had no such parameter.

Investigated every path by which a relationship count reaches a non-sudo caller here. The property holds, by a different mechanism: secured/include.ts:498-510 omits a denied relation pre-query (return null at :559, so the count is never asked of the database), and for a row-dependent rule field-visibility.ts:353-362 drops the key post-query, with restoreReductions (secured/read.ts:965-968) writing the count back only under keys Field Visibility kept. main's gated function is dead code here — its only caller, buildAccessScopedCountSelect, is unreachable from context.db, which has no findMany and no caller-supplied _count.

Rather than rely on unreachability, main's gate is ported onto prisma-8's function (1ead103) so a future caller cannot reopen #1111 by omitting it, and main's test of it passes.

Four auto-merges that were silently wrong

None of these conflicted. All four compiled or tested green somewhere else.

  1. packages/cli/src/generator/lists.tsmain's Hook args are not generic over the Prisma client, so a hook's context is untypeable in a consumer — AccessControlledDB<any> is assignable to nothing #1211 added prisma: import('./prisma-client/client.ts').PrismaClient and db: import('./types.ts').CustomDB to every list's TypeInfo. Neither module exists on this line. This would have written a bundle that does not type-check into every one of the twelve examples — a generation break the package suites do not see. Reverted.
  2. packages/core/src/index.ts — duplicate NestedRelationInputError export of a deleted module (above).
  3. hook-pipeline.ts / hooks/index.ts — imported StackContext from context/index.js, which does not export it here.
  4. access-filter.ts — called resolveCountAccessEntryForList with main's third argument against a two-parameter signature.

Five main-only CLI test files covering the surface ADR-0052 replaced (#1233, #1287, #1328, #1232) are removed; their subjects are carried by prisma-8's contract-keyed suites.

Other overlapping files

Area main asserted prisma-8 asserted Merged
access/types.ts Augmented* trailing overloads (#1287/#1328), AccessContext<TPrisma, TDb> SecuredQuery, OrmClient, contract-keyed generics prisma-8 — surface deleted; TDb subsumed by the first-class DB parameter
config/types.ts TypeInfo.prisma / .db TypeInfo.output / .needs prisma-8; #1232's property holds structurally (StackBaseContext<DB,…>)
internal.ts exports Fragment/FieldSelection/ResultOf query/index.js deleted prisma-8
cli/generator/{context,types}.ts, snapshots Prisma 7 bundle, CustomDB, prisma-client/client.ts contract-based bundle prisma-8
docs/{hooks,context-api,migrate-from-keystone}.md old surface rewritten prisma-8
docs/concepts/access-control.md documented disconnect as gated and sudo as exempt eight kinds refused unconditionally rewrittenmain's section auto-merged and was false here (err.listKey vs listName, context.db.post casing, a sudo exemption that does not exist)
9 changesets consumed by main's Version Packages present consumed; content verified present in the CHANGELOGs at 0.42.3

Changes from main that this line makes meaningless — stated, not dropped

Dependency bumps that do apply are carried: @typescript-eslint/parser 8.69.0, globals 17.12.0, @types/react ^19.2.18, and the vitest family aligned on 4.1.11 (main's #1185 bumped @vitest/browser alone, leaving an unmet peer).

ADR-0066 collision

Both branches claimed 0066. main's is the published number and keeps it. prisma-8's 0066-a-plugins-write-of-its-own-column-runs-no-hook.md0068, with all 18 references updated across CLAUDE.md, CONTEXT.md, packages/core/{CLAUDE.md,src/extend.ts,src/config/types.ts,src/access/multi-column-read-write.test.ts}, packages/rag/{CLAUDE.md,src/config/plugin.ts,plugin.test.ts,embedding-write.test.ts}, docs/content/how-to/rag-advanced.md and two changesets. One of those 18 was missed on the first pass and caught in review: examples/rag-ollama-demo/README.md still read (ADR-0066) on a sentence about writePluginOwnedField running no hook — a reference that resolved to a real but unrelated record, which is why neither check:adr-duplicates nor the link check flagged it. It now reads ADR-0068. No reference is left at 0066. An earlier revision of this description claimed two were deliberately left there; that was wrong. The two documents that assert main's decision — ADR-0052's #1211 amendment and packages/core/CLAUDE.md's "#1176" section — were rewritten in prose to the merged behaviour and cite no ADR number at all, so there was nothing to leave. grep -rn 'ADR-0066' over the tree now returns nothing. pnpm check:adr-duplicates68 numbers checked, no duplicates.

Verified by running

Gate Result
pnpm build 11/11 tasks
Package suites core 87 files / 1623 tests; cli 43/414; auth 23/505; ui 62/671; all others green
Twelve examples regenerate no drift (git diff --exit-code over the 13 CI paths → 0)
Drift gate still bites mutated examples/starter/prisma/contract.ts → exit 1; restored → exit 0
Examples type-check and build 21/21 tasks
examples/blog access-control suite 28 checks passed (real database)
examples/mcp-demo suite 10 checks passed (real database)
check:doc-ts-blocks exit 0 — 126 blocks, 89 compile
check:doc-ts-blocks --self-test 55 blocks, 0 mismatches
check:client-side-effect-imports exit 0
check:prisma-error-codes exit 0 — 1482 files, 26 literals across 7 allowlisted files
check:adr-duplicates exit 0
pnpm --filter opensaas-stack-docs link-check 10 paths, 32 redirects, 34 pages valid
docs build passes
pnpm lint 0 errors (1 pre-existing warning in migration-generator.ts, untouched by this merge)
pnpm format:check clean
pnpm manypkg check workspaces valid
Changesets 109 minor, 17 patch, 0 major

No documentation-checker excuse was added, widened or removedgit diff origin/prisma-8 -- scripts/ .github/ is empty, and neither branch touched those paths since the fork.

Flakes observed, neither mine

  • packages/core/src/db/dev-database.test.ts > two instances in one process take distinct ports fails under parallel turbo, passes standalone. packages/core/src/db/ is byte-identical to origin/prisma-8 — port contention, not this merge.
  • packages/ui browser-mode suite failed once under turbo with Failed to search relationship options: network error, passed on retry and standalone (62/671).

Not verified

  • The two database-backed example suites were run against a local Postgres rather than CI's service container, and mcp-demo needed BETTER_AUTH_URL supplied by hand (CI sets it).
  • pnpm test:e2e (Playwright) was not run.

🤖 Generated with Claude Code

borisno2 and others added 30 commits September 3, 2026 18:15
…nship (#1120)

A relationship hidden via `access: { read: () => false }` still leaked its
true count through both the admin list view's _count path and a caller-
supplied `_count.select` — neither ever checked the counting list's own
field-level read access, only the related list's operation-level query
access. resolveCountAccessEntryForList now takes the counting field's own
FieldAccess and denies the count when field-level read access denies it,
mirroring the check filterReadableFields already runs for an ordinary
include of the same field. A synthetic back-relation has no field of its
own and stays exempt.

Closes #1111


Claude-Session: https://claude.ai/code/session_01BycBeXShej1q9zyF5EiuhT

Co-authored-by: Claude <noreply@anthropic.com>
Bumps [@vercel/blob](https://github.com/vercel/storage/tree/HEAD/packages/blob) from 2.6.1 to 2.8.0.
- [Release notes](https://github.com/vercel/storage/releases)
- [Changelog](https://github.com/vercel/storage/blob/main/packages/blob/CHANGELOG.md)
- [Commits](https://github.com/vercel/storage/commits/@vercel/blob@2.8.0/packages/blob)

---
updated-dependencies:
- dependency-name: "@vercel/blob"
  dependency-version: 2.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
)

The Hook arguments section keyed `item`, `resolvedData` and `needs` but left
`context` unparameterised, so the Prisma 8 keying would have re-landed the
gap #1211 reports on main: a hook's context resolves over `PrismaClientLike =
any`, `AccessControlledDB<any>` is assignable to nothing, and a typo'd
delegate type-checks.

Records that TypeInfo carries the app's DB and core's hook types read
`context` off it, and that main's pre-contract fix establishes the same seam
rather than a second mechanism.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
#1214)

TypeInfo gains a `prisma` member (defaulted to PrismaClientLike, fully
additive) and every list/field hook-args type now types `context` as
StackContext<TTypeInfo['prisma']> / AccessContext<TTypeInfo['prisma']>
instead of the unparameterised default. Previously context.db resolved
through AccessControlledDB<any> - a mapped type over keyof any that
contributes no named property - so a hook's context was assignable to
nothing app-specific and consumers needed `context as unknown as Context`
to use it anywhere typed.

The CLI generator emits the new `prisma` member on each list's
Lists.<List>.TypeInfo, pointing at the project's own generated
PrismaClient, so no config changes are required downstream.

Adds a dedicated compile-time regression test (against core's real built
types, not a stub) proving context.db is now assignable to the generated
CustomDB with no cast and resolves real per-list row types, and extends
the #952 large-schema fixture to also generate the Lists namespace and
exercise list-level/field-level hooks reading context.db without
reintroducing TS2589.

Closes #1211


Claude-Session: https://claude.ai/code/session_01UUfo5pf9kkNAYQ54X9EEhp

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
#1264)

context.db.<list>.findUnique/findFirst/findMany (and singleton get) only
had the generic *Args-based overload, so passing a query fragment still
compiled but the return type stayed the unnarrowed {List}GetPayload<T>
instead of ResultOf<fragment> — an unselected field was silently readable
with no compile error. These methods now carry the same fragment overload
core's AccessControlledDB exposes (AugmentedFindUnique/First/Many), hand-
written to match rather than wrapped through core's generics, which
reintroduced #952's TS2589-style depth blowup on schemas with cross-
referencing GetPayload chains.

Full bidirectional assignability between AccessControlledDB and CustomDB
(and dropping the `as unknown as` cast in the generated context factory)
remains blocked by two narrower, separately-tracked gaps: virtual-field
folding (#1232) and singleton get() having no AccessControlledDB
counterpart — documented at the cast site.


Claude-Session: https://claude.ai/code/session_01KzSpQGvfQCzFrvBJ2cRZAL

Co-authored-by: Claude <noreply@anthropic.com>
#1277)

BaseContext/Context previously re-declared StackContext's extra members
(serverAction, sudo, withSession) by hand, and the list was missing
`transaction` — so `context.transaction(...)` failed to typecheck even
though it works at runtime. BaseContext now omits db/session/serverAction/
transaction/sudo/withSession from StackContext instead of AccessContext, so
any future StackContext member reaches the generated Context automatically;
the four self-referential members are re-declared on Context to resolve to
the generated type.

Closes #1261


Claude-Session: https://claude.ai/code/session_01V5k8pivgut8oLJGe8jz5sU

Co-authored-by: Claude <noreply@anthropic.com>
Bumps [sharp](https://github.com/lovell/sharp) from 0.35.3 to 0.35.4.
- [Release notes](https://github.com/lovell/sharp/releases)
- [Commits](lovell/sharp@v0.35.3...v0.35.4)

---
updated-dependencies:
- dependency-name: sharp
  dependency-version: 0.35.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…#1275)

Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.63.0 to 8.69.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.69.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.69.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [tailwind-merge](https://github.com/dcastil/tailwind-merge) from 3.5.0 to 3.6.0.
- [Release notes](https://github.com/dcastil/tailwind-merge/releases)
- [Commits](dcastil/tailwind-merge@v3.5.0...v3.6.0)

---
updated-dependencies:
- dependency-name: tailwind-merge
  dependency-version: 3.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [globals](https://github.com/sindresorhus/globals) from 17.8.0 to 17.12.0.
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](sindresorhus/globals@v17.8.0...v17.12.0)

---
updated-dependencies:
- dependency-name: globals
  dependency-version: 17.12.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…1287) (#1289)

* Fix #1287: generated Crud read methods not assignable to structural seams

0.42.1's fragment overload on findUnique/findFirst/findMany (and singleton
get) left a two-member overload set (fragment, then the generic
SelectSubset-based member) that TypeScript won't structurally match against
a plain seam once SelectSubset carries Prisma's real conditional-
intersection shape, and broke Parameters<> (resolves against the last
overload, collapsing an unresolved generic T to never).

Adds a third, trailing, non-generic overload member over the plain
{List}FindXArgs to each method. Verified empirically that its position
among the three doesn't affect assignability, and that ordinary calls
(bare, or with select/include) still resolve through the generic member
first, keeping its T-narrowed return.

Closes #1287

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gxv5F6T184gXxqWd7c8mqf

* Restrict the trailing overload member so it can't bypass select+include exclusivity

Code review on #1289 caught a regression the first commit introduced: giving
the trailing member the full {List}FindXArgs (select/include both present
as independently-optional keys) let overload resolution fall through to it
for a `{ select, include }` call the generic member's SelectSubset guard
correctly rejects — silently accepting a combination Prisma itself forbids.

Restrict the trailing member's parameter type to omit select/include/query
entirely (Pick<..., 'where'> for findUnique, Omit<..., 'select' | 'include'
| 'query'> elsewhere). A literal call naming those keys no longer
structurally matches this member at all, so it stays reachable only by the
seam-assignability and Parameters<> checks it exists for, never by a real
call. Verified empirically that the exclusivity guard, seam assignability,
and Parameters<> resolution all hold together.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gxv5F6T184gXxqWd7c8mqf

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
… getContext (#1337)

* Add trailing overload to core's AugmentedFind* and thread TDb through getContext

Core's AugmentedFindUnique/AugmentedFindFirst/AugmentedFindMany now carry the
same trailing non-generic overload the generated CustomDB emits (#1287), and
getContext gains a third, unconstrained, defaulted TDb type parameter so a
caller can ask for StackContext<TPrisma, CustomDB> directly instead of
erasing the type with `as unknown as`. The generated context.ts factory uses
this to drop to a single, honest `as Context<TSession>` cast.

Closes #1328

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0167m3BzYP3jiUABj89QWeoF

* Note the Parameters<> resolution change from the trailing overload in the changeset

Per review feedback on PR #1337 from the issue author, who verified the fix
against a real downstream call site and confirmed TDb is the load-bearing
half; documents the trailing overload's effect on Parameters<> as an
observable (intentional) side effect.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0167m3BzYP3jiUABj89QWeoF

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Bumps [@radix-ui/react-popover](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/popover) from 1.1.20 to 1.1.23.
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/popover/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/popover)

---
updated-dependencies:
- dependency-name: "@radix-ui/react-popover"
  dependency-version: 1.1.23
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the aws-sdk group with 2 updates: [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) and [@aws-sdk/s3-request-presigner](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages/s3-request-presigner).


Updates `@aws-sdk/client-s3` from 3.1121.0 to 3.1127.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1127.0/clients/client-s3)

Updates `@aws-sdk/s3-request-presigner` from 3.1126.0 to 3.1127.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages/s3-request-presigner/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.1127.0/packages/s3-request-presigner)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-s3"
  dependency-version: 3.1127.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: aws-sdk
- dependency-name: "@aws-sdk/s3-request-presigner"
  dependency-version: 3.1127.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: aws-sdk
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [@radix-ui/react-select](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/select) from 2.3.4 to 2.3.7.
- [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/select/CHANGELOG.md)
- [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/select)

---
updated-dependencies:
- dependency-name: "@radix-ui/react-select"
  dependency-version: 2.3.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [pg](https://github.com/brianc/node-postgres/tree/HEAD/packages/pg) and [@types/pg](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/pg). These dependencies needed to be updated together.

Updates `pg` from 8.22.0 to 8.23.0
- [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md)
- [Commits](https://github.com/brianc/node-postgres/commits/pg@8.23.0/packages/pg)

Updates `@types/pg` from 8.20.0 to 8.23.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/pg)

---
updated-dependencies:
- dependency-name: pg
  dependency-version: 8.23.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: "@types/pg"
  dependency-version: 8.23.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…sconnect (#1385)

Nested set/updateMany/deleteMany were a pass-through straight to Prisma
with no target-list access check and no hooks. Non-sudo contexts now get
a NestedRelationInputError instead; sudo is unaffected. Nested
disconnect's target-row form ({ disconnect: { id } }) now requires the
target list's operation.query access, reusing the connect reachability
check; the to-one boolean form ({ disconnect: true }) is unchanged.

Closes #1384


Claude-Session: https://claude.ai/code/session_01HHpw6shZoER4AjaaYg6QC9

Co-authored-by: Claude <noreply@anthropic.com>
prisma-8 is an orphan branch: it shares no git merge-base with main. The
content fork point is 93a3736 (#1119), 28 main commits back, so this is a
three-way merge computed against that base rather than a merge of unrelated
histories.

Resolutions of substance:

- Nested write access (#1385 on main vs ADR-0050 here). main refused nested
  set/updateMany/deleteMany for non-sudo callers and gated disconnect's
  target-row form. prisma-8 removed nested relation input from the payload
  outright, unconditionally, sudo included. The prisma-8 refusal is the
  strictly stronger property; main's nested-operations.ts is deleted here and
  its dead re-export of NestedRelationInputError dropped from core's barrel
  (it auto-merged into a duplicate export of a deleted module).

- Hook context (#1176/#1179, main's ADR-0066). main gave hooks the full
  StackContext; prisma-8's ADR-0052 split BaseContext (no sudo/withSession/
  transaction) from Context, in core, in the generator, and in every emitted
  bundle. prisma-8's split stands; main's ADR-0066 is marked superseded, and
  its doc text that auto-merged into packages/core/CLAUDE.md and ADR-0052's
  #1211 amendment is corrected to the merged behaviour.

- _count field-level read access (#1120/#1111). Verified the property still
  holds: secured/include.ts omits a denied relation pre-query and Field
  Visibility drops it post-query. main's fieldAccess parameter on
  resolveCountAccessEntryForList is moot — its only caller is unreachable
  from context.db here.

- The old typed-query/generated-bundle surface (Fragment/ResultOf,
  AugmentedFind*, AccessContext<TPrisma, TDb>, CustomDB, Prisma.*Args) is
  taken from prisma-8 throughout; prisma-8 replaced it.

- ADR-0066 was claimed by both branches. prisma-8's is renumbered to 0068 and
  every reference to it updated.

- Dependency bumps from main carried forward and aligned monorepo-wide:
  @types/react ^19.2.18, pg ^8.23.0, @types/pg ^8.23.1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Picks up main's dependabot bumps (@typescript-eslint/parser 8.69.0, globals
17.12.0, @types/react 19.2.18) and the monorepo-wide pg 8.23.0 /
@types/pg 8.23.1 alignment the merge settled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
borisno2 and others added 4 commits September 11, 2026 16:26
- access-filter.ts called resolveCountAccessEntryForList with main's third
  `fieldAccess` argument against prisma-8's two-parameter signature.
- hook-pipeline.ts and hooks/index.ts imported StackContext from
  context/index.js, which does not export it on this line; both carried
  main's #1179 hook-context typing, reverted to prisma-8's AccessContext.

Also reverts the pg/@types/pg bump from main's #1380: @prisma/orm-postgres
8.0.0-rc.8 pins pg 8.22.0 and @types/pg 8.20.4 exactly, and a second copy of
@types/pg makes core's testing harness fail to compile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
prisma-8's resolveCountAccessEntryForList had lost the fieldAccess parameter
#1120 added, because the secured read gates the same field one layer up in
secured/include.ts and the function's only caller is unreachable from
context.db here. Porting the parameter back keeps the gate attached to the
entry itself, so a future caller cannot reopen #1111 by omitting it, and
restores main's own test of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…B members

main's #1211 added `prisma: import('./prisma-client/client.ts').PrismaClient`
and `db: import('./types.ts').CustomDB` to every list's TypeInfo. Both name a
module prisma-8 does not emit, so the merge would have written a bundle that
does not type-check into every example — a generation break the package
type-suites do not see. Reverted to prisma-8's generator, with main's test of
the removed member.

Also removes five main-only CLI test files covering the generated surface
ADR-0052 replaced: the fragment/CustomDB seam (#1233, #1287, #1328) and the
hook context.db typing (#1232). Their subjects are carried by prisma-8's own
contract-keyed suites.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
main's #1185 bumped @vitest/browser to ^4.1.11 while vitest stayed ^4.1.10,
leaving an unmet peer. Every vitest-family specifier moves to ^4.1.11 rather
than reverting the bump.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@changeset-bot

changeset-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 02c526d

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

This PR includes changesets to release 9 packages
Name Type
@opensaas/stack-rag Minor
@opensaas/stack-cli Minor
@opensaas/stack-core Minor
@opensaas/stack-auth Minor
@opensaas/stack-storage Minor
@opensaas/stack-tiptap Minor
@opensaas/stack-ui Minor
@opensaas/stack-storage-s3 Minor
@opensaas/stack-storage-vercel Minor

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

@vercel

vercel Bot commented Sep 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
stack-docs Ready Ready Preview Sep 11, 2026 7:18am UTC

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for Core Package Coverage (./packages/core)

Status Category Percentage Covered / Total
🟢 Lines 93.02% (🎯 81%) 3509 / 3772
🟢 Statements 91.38% (🎯 76%) 3956 / 4329
🟢 Functions 95.87% (🎯 78%) 791 / 825
🟢 Branches 86.02% (🎯 71%) 2672 / 3106
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/core/src/access/access-filter.ts 82.83% 81.25% 90.47% 86.95% 166, 294, 345, 352, 375, 560, 691, 692, 699, 768, 794, 796-797, 876, 879-881, 890-898, 939-940, 972-983, 1011-1040
packages/core/src/access/relationship-count.ts 100% 92.85% 100% 100%
Generated in workflow #2232 for commit 02c526d by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for UI Package Coverage (./packages/ui)

Status Category Percentage Covered / Total
🔵 Lines 78.7% 244 / 310
🔵 Statements 78.43% 251 / 320
🔵 Functions 69.81% 74 / 106
🔵 Branches 67.51% 160 / 237
File CoverageNo changed files found.
Generated in workflow #2232 for commit 02c526d by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for CLI Package Coverage (./packages/cli)

Status Category Percentage Covered / Total
🔵 Lines 72.38% 1701 / 2350
🔵 Statements 72.16% 1823 / 2526
🔵 Functions 80.27% 293 / 365
🔵 Branches 59.74% 837 / 1401
File CoverageNo changed files found.
Generated in workflow #2232 for commit 02c526d by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for Auth Package Coverage (./packages/auth)

Status Category Percentage Covered / Total
🔵 Lines 91.2% 280 / 307
🔵 Statements 89.94% 313 / 348
🔵 Functions 96.05% 73 / 76
🔵 Branches 82.38% 262 / 318
File CoverageNo changed files found.
Generated in workflow #2232 for commit 02c526d by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for Storage Package Coverage (./packages/storage)

Status Category Percentage Covered / Total
🔵 Lines 77.67% 247 / 318
🔵 Statements 77.68% 275 / 354
🔵 Functions 87.09% 81 / 93
🔵 Branches 77.1% 256 / 332
File CoverageNo changed files found.
Generated in workflow #2232 for commit 02c526d by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for RAG Package Coverage (./packages/rag)

Status Category Percentage Covered / Total
🔵 Lines 91.29% 556 / 609
🔵 Statements 90.58% 606 / 669
🔵 Functions 97.39% 112 / 115
🔵 Branches 84.26% 375 / 445
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/rag/src/config/plugin.ts 93.92% 82.5% 93.1% 96.22% 253, 265, 275-281, 362, 381-385, 397, 464, 466, 504, 543
Generated in workflow #2232 for commit 02c526d by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for Storage S3 Package Coverage (./packages/storage-s3)

Status Category Percentage Covered / Total
🔵 Lines 100% 40 / 40
🔵 Statements 100% 40 / 40
🔵 Functions 100% 9 / 9
🔵 Branches 100% 19 / 19
File CoverageNo changed files found.
Generated in workflow #2232 for commit 02c526d by the Vitest Coverage Report Action

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for Storage Vercel Package Coverage (./packages/storage-vercel)

Status Category Percentage Covered / Total
🔵 Lines 100% 68 / 68
🔵 Statements 100% 71 / 71
🔵 Functions 100% 15 / 15
🔵 Branches 97.87% 46 / 47
File CoverageNo changed files found.
Generated in workflow #2232 for commit 02c526d by the Vitest Coverage Report Action

@borisno2 borisno2 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

APPROVE

Reviewed from a fresh gh pr checkout 1416 worktree. I did not take "merged cleanly" or "CI green" as evidence for anything; every claim below was re-derived by running.

Verdict

No blocking findings. No security property is weakened, no side of the merge is lost, every example still works. One follow-up residue (a stale ADR pointer) and one PR-description inaccuracy, both noted below.


1. The access-control judgement (#1385) — verified by running, and it holds

I wrote my own probe rather than trusting secured-write.test.ts, and ran it against the real harness. 24/24 passed:

Probe Result
8 nested kinds (create / update / delete / connectOrCreate / disconnect / set / updateMany / deleteMany) on create, under sudo(), owning relationship field all 8 → NestedRelationInputError
the same 8 on update, under sudo() all 8 → NestedRelationInputError
the same 8 injected by a resolveInput hook, caller payload clean ({ title } only), non-sudo and sudo all 8 → NestedRelationInputError

Control probe (to prove the harness is real and the gate is not simply refusing everything): { connect: { id } }, null-clear, a plain sudo create and a read-back all succeed. Passed.

The second refusal genuinely fires. I mutated it out — commented write-pipeline.ts:339 (refuseNestedRelationInput(…, resolvedData)) — and all 8 post-hook probes went red immediately. Worth recording what they degrade to: without :339 the payload falls through to lowerRelationInput, which throws MalformedRelationInputError, so nothing reaches the driver even then — but it happens after the hook chain and beforeOperation side effects have run, and under the wrong error name. The second call site is load-bearing, not belt-and-braces.

The "unconditional" claim is structural and I confirmed it by reading: refuseNestedRelationInput has no _isSudo branch, and neither call site (:182 on inputData, :339 on resolvedData) is guarded. main's three-kind gate was sudo-exempt and dispatch-time-only. Eight kinds, no exemption, checked twice — strictly stronger. Agreed.

2. The disconnect gate is genuinely moot — confirmed

Confirmed by reading relationship-input.ts end to end, not by taking the summary:

  • reachable() survives and is the same shape main's gate reused: target list's operation.query ANDed with the identity criterion, evaluated in the database, non-existent and unreadable folded into one answer.
  • It now covers both spellings — lowerRelationInput calls it for kind: 'owning' ({ connect: { id } }) and for kind: 'foreignKey' (authorId named directly). That is the #1331 oracle closed, which main does not have.
  • The disconnect replacement — assigning null — writes lowered[key.column] = null. It names no target row, so there is nothing for a target-list read to gate.
  • The unlinking is not ungoverned: filterWritableFields runs at :342, lowerRelationInput at :355, so the null is gated by the owning relationship field's own write access first. field-access.ts:313-318 maps the raw <field>Id column back to that same owning field, so the FK spelling cannot dodge it either.

No spelling reaches a row the caller could not otherwise reach. Reasoning confirmed.

3. The four auto-merges, and the hunt for a fifth

All four confirmed fixed:

  1. cli/generator/lists.ts — byte-identical to origin/prisma-8; no CustomDB / prisma-client/client.ts members. Proved it the hard way: regenerated all twelve examples plus the contract fixture, then pnpm build:examples20/20 tasks, which is a full tsc/Next type-check of every emitted bundle. The only surviving mentions of CustomDB are the negative assertions in types.test.ts:161.
  2. core/src/index.ts — single export of NestedRelationInputError, from context/relationship-input.js (:178-185). context/nested-operations.ts is absent. File is byte-identical to prisma-8.
  3. hook-pipeline.ts / hooks/index.ts — byte-identical to prisma-8; no StackContext import from context/index.js.
  4. access-filter.ts — the 3-argument call now meets a 3-parameter signature; packages/core 87 files / 1623 tests pass.

I looked for a fifth and did not find one. Method, so you can judge how complete that is: I diffed the merged tree against both parents and took the intersection — the set of files that differ from prisma-8 and from main, i.e. all genuinely merged content. It is 45 files, and only seven are source: access-filter.ts, access-filter.test.ts, relationship-count.ts (the #1111 port), and config/types.ts / extend.ts / multi-column-read-write.test.ts / rag/src/* (pure ADR renumber — I diffed them and every changed line is 00660068 and nothing else). Everything else is manifests, CHANGELOGs, changesets, docs and the lockfile, each of which I walked:

  • Every main-changed file that this PR does not touch (30 of them) is accounted for: 5 are deleted by prisma-8 outright, the rest are the CLI generator surface and core context surface ADR-0050/0052 replaced, plus three docs prisma-8 rewrote.
  • Every main dependency bump is carried except pg/@types/pg; nothing else silently reverted.
  • All 9 consumed changesets verified present verbatim in the CHANGELOGs at 0.42.3.
  • pnpm install --frozen-lockfile resolves clean.

4. The hook-context resolution — right call, and the superseding is justified

Resolving for prisma-8 is correct. ADR-0052's BaseContext / Context split is not a local preference: it is emitted per app by the generator, baked into the contract, and instantiated across twelve converted examples. Satisfying main's ADR-0066 would mean re-opening a spec, which is not a merge resolution.

"A capability reduction, never a security regression" holds. A hook context that cannot reach sudo(), withSession() or transaction() grants strictly less authority than one that can — the direction of the change is monotone. I checked the one case where a reduction could have pushed callers somewhere worse: a plugin needing to write a column the application is denied. That has a first-class replacement (writePluginOwnedField, ADR-0068), which exists and is tested. And the surviving half — a hook's db bound to the write's own transaction client (ADR-0010/0028/0023) — is intact.

Marking main's ADR-0066 superseded is justified. Two things make it legitimate rather than presumptuous: the record is arriving in this tree as part of this merge, so the integrator is the right person to state its status here; and the note does not rewrite the record's argument — it adds a status line and a scoped block naming exactly what is reversed (the four derive-a-new-context operations), exactly what survives (the transaction binding, and why the record was written), and why _resolveOutputChain is moot by construction rather than dropped. That is the correct shape for a supersede. The three corrected documents check out against the code: access-control.md's new section names err.listName / fieldKey / kinds (matching the class — main's err.listKey was wrong), and its { tags: { deleteMany: {} } } example does land on NestedRelationInputError rather than NonOwningRelationInputError, because kindsIn is checked before the inverse branch.

5. The ported #1111 gate — correct, and cannot refuse anything legitimate

The port is faithful to main's: same optional fieldAccess parameter, same isFieldReadableForPredicate evaluation, same "checked before the related list's query access", same undefined-means-exempt for a synthetic back-relation (which has no field of its own on the counting list). main's test came across with it and passes.

It cannot refuse anything legitimate: the only caller is buildAccessScopedInclude, which has no in-repo caller outside its own tests — context.db reaches counts through secured/include.ts. The one behavioural edge (a row-dependent read rule denies at predicate time) is main's own documented semantics, and it is unreachable here anyway. Defensive parity on a dormant surface, which is the right call over relying on unreachability.

6. The pg revert — confirmed

node_modules/.../@prisma/orm-postgres@8.0.0-rc.8/package.json pins "pg": "8.22.0" and "@types/pg": "8.20.4"exact, not ranges. Confirmed nothing else on main wanted the newer one: #1380 touched exactly one manifest, examples/rag-openai-chatbot/package.json, and on this line that example no longer declares pg at all. Revert is correct and costs nothing.

7. The renumber — one missed reference (follow-up, not blocking)

pnpm check:adr-duplicates68 numbers checked, no duplicates. 0066 (main's, hook context), 0067 and 0068 all present and distinct. I grepped every ADR-0066 / ADR-0068 reference in the tree and checked each against its intended record. 17 are correct. One is not — see the inline comment: examples/rag-ollama-demo/README.md:234 still says (ADR-0066) where it means ADR-0068, so it now points at a real but wrong record. The duplicate checker cannot catch this — it checks numbers, not references.

Related, minor: the PR body says "The two references that meant main's record (ADR-0052, packages/core/CLAUDE.md) were left at 0066." There are no ADR-0066 references anywhere in the tree except the stale one above — those two documents reference the decision by prose, not by number. The description overstates; the tree is fine.


Everything else, re-run locally

Gate Result
pnpm install --frozen-lockfile clean
pnpm build 11/11
Twelve examples + contract fixture regenerate no drift (git diff --exit-code → 0)
Drift gate still bites added a field to examples/starter/opensaas.config.ts, regenerated → exit 1, naming contract.ts / contract.d.ts / contract.json; restored → exit 0
pnpm build:examples 20/20 (type-checks every emitted bundle)
examples/starter boots dev database provisioned (6 operations), GET /admin200
Example suites (starter, starter-auth, json-demo) 11/11 tasks
packages/core 87 files / 1623 tests
packages/cli 43 / 414
packages/auth 23 / 505
check:doc-ts-blocks exit 0 — 126 blocks, 89 compile
check:doc-ts-blocks --self-test 55 blocks, 0 mismatches
check:client-side-effect-imports exit 0
check:prisma-error-codes exit 0 — 1477 files, 26 literals / 7 allowlisted
check:adr-duplicates exit 0 — 68 numbers
pnpm lint 0 errors (1 pre-existing warning, migration-generator.ts:851, untouched here)
pnpm format:check clean
pnpm manypkg check workspaces valid
Changesets 115 minor, 20 patch, 0 major — no premature version cut

CI, per job: test pass (15m7s), e2e (postgres) pass, e2e (dev-database) pass, coverage pass, GitGuardian pass, Vercel pass. I checked the test job's 81 steps individually for #1407: the later gates executed, they were not skipped behind a failure. Steps 5–28 (version consistency, ADR duplicates, error codes, client imports, lint, format, all 13 regenerations, the dirty-regeneration gate, doc-block self-test, doc-block check) are all success. The four skipped steps are the base_ref == 'main' alternates whose integration-branch counterparts ran instead (30/31, 35/36, and 29), plus step 40, which uploads browser screenshots only on failure. Neither known flake (#1404, #1411) appeared in CI or in my local runs.

Findings by category

  • Blocking: none.
  • Follow-up residue: the stale ADR-0066 in examples/rag-ollama-demo/README.md:234; the PR-description overstatement about "two references left at 0066".

One process note, not a change request: the description's own numbers are slightly off in places (109/17 changesets vs the 115/20 I count; 1482 vs 1477 files scanned). Nothing turns on it, but since this PR's entire safety case rests on the author's inspection being trusted, the small drift is worth tightening before the release PR quotes it.

second argument, `sudo`, is now declared as the `StackContext` it always was — a plugin
reaching `sudo().db` is unaffected, one reaching `ormHandle` off it was already getting
`undefined` and now fails to compile. See ADR-0066.
`undefined` and now fails to compile. See ADR-0068.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The renumber is correct here, and in 16 other places — but it missed one, and the miss is the bad kind: it now points at a real but wrong record.

examples/rag-ollama-demo/README.md:234 still reads:

Writes the vector and its metadata through core's writePluginOwnedField — the columns are write-denied to application code, and that write runs no hook of the list's (ADR-0066)

That means ADR-0068. Since this merge adds main's ADR-0066 (the hook-bound context record), the reference no longer dangles — it resolves, to a document about something else entirely. pnpm check:adr-duplicates cannot catch this: it checks that numbers are unique, not that references point where they mean to.

I grepped the whole tree: this is the only remaining ADR-0066 reference outside docs/adr/ itself. One-line fix, follow-up rather than blocking.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 02c526dexamples/rag-ollama-demo/README.md:234 now reads ADR-0068.

You were right that this is the bad kind of miss, and I swept the whole tree on the meaning of each
occurrence rather than counting, since both numbers are legitimately in use:

  • ADR-0066 — exactly one occurrence existed, the one you found. Its sentence is about
    writePluginOwnedField and "that write runs no hook of the list's", which is squarely the
    renumbered record. grep -rn 'ADR-0066' over the tree now returns nothing.
  • ADR-0068 — 17 occurrences, all genuinely the renumbered record. Two sit in paragraphs whose
    surrounding topic is the hook context (0066's subject) and so deserved a second look:
    packages/core/CLAUDE.md:340 ("because a hook cannot reach sudo()…") and
    packages/core/src/config/types.ts:3197 (the StackContext vs StackBaseContext split). In both
    the parenthetical attaches to a claim about writePluginOwnedField specifically, not about what a
    hook's context carries, so 0068 is correct in each.

I also corrected the PR description. It claimed two references were deliberately left at 0066 and
covered by the supersede note — that was wrong twice over: there are none, and the two documents it
named (ADR-0052's #1211 amendment and packages/core/CLAUDE.md's "#1176" section) were rewritten
in prose to the merged behaviour and cite no ADR number at all, so there was never anything to
leave. The count there now reads 18.

Gates on the new tip: pnpm check:adr-duplicates passes (68 numbers, no duplicates) and
pnpm --filter opensaas-stack-docs link-check passes. The change is one line of documentation text
— no source, no manifest, no changeset.

args: CountArgs,
fieldAccess?: FieldAccess,
): Promise<CountAccessEntry> {
if (fieldAccess) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Confirmed correct, and confirmed it cannot refuse anything legitimate.

The port is faithful to main's: same optional parameter, same isFieldReadableForPredicate evaluation, same ordering (before the related list's query access, so a field-level denial short-circuits), same undefined-means-exempt for a synthetic back-relation. main's test came across with it and passes in the 1623.

On "cannot refuse something legitimate": the one behavioural edge is that a row-dependent read rule denies at predicate time, because there is no fetched row yet. That is main's own documented semantics (the #915 evaluator), not something the port introduced — and it is unreachable here regardless: the only caller is buildAccessScopedInclude, which has no in-repo caller outside its own tests, since context.db reaches counts through secured/include.ts.

Porting rather than leaning on unreachability is the right call. Unreachability is a property of today's call graph; the gate is a property of the function.

const accessEntry = await resolveCountAccessEntryForList(
relatedConfig.listConfig,
args,
fieldConfig?.access,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is auto-merge #4, and it is genuinely fixed — the 3-argument call now meets a 3-parameter signature.

Worth stating what made it dangerous, since it is the shape to keep watching for: it was not a merge conflict and it was not a test failure. It was a call that simply did not type-check, in a file whose own suite never exercised the path. That class of defect is invisible to "it merged cleanly" and to "CI is green" — it only surfaces when something actually compiles the call.

@@ -0,0 +1,148 @@
# The hook-bound context is the same secured context as a transaction's

Status: superseded by [ADR-0050](0050-nested-relation-input-leaves-the-secured-write-surface.md) and [ADR-0052](0052-the-generated-types-declare-the-contract-remainder-and-instantiate-core-generics.md)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Marking another branch's decision record superseded is a real editorial act, so: I think this one is justified, and for two specific reasons rather than deference.

First, standing. The record is arriving in this tree as part of this merge. It has never been in force on this line. The integrator is the right person to state its status here — this is not reaching across to overwrite a live decision elsewhere.

Second, form. The note does not rewrite the record's argument or edit its body. It adds a status line and a scoped block that names exactly what is reversed (the four derive-a-new-context operations), exactly what survives and why the record was written at all (the transaction binding, ADR-0010/0028/0023), and why _resolveOutputChain is moot by construction rather than quietly dropped. A future reader gets the original reasoning intact plus an accurate account of what happened to it. That is what a supersede is supposed to look like.

I also agree with resolving the underlying conflict for prisma-8. ADR-0052's split is emitted by the generator, baked into the contract and instantiated across twelve examples; reverting it is a respec, not a merge resolution. And the direction is monotone — a hook context without sudo() / withSession() / transaction() grants strictly less authority, with a first-class replacement (writePluginOwnedField) for the one case that needed the escalation.

a `resolveInput` hook assembled after the types had their say.

The refusal is **unconditional**: unlike an access denial, `sudo()` does not
lift it. Each of these was a second write against another list hidden inside

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

"The refusal is unconditional: unlike an access denial, sudo() does not lift it" — verified by running, and the second half of the claim verified by mutation.

I probed all eight kinds three ways: on create under sudo(), on update under sudo(), and injected by a resolveInput hook with a completely clean caller payload ({ title } only) both non-sudo and sudo. 24/24 refused with NestedRelationInputError. A control probe confirms the harness is real rather than refusing everything: { connect: { id } }, null-clear, a plain sudo create and a read-back all succeed.

For the post-hook half I commented out write-pipeline.ts:339 (refuseNestedRelationInput(…, resolvedData)) and all eight post-hook probes went red, then green again on restore. That call site is load-bearing. Worth noting what it degrades to: without it the payload falls through to lowerRelationInput and throws MalformedRelationInputError, so nothing reaches the driver even then — but only after the hook chain and beforeOperation side effects have run, and under an error name describing the wrong problem.

The prose here is accurate against the code, including the detail that { tags: { deleteMany: {} } } lands on NestedRelationInputError and not NonOwningRelationInputErrorkindsIn is checked before the inverse branch in refuseNestedRelationInput.

The renumber from 0066 to 0068 missed this reference. ADR-0066 still
exists on this line — main's hook-bound-context record, now superseded —
so the stale number resolved to a real but unrelated document, which
neither the duplicate-number check nor the link check can catch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@borisno2
borisno2 merged commit 02c526d into prisma-8 Sep 11, 2026
7 checks passed
@borisno2
borisno2 deleted the claude/integrate-main-into-prisma-8 branch September 11, 2026 07:35
@borisno2

Copy link
Copy Markdown
Member Author

This has landed on prisma-8 as 02c526d8, by fast-forward rather than through the merge button. Safe to close by hand — I was not permitted to close it myself.

Why not the button. This repository allows squash only, and this pull request is a merge commit carrying both parents — deliberately so, because prisma-8 and main had no common ancestor and this commit is what establishes one. Squashing would have collapsed it into a single commit, discarded the parent link, and left the branches orphans again, making the release pull request unmergeable. The same reviewed, CI-green commit was therefore pushed as a fast-forward: no history rewrite, and 57a22e92 remains the rollback point if anything needs undoing.

Result: prisma-8 is now 73 ahead of main and 0 behind, with main's tip as the merge base, so the release pull request is an ordinary merge.

Please leave the branch claude/integrate-main-into-prisma-8 in place until the release lands — it is the ref the fast-forward came from.

🤖 Generated with Claude Code

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