TML-2816: namespace-aware DSL/ORM surface — additive slice#720
Conversation
📝 WalkthroughWalkthroughAdds namespace-aware resolution across contract authoring, storage lookups, codecs, SQL builder, and ORM. Functions gain namespaceId parameters, ambiguity checks added, and proxies expose db.. and orm... Tests and docs updated, including cross-namespace FK/relation handling.ChangesNamespace-scoped SQL/ORM surface
Sequence Diagram(s)sequenceDiagram
rect rgba(66, 135, 245, 0.5)
participant Dev as Client Code
participant ORM as ORM Client
participant Builder as SQL Builder
participant Runtime as Execution Runtime
participant DB as Database
end
Dev->>ORM: orm.public.User.all()
ORM->>Builder: compileSelect(ns="public", table="users")
Builder->>Runtime: codecRefForColumn("public","users","id")
Runtime->>DB: SELECT ... FROM "public"."users"
DB-->>Runtime: rows
Runtime-->>ORM: rows with codecs
ORM-->>Dev: mapped fields per namespace
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
size-limit report 📦
|
@prisma-next/extension-author-tools
@prisma-next/mongo-runtime
@prisma-next/family-mongo
@prisma-next/sql-runtime
@prisma-next/family-sql
@prisma-next/extension-arktype-json
@prisma-next/middleware-cache
@prisma-next/mongo
@prisma-next/extension-paradedb
@prisma-next/extension-pgvector
@prisma-next/extension-postgis
@prisma-next/postgres
@prisma-next/sql-orm-client
@prisma-next/sqlite
@prisma-next/extension-supabase
@prisma-next/target-mongo
@prisma-next/adapter-mongo
@prisma-next/driver-mongo
@prisma-next/contract
@prisma-next/utils
@prisma-next/config
@prisma-next/errors
@prisma-next/framework-components
@prisma-next/operations
@prisma-next/ts-render
@prisma-next/contract-authoring
@prisma-next/ids
@prisma-next/psl-parser
@prisma-next/psl-printer
@prisma-next/cli
@prisma-next/cli-telemetry
@prisma-next/emitter
@prisma-next/migration-tools
prisma-next
@prisma-next/vite-plugin-contract-emit
@prisma-next/mongo-codec
@prisma-next/mongo-contract
@prisma-next/mongo-value
@prisma-next/mongo-contract-psl
@prisma-next/mongo-contract-ts
@prisma-next/mongo-emitter
@prisma-next/mongo-schema-ir
@prisma-next/mongo-query-ast
@prisma-next/mongo-orm
@prisma-next/mongo-query-builder
@prisma-next/mongo-lowering
@prisma-next/mongo-wire
@prisma-next/sql-contract
@prisma-next/sql-errors
@prisma-next/sql-operations
@prisma-next/sql-schema-ir
@prisma-next/sql-contract-psl
@prisma-next/sql-contract-ts
@prisma-next/sql-contract-emitter
@prisma-next/sql-lane-query-builder
@prisma-next/sql-relational-core
@prisma-next/sql-builder
@prisma-next/target-postgres
@prisma-next/target-sqlite
@prisma-next/adapter-postgres
@prisma-next/adapter-sqlite
@prisma-next/driver-postgres
@prisma-next/driver-sqlite
commit: |
| if (ormCallCount === 1) return { lane: 'orm' }; | ||
| return txOrmProxy; | ||
| }) as typeof ormMock); | ||
| }) as unknown as typeof ormMock); |
There was a problem hiding this comment.
Why is this cast now necessary?
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (7)
packages/3-extensions/sql-orm-client/src/orm.ts (1)
204-217:⚠️ Potential issue | 🟠 Major | ⚡ Quick winCustom collection key validation is still default-namespace-only.
Line 204 validates
collectionskeys againstdomainModelsAtDefaultNamespace(contract.domain), so a valid model that exists only in another namespace (for example,auth.Session) is rejected in multi-namespace contracts. This breaks custom collection registration for the new namespaced surface.💡 Suggested fix
import { type Contract, - domainModelsAtDefaultNamespace, soleDomainNamespaceId, } from '`@prisma-next/contract/types`'; @@ function createCollectionRegistry< TContract extends Contract<SqlStorage>, Collections extends Partial<Record<string, AnyCollectionClass>>, >(contract: TContract, collections: Collections | undefined): Map<string, AnyCollectionClass> { const registry = new Map<string, AnyCollectionClass>(); if (!collections) { return registry; } - const models = domainModelsAtDefaultNamespace(contract.domain); + const modelNames = new Set(domainModelNames(contract)); for (const [key, collectionClass] of Object.entries(collections)) { @@ - if (!Object.hasOwn(models, key)) { + if (!modelNames.has(key)) { throw new Error( - `No model found for custom collection '${key}'. Available models: ${Object.keys(models).join(', ')}`, + `No model found for custom collection '${key}'. Available models: ${[...modelNames].join(', ')}`, ); } registry.set(key, collectionClass); }🤖 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-extensions/sql-orm-client/src/orm.ts` around lines 204 - 217, The validation currently uses domainModelsAtDefaultNamespace(contract.domain) which only returns default-namespace models so namespaced models (e.g., "auth.Session") get rejected; replace that lookup with the namespace-aware model map (use the function that returns all domain models including namespaces — e.g., domainModels(contract.domain) or the equivalent namespaced lookup) when building the models variable and then keep the existing isCollectionClass and Object.hasOwn(models, key) checks so custom collection keys validate against the full, namespaced model set; update the reference in the block where models is defined and used (the models variable, domainModelsAtDefaultNamespace call, and the loop over collections) to use the namespace-aware lookup.packages/2-sql/2-authoring/contract-psl/src/interpreter.ts (2)
1517-1528:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftBare-name namespace maps still collide same-named models.
Both
modelNamespaceIdsand the fallbackmodelMappingsoverwrite earlier entries by baremodel.name. That meansauth.Userandpublic.Usercannot coexist safely for any unqualified lookup: relation targets andresolvePolymorphism()will pick whichever namespace was inserted last, not the declaring model’s coordinate. In practice,auth.Session.user User@relation(...)can end up targetingpublic.User, and discriminator/base patches can land on the wrong model.Keep these lookups coordinate-aware end-to-end, or resolve unqualified names relative to the declaring model’s namespace and emit an ambiguity diagnostic when multiple coordinates still match.
Also applies to: 1626-1639
🤖 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/src/interpreter.ts` around lines 1517 - 1528, The bug is that modelNamespaceIds and fallback modelMappings use bare model.name keys causing collisions between same-named models across namespaces (e.g., auth.User vs public.User); update lookups to be coordinate-aware: change where entries are set (in the loop that builds modelNamespaceIds/modelMappings and where resolvePolymorphism() or relation resolution uses them) to use a fully-qualified key (e.g., `${namespace.name}.${model.name}` or the model coordinate from modelEntries) instead of model.name, and when code paths accept unqualified names keep resolving them relative to the declaring model’s namespace (using namespaceId from modelEntries) and emit an ambiguity diagnostic if multiple qualified matches exist so relation targets and polymorphism patches always bind to the correct model.
1118-1124:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftRelation metadata is only half-qualified here.
This threads
targetNamespaceId, but the declaring side still stays bare. When two namespaces both containUser, the laterindexFkRelations/ model-relation assembly path cannot distinguishauth.Userfrompublic.User, so relations/backrelations from one namespace can be attached to the other.Please carry the declaring model’s namespace (and the same coordinate on backrelation candidates) through this metadata so the later indexing step can stay namespace-stable.
🤖 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/src/interpreter.ts` around lines 1118 - 1124, The relation metadata being pushed via resultFkRelationMetadata currently includes targetNamespaceId but omits the declaring model’s namespace, causing ambiguity between same-named models in different namespaces; update the object pushed in the resultFkRelationMetadata (the block that sets declaringModelName, declaringFieldName, declaringTableName, targetModelName, targetTableName and ...ifDefined('targetNamespaceId', targetNamespaceId)) to also include the declaringNamespaceId (e.g., ...ifDefined('declaringNamespaceId', declaringNamespaceId) or the appropriate variable that holds the declaring model’s namespace), and mirror this change wherever backrelation candidate metadata is assembled so both declaring and target sides carry their namespace coordinates for use by indexFkRelations and model-relation assembly.packages/3-extensions/sql-orm-client/src/collection-contract.ts (1)
318-331:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winValidate
relation.to.namespacebefore accepting a relation entry.At Line 330,
toNamespaceis assigned without checking thatrel.to.namespaceis a string. A malformed relation can pass this guard and fail later with a misleading namespace-resolution error instead of being treated as malformed.Suggested fix
if ( !rel.to || typeof rel.to !== 'object' || typeof rel.to.model !== 'string' || + typeof rel.to.namespace !== 'string' || !Array.isArray(localFields) || !Array.isArray(targetFields) ) { continue; }🤖 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-extensions/sql-orm-client/src/collection-contract.ts` around lines 318 - 331, The relation entry accepts rel.to.namespace without validating its type; update the guard that checks rel and its fields (the if that currently validates rel.to, rel.to.model, localFields, targetFields) to also require typeof rel.to.namespace === 'string' (or allow undefined explicitly if intended), and if that check fails continue skipping the relation; then assign toNamespace from rel.to.namespace in the resolved[name] object as before so malformed namespace values are treated as invalid relations rather than causing later namespace-resolution errors.packages/2-sql/4-lanes/relational-core/src/codec-ref-for-column.ts (1)
39-48:⚠️ Potential issue | 🟠 Major | ⚡ Quick winScope enum
typeReffallback to the requested namespace.After
namespaceIdbecame required, this fallback still scans all namespaces and can return the wrong enum entry when two namespaces define the sametypeRef. Keep this lookup namespace-qualified.🔧 Proposed fix
- if (!instance) { - for (const ns of Object.values(storage.namespaces)) { - const nsEnums = (ns as { enum?: Record<string, unknown> }).enum; - if (nsEnums) { - const nsEntry = nsEnums[columnDef.typeRef]; - if (nsEntry !== undefined) { - instance = nsEntry; - break; - } - } - } - } + if (!instance) { + const nsEnums = ( + storage.namespaces[namespaceId] as { enum?: Record<string, unknown> } | undefined + )?.enum; + if (nsEnums) { + instance = nsEnums[columnDef.typeRef]; + } + }🤖 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/4-lanes/relational-core/src/codec-ref-for-column.ts` around lines 39 - 48, The code currently scans all storage.namespaces and can pick the wrong enum when multiple namespaces define the same columnDef.typeRef; instead, restrict lookup to the requested namespace using the required namespace identifier on the column definition (e.g. columnDef.namespaceId). Change the logic that iterates Object.values(storage.namespaces) to directly index storage.namespaces[columnDef.namespaceId] (or the corresponding namespaceId variable), verify that namespace exists, then read its enum map and pick nsEnums[columnDef.typeRef] into instance; remove the cross-namespace loop so the lookup is namespace-qualified.packages/2-sql/5-runtime/src/sql-context.ts (1)
550-567:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftInclude namespace in codec instance site identity.
The registry lookup is now namespace-scoped, but
usedAtentries and inline instance names are still keyed only by(table, column). Same bare table names across namespaces can collapse context for stateful codecs and leak cross-namespace instance metadata.🔧 Suggested direction
- const site = { table: tableName, column: columnName }; + const site = { namespaceId, table: tableName, column: columnName }; const name = ref.typeParams !== undefined - ? `<col:${tableName}.${columnName}>` + ? `<col:${namespaceId}.${tableName}.${columnName}>` : `<codec:${ref.codecId}>`;Also thread
namespaceIdthroughcollectTypeRefSites(...)and extendSqlCodecInstanceContext['usedAt']to carry namespace so contexts remain unambiguous end-to-end.Based on learnings,
buildContractCodecRegistrymust avoid leaking shared codec context across columns so column-aware dispatch remains correct.🤖 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/5-runtime/src/sql-context.ts` around lines 550 - 567, The usedAt/site identity must include namespaceId to prevent cross-namespace collisions: change the site object created in the loop to include namespaceId (e.g., { namespace: namespaceId, table: tableName, column: columnName }), update the type of SqlCodecInstanceContext['usedAt'] (and any usedAtByKey maps) to store sites with namespace, and adjust nameByKey generation to incorporate namespace when producing inline names for per-column codecs; then thread namespaceId through collectTypeRefSites(...) and into buildContractCodecRegistry so every place that records or reads usedAt or builds instance names uses the namespace-aware site shape.packages/3-extensions/sql-orm-client/src/collection-dispatch.ts (1)
208-216:⚠️ Potential issue | 🟠 Major | ⚡ Quick winThread
namespaceIdinto identity-filter binding.Line 215 still builds identity filters without namespace context, and Lines 257/272 bind that filter via
bindWhereExpr(...)withoutnamespaceId. In multi-namespace contracts with duplicate table names, mutation read-back can fail on ambiguous table resolution (or bind against the wrong table context).🔧 Proposed fix
export function reloadMutationRowsByIdentities<Row>(options: { @@ - const identityFilter = buildIdentityInFilter(contract, tableName, identityColumns, identityRows); + const identityFilter = buildIdentityInFilter( + contract, + namespaceId, + tableName, + identityColumns, + identityRows, + ); @@ function buildIdentityInFilter( contract: Contract<SqlStorage>, + namespaceId: string, tableName: string, identityColumns: readonly string[], identityRows: readonly Record<string, unknown>[], ): AnyExpression | undefined { @@ - return bindWhereExpr( - contract, - BinaryExpr.in(ColumnRef.of(tableName, singleColumn), ListExpression.fromValues(values)), - ); + return bindWhereExpr( + contract, + BinaryExpr.in(ColumnRef.of(tableName, singleColumn), ListExpression.fromValues(values)), + namespaceId, + ); @@ - return bindWhereExpr(contract, OrExpr.of(tuples)); + return bindWhereExpr(contract, OrExpr.of(tuples), namespaceId); }Also applies to: 242-273
🤖 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-extensions/sql-orm-client/src/collection-dispatch.ts` around lines 208 - 216, The identity filter is being built and later bound without the namespace context, causing ambiguous table resolution in multi-namespace contracts; update the call sites so namespaceId is threaded into the filter creation and binding: modify buildIdentityInFilter(...) invocation to accept namespaceId (e.g. buildIdentityInFilter(contract, namespaceId, tableName, identityColumns, identityRows) or add namespaceId parameter) and ensure subsequent uses (where bindWhereExpr(...) is called) pass namespaceId when binding the identityFilter; update the relevant functions (buildIdentityInFilter and bindWhereExpr call-sites) and their signatures to accept and propagate namespaceId while preserving existing parameters like contract, tableName, identityColumns, and identityRows.
🧹 Nitpick comments (1)
test/integration/test/namespaced-accessors-e2e.integration.test.ts (1)
1-28: ⚡ Quick winTrim long narrative comments and link to canonical docs instead.
The top-level and mid-file prose blocks are overly verbose for test code; keep a short intent comment and link to the canonical design/ADR doc for details.
As per coding guidelines:
**/*.{ts,tsx,js,jsx}: "Don't add comments if avoidable, prefer code that expresses its intent. Prefer links to canonical docs over long comments."Also applies to: 136-142
🤖 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/namespaced-accessors-e2e.integration.test.ts` around lines 1 - 28, Replace the long top-level and mid-file narrative comments in namespaced-accessors-e2e.integration.test.ts with a single short intent line describing the test (e.g., "E2E test for explicit namespaced accessors across public/auth schemas") and add a single link to the canonical design/ADR (replace with the project ADR URL), removing the verbose prose blocks at the top and around lines 136-142; keep the test behavior and fixtures unchanged (target the long block comment and the subsequent mid-file comment blocks that describe the same intent).
🤖 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/4-lanes/sql-builder/src/runtime/sql.ts`:
- Around line 33-49: The current proxy resolution gives namespace ids priority
via Object.hasOwn(storage.namespaces, prop) which hides a flat table when a
namespace id equals a flat table name; update the branch that checks
Object.hasOwn(storage.namespaces, prop) to also call
resolveTableForFlatName(storage, prop) (or otherwise check for a flat table) and
if both a namespace and a flat table are present, throw a clear collision error
instead of silently choosing the namespace; otherwise proceed to create the
namespace proxy (using resolveTableInNamespace and TableProxyImpl) as
before—this makes db.<name> fail fast on namespace/table key collisions.
In `@packages/3-extensions/sql-orm-client/src/query-plan-meta.ts`:
- Around line 24-30: The catch block in query-plan-meta.ts currently checks
error.message.includes('ambiguous') case-sensitively; change that check to a
case-insensitive match (e.g. use a case-insensitive regex like /\bambiguous\b/i
or compare error.message.toLowerCase().includes('ambiguous')) so any
capitalization or minor wording changes still rethrow the ambiguity error; keep
the existing Error instance guard (error instanceof Error) and rethrow the
original error when the match succeeds, otherwise continue to throw the Unknown
table error for tableName.
In `@packages/3-extensions/sql-orm-client/src/where-binding.ts`:
- Around line 153-170: The current lookup for resolvedNamespaceId picks the
first namespace with the table and only afterward checks the column, causing
wrong binds or false "Unknown column" errors when bare table names appear in
multiple namespaces; change the lookup to find namespaces where both table and
column exist by searching Object.keys(namespaces).filter(ns =>
namespaces[ns]?.tables[columnRef.table]?.columns?.[columnRef.column] !==
undefined) and then: if zero candidates throw unknown column, if multiple
candidates throw an ambiguity error listing the candidate namespace ids,
otherwise set resolvedNamespaceId to the single candidate and proceed to call
codecRefForStorageColumn(contract.storage, resolvedNamespaceId, columnRef.table,
columnRef.column). Ensure you still handle an explicitly provided namespaceId by
validating that the column exists in that namespace and throwing if not.
---
Outside diff comments:
In `@packages/2-sql/2-authoring/contract-psl/src/interpreter.ts`:
- Around line 1517-1528: The bug is that modelNamespaceIds and fallback
modelMappings use bare model.name keys causing collisions between same-named
models across namespaces (e.g., auth.User vs public.User); update lookups to be
coordinate-aware: change where entries are set (in the loop that builds
modelNamespaceIds/modelMappings and where resolvePolymorphism() or relation
resolution uses them) to use a fully-qualified key (e.g.,
`${namespace.name}.${model.name}` or the model coordinate from modelEntries)
instead of model.name, and when code paths accept unqualified names keep
resolving them relative to the declaring model’s namespace (using namespaceId
from modelEntries) and emit an ambiguity diagnostic if multiple qualified
matches exist so relation targets and polymorphism patches always bind to the
correct model.
- Around line 1118-1124: The relation metadata being pushed via
resultFkRelationMetadata currently includes targetNamespaceId but omits the
declaring model’s namespace, causing ambiguity between same-named models in
different namespaces; update the object pushed in the resultFkRelationMetadata
(the block that sets declaringModelName, declaringFieldName, declaringTableName,
targetModelName, targetTableName and ...ifDefined('targetNamespaceId',
targetNamespaceId)) to also include the declaringNamespaceId (e.g.,
...ifDefined('declaringNamespaceId', declaringNamespaceId) or the appropriate
variable that holds the declaring model’s namespace), and mirror this change
wherever backrelation candidate metadata is assembled so both declaring and
target sides carry their namespace coordinates for use by indexFkRelations and
model-relation assembly.
In `@packages/2-sql/4-lanes/relational-core/src/codec-ref-for-column.ts`:
- Around line 39-48: The code currently scans all storage.namespaces and can
pick the wrong enum when multiple namespaces define the same columnDef.typeRef;
instead, restrict lookup to the requested namespace using the required namespace
identifier on the column definition (e.g. columnDef.namespaceId). Change the
logic that iterates Object.values(storage.namespaces) to directly index
storage.namespaces[columnDef.namespaceId] (or the corresponding namespaceId
variable), verify that namespace exists, then read its enum map and pick
nsEnums[columnDef.typeRef] into instance; remove the cross-namespace loop so the
lookup is namespace-qualified.
In `@packages/2-sql/5-runtime/src/sql-context.ts`:
- Around line 550-567: The usedAt/site identity must include namespaceId to
prevent cross-namespace collisions: change the site object created in the loop
to include namespaceId (e.g., { namespace: namespaceId, table: tableName,
column: columnName }), update the type of SqlCodecInstanceContext['usedAt'] (and
any usedAtByKey maps) to store sites with namespace, and adjust nameByKey
generation to incorporate namespace when producing inline names for per-column
codecs; then thread namespaceId through collectTypeRefSites(...) and into
buildContractCodecRegistry so every place that records or reads usedAt or builds
instance names uses the namespace-aware site shape.
In `@packages/3-extensions/sql-orm-client/src/collection-contract.ts`:
- Around line 318-331: The relation entry accepts rel.to.namespace without
validating its type; update the guard that checks rel and its fields (the if
that currently validates rel.to, rel.to.model, localFields, targetFields) to
also require typeof rel.to.namespace === 'string' (or allow undefined explicitly
if intended), and if that check fails continue skipping the relation; then
assign toNamespace from rel.to.namespace in the resolved[name] object as before
so malformed namespace values are treated as invalid relations rather than
causing later namespace-resolution errors.
In `@packages/3-extensions/sql-orm-client/src/collection-dispatch.ts`:
- Around line 208-216: The identity filter is being built and later bound
without the namespace context, causing ambiguous table resolution in
multi-namespace contracts; update the call sites so namespaceId is threaded into
the filter creation and binding: modify buildIdentityInFilter(...) invocation to
accept namespaceId (e.g. buildIdentityInFilter(contract, namespaceId, tableName,
identityColumns, identityRows) or add namespaceId parameter) and ensure
subsequent uses (where bindWhereExpr(...) is called) pass namespaceId when
binding the identityFilter; update the relevant functions (buildIdentityInFilter
and bindWhereExpr call-sites) and their signatures to accept and propagate
namespaceId while preserving existing parameters like contract, tableName,
identityColumns, and identityRows.
In `@packages/3-extensions/sql-orm-client/src/orm.ts`:
- Around line 204-217: The validation currently uses
domainModelsAtDefaultNamespace(contract.domain) which only returns
default-namespace models so namespaced models (e.g., "auth.Session") get
rejected; replace that lookup with the namespace-aware model map (use the
function that returns all domain models including namespaces — e.g.,
domainModels(contract.domain) or the equivalent namespaced lookup) when building
the models variable and then keep the existing isCollectionClass and
Object.hasOwn(models, key) checks so custom collection keys validate against the
full, namespaced model set; update the reference in the block where models is
defined and used (the models variable, domainModelsAtDefaultNamespace call, and
the loop over collections) to use the namespace-aware lookup.
---
Nitpick comments:
In `@test/integration/test/namespaced-accessors-e2e.integration.test.ts`:
- Around line 1-28: Replace the long top-level and mid-file narrative comments
in namespaced-accessors-e2e.integration.test.ts with a single short intent line
describing the test (e.g., "E2E test for explicit namespaced accessors across
public/auth schemas") and add a single link to the canonical design/ADR (replace
with the project ADR URL), removing the verbose prose blocks at the top and
around lines 136-142; keep the test behavior and fixtures unchanged (target the
long block comment and the subsequent mid-file comment blocks that describe the
same intent).
🪄 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: 18c34605-af39-4e06-8ef0-dfd0714e84ac
⛔ Files ignored due to path filters (7)
projects/explicit-namespace-dsl/learnings.mdis excluded by!projects/**projects/explicit-namespace-dsl/plan.mdis excluded by!projects/**projects/explicit-namespace-dsl/plans/plan.mdis excluded by!projects/**projects/explicit-namespace-dsl/slices/01-additive-namespaced-surface/plan.mdis excluded by!projects/**projects/explicit-namespace-dsl/slices/01-additive-namespaced-surface/spec.mdis excluded by!projects/**projects/explicit-namespace-dsl/slices/02-remove-flat-fallback/spec.mdis excluded by!projects/**projects/explicit-namespace-dsl/spec.mdis excluded by!projects/**
📒 Files selected for processing (98)
packages/2-sql/1-core/contract/src/resolve-storage-table.tspackages/2-sql/1-core/contract/test/resolve-storage-table.test.tspackages/2-sql/2-authoring/contract-psl/src/interpreter.tspackages/2-sql/2-authoring/contract-psl/src/psl-field-resolution.tspackages/2-sql/2-authoring/contract-psl/src/psl-relation-resolution.tspackages/2-sql/2-authoring/contract-psl/test/interpreter.namespaces.test.tspackages/2-sql/2-authoring/contract-ts/src/build-contract.tspackages/2-sql/2-authoring/contract-ts/src/contract-definition.tspackages/2-sql/2-authoring/contract-ts/test/contract-builder.cross-namespace-same-table.test.tspackages/2-sql/4-lanes/relational-core/DEVELOPING.mdpackages/2-sql/4-lanes/relational-core/src/ast/codec-types.tspackages/2-sql/4-lanes/relational-core/src/codec-descriptor-registry.tspackages/2-sql/4-lanes/relational-core/src/codec-ref-for-column.tspackages/2-sql/4-lanes/relational-core/src/query-lane-context.tspackages/2-sql/4-lanes/relational-core/test/codec-descriptor-registry.test.tspackages/2-sql/4-lanes/relational-core/test/codec-ref-for-column.test.tspackages/2-sql/4-lanes/sql-builder/src/exports/types.tspackages/2-sql/4-lanes/sql-builder/src/runtime/builder-base.tspackages/2-sql/4-lanes/sql-builder/src/runtime/mutation-impl.tspackages/2-sql/4-lanes/sql-builder/src/runtime/resolve-table.tspackages/2-sql/4-lanes/sql-builder/src/runtime/sql.tspackages/2-sql/4-lanes/sql-builder/src/runtime/table-proxy-impl.tspackages/2-sql/4-lanes/sql-builder/src/types/db.tspackages/2-sql/4-lanes/sql-builder/test/runtime/field-proxy.test.tspackages/2-sql/4-lanes/sql-builder/test/runtime/namespaced-resolution.test.tspackages/2-sql/4-lanes/sql-builder/test/runtime/same-bare-table-name.test.tspackages/2-sql/4-lanes/sql-builder/test/types/namespaced-db.types.test-d.tspackages/2-sql/5-runtime/src/sql-context.tspackages/2-sql/5-runtime/test/contract-codec-registry.test.tspackages/2-sql/5-runtime/test/same-bare-table-name.test.tspackages/2-sql/5-runtime/test/sql-context.codec-context.test.tspackages/3-extensions/postgres/test/fixtures/namespaced-contract.tspackages/3-extensions/postgres/test/namespaced-facade.types.test-d.tspackages/3-extensions/postgres/test/postgres.test.tspackages/3-extensions/sql-orm-client/src/aggregate-builder.tspackages/3-extensions/sql-orm-client/src/collection-column-mapping.tspackages/3-extensions/sql-orm-client/src/collection-contract.tspackages/3-extensions/sql-orm-client/src/collection-dispatch.tspackages/3-extensions/sql-orm-client/src/collection-internal-types.tspackages/3-extensions/sql-orm-client/src/collection-mutation-dispatch.tspackages/3-extensions/sql-orm-client/src/collection-runtime.tspackages/3-extensions/sql-orm-client/src/collection.tspackages/3-extensions/sql-orm-client/src/filters.tspackages/3-extensions/sql-orm-client/src/grouped-collection.tspackages/3-extensions/sql-orm-client/src/model-accessor.tspackages/3-extensions/sql-orm-client/src/mutation-executor.tspackages/3-extensions/sql-orm-client/src/orm.tspackages/3-extensions/sql-orm-client/src/query-plan-aggregate.tspackages/3-extensions/sql-orm-client/src/query-plan-meta.tspackages/3-extensions/sql-orm-client/src/query-plan-mutations.tspackages/3-extensions/sql-orm-client/src/query-plan-select.tspackages/3-extensions/sql-orm-client/src/storage-resolution.tspackages/3-extensions/sql-orm-client/src/types.tspackages/3-extensions/sql-orm-client/src/where-binding.tspackages/3-extensions/sql-orm-client/src/where-interop.tspackages/3-extensions/sql-orm-client/test/aggregate-builder.test.tspackages/3-extensions/sql-orm-client/test/collection-column-mapping.test.tspackages/3-extensions/sql-orm-client/test/collection-contract.test.tspackages/3-extensions/sql-orm-client/test/collection-dispatch.test.tspackages/3-extensions/sql-orm-client/test/collection-fixtures.tspackages/3-extensions/sql-orm-client/test/collection-mutation-dispatch.test.tspackages/3-extensions/sql-orm-client/test/collection-runtime.test.tspackages/3-extensions/sql-orm-client/test/collection-variant.test.tspackages/3-extensions/sql-orm-client/test/filters.test.tspackages/3-extensions/sql-orm-client/test/generated-contract-types.test-d.tspackages/3-extensions/sql-orm-client/test/include-cardinality.test-d.tspackages/3-extensions/sql-orm-client/test/model-accessor.test.tspackages/3-extensions/sql-orm-client/test/mutation-executor.test.tspackages/3-extensions/sql-orm-client/test/namespace-qualification.test.tspackages/3-extensions/sql-orm-client/test/orm-namespace-crud.test.tspackages/3-extensions/sql-orm-client/test/orm-namespace-relation.test.tspackages/3-extensions/sql-orm-client/test/orm-namespace-resolution.test.tspackages/3-extensions/sql-orm-client/test/orm-namespace-returning-crud.test.tspackages/3-extensions/sql-orm-client/test/orm-namespaced.test.tspackages/3-extensions/sql-orm-client/test/orm-namespaced.types.test-d.tspackages/3-extensions/sql-orm-client/test/orm-same-bare-table-name.test.tspackages/3-extensions/sql-orm-client/test/orm.test.tspackages/3-extensions/sql-orm-client/test/orm.types.test-d.tspackages/3-extensions/sql-orm-client/test/query-plan-aggregate.test.tspackages/3-extensions/sql-orm-client/test/query-plan-meta.test.tspackages/3-extensions/sql-orm-client/test/query-plan-mutations.test.tspackages/3-extensions/sql-orm-client/test/query-plan-select.test.tspackages/3-extensions/sql-orm-client/test/repository.test.tspackages/3-extensions/sql-orm-client/test/rich-filters-and-where.test.tspackages/3-extensions/sql-orm-client/test/rich-query-plans.test.tspackages/3-extensions/sql-orm-client/test/simplify-deep.test-d.tspackages/3-extensions/sql-orm-client/test/storage-resolution.test.tspackages/3-extensions/sqlite/test/fixtures/namespaced-contract.tspackages/3-extensions/sqlite/test/namespaced-facade.types.test-d.tstest/integration/test/namespaced-accessors-e2e.integration.test.tstest/integration/test/sql-orm-client/collection-fixtures.tstest/integration/test/sql-orm-client/collection-mutation-defaults.test.tstest/integration/test/sql-orm-client/include.test.tstest/integration/test/sql-orm-client/integration-helpers.tstest/integration/test/sql-orm-client/model-accessor.pgvector.test.tstest/integration/test/sql-orm-client/nested-includes-helpers.tstest/integration/test/sql-orm-client/polymorphism.test.tstest/integration/test/sql-orm-client/upsert.test.ts
| } catch (error) { | ||
| // Surface the ambiguous-bare-name fail-fast rather than masking it as an | ||
| // unknown table. | ||
| if (error instanceof Error && error.message.includes('ambiguous')) { | ||
| throw error; | ||
| } | ||
| throw new Error(`Unknown table "${tableName}" in SQL ORM query planner`); |
There was a problem hiding this comment.
Make ambiguity rethrow matching robust.
Line 27 uses a case-sensitive substring check; "Ambiguous ..." (or wording changes) can be misclassified and surfaced as an unknown-table error instead of the intended ambiguity diagnostic.
Proposed fix
- if (error instanceof Error && error.message.includes('ambiguous')) {
+ if (error instanceof Error && /ambiguous/i.test(error.message)) {
throw error;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } catch (error) { | |
| // Surface the ambiguous-bare-name fail-fast rather than masking it as an | |
| // unknown table. | |
| if (error instanceof Error && error.message.includes('ambiguous')) { | |
| throw error; | |
| } | |
| throw new Error(`Unknown table "${tableName}" in SQL ORM query planner`); | |
| } catch (error) { | |
| // Surface the ambiguous-bare-name fail-fast rather than masking it as an | |
| // unknown table. | |
| if (error instanceof Error && /ambiguous/i.test(error.message)) { | |
| throw error; | |
| } | |
| throw new Error(`Unknown table "${tableName}" in SQL ORM query planner`); |
🤖 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-extensions/sql-orm-client/src/query-plan-meta.ts` around lines 24
- 30, The catch block in query-plan-meta.ts currently checks
error.message.includes('ambiguous') case-sensitively; change that check to a
case-insensitive match (e.g. use a case-insensitive regex like /\bambiguous\b/i
or compare error.message.toLowerCase().includes('ambiguous')) so any
capitalization or minor wording changes still rethrow the ambiguity error; keep
the existing Error instance guard (error instanceof Error) and rethrow the
original error when the match succeeds, otherwise continue to throw the Unknown
table error for tableName.
There was a problem hiding this comment.
Same here, quality of fallback does not matter as long as it does not break the existing tests - flat fallback will be immediately removed once this PR is merged.
There was a problem hiding this comment.
@SevInf Understood — since the flat fallback is ephemeral and gets cut in the very next slice, keeping it as-is is perfectly reasonable. I'll drop the suggestion.
(ᵔ ᴥ ᵔ)
🧠 Learnings used
Learnt from: CR
Repo: prisma/prisma-next PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-08T11:12:59.730Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Column access: Use `table.columns.fieldName` to avoid conflicts with table properties.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 191
File: packages/1-framework/3-tooling/cli/src/commands/contract-emit.ts:5-5
Timestamp: 2026-03-01T13:54:21.863Z
Learning: In the Prisma-next repository, prefer importing and using 'pathe' over the built-in 'node:path' module for path operations across the codebase (including CLI commands and tooling files). Apply this in TypeScript files, replacing imports from 'node:path' with 'pathe' and adjusting API usage accordingly. Ensure consistent usage across all modules and update any affected tests or tooling imports when refactoring.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 234
File: packages/2-sql/4-lanes/sql-lane/test/rich-mutation.test.ts:2-2
Timestamp: 2026-03-23T10:01:15.075Z
Learning: In the prisma/prisma-next repo, prefer using `pathe` over Node’s built-in `node:path`. For any TypeScript file (including tests, test helpers, and integration tooling) replace imports like `import { dirname, join } from 'node:path'` with `import { dirname, join } from 'pathe'`—especially when code reads files/fixtures from disk.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 234
File: packages/3-extensions/sql-orm-client/src/model-accessor.ts:83-99
Timestamp: 2026-03-23T10:01:15.206Z
Learning: When generating SQL for comparison operators in the SQL ORM client, do not emit `col = NULL` or `col <> NULL` for equality/inequality. If `value === null` and the operator is `eq`, emit `IS NULL` (via `NullCheckExpr.isNull(left)`); if the operator is `neq`, emit `IS NOT NULL` (via `NullCheckExpr.isNotNull(left)`). Otherwise, use `BinaryExpr` with `LiteralExpr.of(value)` for non-null values.
Learnt from: aqrln
Repo: prisma/prisma-next PR: 269
File: packages/3-extensions/sql-orm-client/src/types.ts:978-983
Timestamp: 2026-04-01T08:16:29.540Z
Learning: In the generated SQL contract types in prisma/prisma-next (e.g., types like `RelationParentCols`), the contract type fields are named `parentCols`/`childCols` (not `parentColumns`/`childColumns`). When writing code that consumes the generated contract type, use `parentCols`/`childCols` to match the emitted type shape; only the SQL contract builder input uses `parentColumns`/`childColumns`.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 294
File: packages/2-mongo-family/2-query/query-ast/src/read-plan.ts:4-10
Timestamp: 2026-04-04T10:08:34.220Z
Learning: In prisma/prisma-next, don’t flag declaration-emit/typecheck warnings/errors when a file uses an intentional nominal/branding pattern with a non-exported `declare const <brand>: unique symbol` as a computed property key on an exported interface (e.g., `readonly [__mongoReadPlanRow]: ...` / `readonly [aggregateResultBrand]: ...`). The `unique symbol` must remain unexported to prevent external forging, while the branded property key staying on the exported interface provides compile-time discrimination. If the build correctly emits declaration files for this pattern, treat it as intentional rather than a declaration-emit error.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 293
File: packages/1-framework/1-core/shared/contract/src/canonicalization.ts:203-216
Timestamp: 2026-04-04T12:19:05.250Z
Learning: In the prisma/prisma-next repository, do not treat `Array.prototype.sort` comparators as “potentially non-deterministic” solely because the comparator does not include explicit tie-breaking keys. This is because the repo’s required runtime is Node >= 24, where `Array.prototype.sort` is stable (equal elements retain their original relative order via a stable sort such as TimSort). Therefore, equal-comparing elements should remain deterministic without additional tie-break logic.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 334
File: packages/2-mongo-family/9-family/src/core/control-instance.ts:204-228
Timestamp: 2026-04-13T15:54:52.337Z
Learning: When reviewing TypeScript code in this repo, do not assume a property is potentially `undefined` at a call site just because an arktype schema definition uses the DSL syntax `'key?': 'type'` (a quoted string key with a trailing `?`). This DSL notation is not the same as TypeScript optional properties (`key?: type`). To determine whether `key` is truly optional/undefined-capable, verify the actual exported TypeScript type declaration (e.g., `contract-types.ts`) and the runtime validation schema (e.g., `validate-contract.ts`) for that property; only then should the review flag call-site handling for potential `undefined`.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 339
File: test/integration/test/cli.init-templates.e2e.test.ts:96-96
Timestamp: 2026-04-15T19:33:48.733Z
Learning: In prisma/prisma-next, when writing tests that run TypeScript compilation (e.g., via `tsc --noEmit`), avoid hardcoded timeout numbers (such as `30_000`). Instead, use `prisma-next/test-utils` timeout helpers—specifically `timeouts.typeScriptCompilation` (implemented in `test/utils/src/timeouts.ts`) for `tsc` compilation scenarios. Use `timeouts.spinUpPpgDev` for PostgreSQL server startup and `timeouts.databaseOperation` for database operation scenarios.
Learnt from: saevarb
Repo: prisma/prisma-next PR: 394
File: packages/1-framework/3-tooling/migration/src/graph-ops.ts:50-54
Timestamp: 2026-04-29T12:44:59.376Z
Learning: In TypeScript overload implementations, an internal “implementation-only” cast (e.g., `state as unknown as <ReturnType>`) can be safe and intentional when the public overload signatures and their structural typing prevent callers from reaching the implementation with an incompatible type. In these cases, reviewers should not flag the cast as a blind/unsafe cast, since compile-time overload enforcement substitutes for a runtime type guard. Don’t require an added `typeof`/runtime check solely to silence the cast when the overload design already prevents invalid inputs.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 417
File: packages/3-extensions/sql-orm-client/src/types.ts:309-318
Timestamp: 2026-05-08T17:07:07.145Z
Learning: In prisma/prisma-next code, prefer `ifDefined` from `prisma-next/utils/defined` for conditional object spreads instead of inline ternary-based spreads. For example, when building option objects for `ParamRef.of` (or similar codec/param construction), wrap optional values with `ifDefined(...)` so that the spread only includes the property when the value is defined, improving readability and avoiding “{...cond ? {a: b} : {}}”-style patterns. Ensure you import `ifDefined` from `prisma-next/utils/defined` and use it consistently for these conditional spreads.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 474
File: packages/2-mongo-family/9-family/src/core/mongo-target-descriptor.ts:68-74
Timestamp: 2026-05-11T05:34:02.685Z
Learning: When reviewing code in this repo (prisma/prisma-next) for the `doc-maintenance` rule/no-transient-artefacts guideline, do not flag Linear ticket IDs in source-code comments (e.g., `TML-2397`) as violations. The rule should only consider truly transient artefacts as forbidden—specifically milestone IDs, acceptance-criteria IDs (e.g., `AC11`), and sub-spec section references (e.g., `§ 3.2`).
Learnt from: wmadden
Repo: prisma/prisma-next PR: 485
File: test/integration/test/cli.init-facade-imports.e2e.test.ts:14-14
Timestamp: 2026-05-12T03:36:07.175Z
Learning: In this repo, the doc-maintenance rule allows stable reference IDs in source-code comments—e.g., Linear ticket IDs matching `TML-*` (such as `TML-2485`, `TML-2397`) and ADR references like `ADR 211`. Do not flag `TML-*` ticket ID references in code comments as violations. Instead, violations are limited to transient artifacts such as `projects/<x>/...` paths, milestone-task IDs like `T1.7` / `T2.5.3`, milestone acceptance-criteria codes like `AM12` / `AC-13`, and prose attributions like `M2 review`, `out of scope`, or `sub-spec § 4`—flag those when found in comments.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 598
File: lint-staged.config.mjs:8-8
Timestamp: 2026-05-27T15:04:44.974Z
Learning: This codebase targets macOS/Linux only (CI runs on Linux; no Windows engine qualifier). During code review, do not flag Windows-specific path concerns (e.g., backslashes vs. forward slashes) or require Windows compatibility for file paths/imports. Focus review on issues relevant to Linux/macOS execution and Node >= 20 constraints.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 641
File: skills-contrib/drive-judge-harness/validate-parser.ts:3-3
Timestamp: 2026-05-30T18:55:46.083Z
Learning: In this repo, for TypeScript files that are executed directly by Node.js via native ESM TypeScript-stripping (including `node --test` and `node <file>.ts`), all relative ESM imports must include explicit `.ts` file extensions (e.g. `import { foo } from '../bar/baz.ts'`). Do not flag these `.ts` extensions as violations—removing them will break Node’s ESM resolver. This rule applies to code paths that are run under Node directly, including skills-contrib scripts, test files, and tooling.
Learnt from: wmadden-electric
Repo: prisma/prisma-next PR: 711
File: packages/2-sql/9-family/src/core/migrations/control-policy.ts:106-122
Timestamp: 2026-06-03T13:51:07.205Z
Learning: When using the Prisma Next `ifDefined` helper from `prisma-next/utils/defined`, follow its actual API: `ifDefined(key: string, value: T | undefined)`. Use it in object spreads as `...ifDefined('myKey', someValue)` (e.g., `...ifDefined('namespace', subject?.namespaceId)`) to conditionally include properties. Do NOT use callback-based forms like `ifDefined(value, (key) => ({ key }))` or any other shape that doesn’t match the `(key, value)` signature.
| const resolvedNamespaceId = | ||
| namespaceId ?? | ||
| Object.keys(namespaces).find((ns) => namespaces[ns]?.tables[columnRef.table] !== undefined); | ||
| if (resolvedNamespaceId === undefined) { | ||
| throw new Error(`Unknown column "${columnRef.column}" in table "${columnRef.table}"`); | ||
| } | ||
| const tableInAnyNs = namespaces[resolvedNamespaceId]?.tables[columnRef.table] as | ||
| | StorageTable | ||
| | undefined; | ||
| if (!tableInAnyNs?.columns[columnRef.column]) { | ||
| throw new Error(`Unknown column "${columnRef.column}" in table "${columnRef.table}"`); | ||
| } | ||
| const codec = codecRefForStorageColumn(contract.storage, columnRef.table, columnRef.column); | ||
| const codec = codecRefForStorageColumn( | ||
| contract.storage, | ||
| resolvedNamespaceId, | ||
| columnRef.table, | ||
| columnRef.column, | ||
| ); |
There was a problem hiding this comment.
Make namespace fallback column-aware and ambiguity-safe.
When namespaceId is missing, lookup currently picks the first namespace containing the table, then checks the column. With duplicate bare table names, this can throw for valid columns in another namespace (or bind against the wrong namespace). Resolve by {table,column} candidates and fail fast on ambiguity.
🔧 Suggested fix
const namespaces = contract.storage.namespaces;
- const resolvedNamespaceId =
- namespaceId ??
- Object.keys(namespaces).find((ns) => namespaces[ns]?.tables[columnRef.table] !== undefined);
+ const resolvedNamespaceId =
+ namespaceId ??
+ (() => {
+ const matches = Object.entries(namespaces)
+ .filter(
+ ([, ns]) => ns?.tables[columnRef.table]?.columns[columnRef.column] !== undefined,
+ )
+ .map(([nsId]) => nsId);
+ if (matches.length === 1) return matches[0];
+ if (matches.length === 0) return undefined;
+ throw new Error(
+ `Ambiguous column "${columnRef.column}" in table "${columnRef.table}" across namespaces: ${matches.join(', ')}`,
+ );
+ })();
if (resolvedNamespaceId === undefined) {
throw new Error(`Unknown column "${columnRef.column}" in table "${columnRef.table}"`);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const resolvedNamespaceId = | |
| namespaceId ?? | |
| Object.keys(namespaces).find((ns) => namespaces[ns]?.tables[columnRef.table] !== undefined); | |
| if (resolvedNamespaceId === undefined) { | |
| throw new Error(`Unknown column "${columnRef.column}" in table "${columnRef.table}"`); | |
| } | |
| const tableInAnyNs = namespaces[resolvedNamespaceId]?.tables[columnRef.table] as | |
| | StorageTable | |
| | undefined; | |
| if (!tableInAnyNs?.columns[columnRef.column]) { | |
| throw new Error(`Unknown column "${columnRef.column}" in table "${columnRef.table}"`); | |
| } | |
| const codec = codecRefForStorageColumn(contract.storage, columnRef.table, columnRef.column); | |
| const codec = codecRefForStorageColumn( | |
| contract.storage, | |
| resolvedNamespaceId, | |
| columnRef.table, | |
| columnRef.column, | |
| ); | |
| const resolvedNamespaceId = | |
| namespaceId ?? | |
| (() => { | |
| const matches = Object.entries(namespaces) | |
| .filter( | |
| ([, ns]) => ns?.tables[columnRef.table]?.columns[columnRef.column] !== undefined, | |
| ) | |
| .map(([nsId]) => nsId); | |
| if (matches.length === 1) return matches[0]; | |
| if (matches.length === 0) return undefined; | |
| throw new Error( | |
| `Ambiguous column "${columnRef.column}" in table "${columnRef.table}" across namespaces: ${matches.join(', ')}`, | |
| ); | |
| })(); | |
| if (resolvedNamespaceId === undefined) { | |
| throw new Error(`Unknown column "${columnRef.column}" in table "${columnRef.table}"`); | |
| } | |
| const tableInAnyNs = namespaces[resolvedNamespaceId]?.tables[columnRef.table] as | |
| | StorageTable | |
| | undefined; | |
| if (!tableInAnyNs?.columns[columnRef.column]) { | |
| throw new Error(`Unknown column "${columnRef.column}" in table "${columnRef.table}"`); | |
| } | |
| const codec = codecRefForStorageColumn( | |
| contract.storage, | |
| resolvedNamespaceId, | |
| columnRef.table, | |
| columnRef.column, | |
| ); |
🤖 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-extensions/sql-orm-client/src/where-binding.ts` around lines 153 -
170, The current lookup for resolvedNamespaceId picks the first namespace with
the table and only afterward checks the column, causing wrong binds or false
"Unknown column" errors when bare table names appear in multiple namespaces;
change the lookup to find namespaces where both table and column exist by
searching Object.keys(namespaces).filter(ns =>
namespaces[ns]?.tables[columnRef.table]?.columns?.[columnRef.column] !==
undefined) and then: if zero candidates throw unknown column, if multiple
candidates throw an ambiguity error listing the candidate namespace ids,
otherwise set resolvedNamespaceId to the single candidate and proceed to call
codecRefForStorageColumn(contract.storage, resolvedNamespaceId, columnRef.table,
columnRef.column). Ensure you still handle an explicitly provided namespaceId by
validating that the column exists in that namespace and throwing if not.
There was a problem hiding this comment.
Same here, quality of fallback does not matter as long as it does not break the existing tests - flat fallback will be immediately removed once this PR is merged.
There was a problem hiding this comment.
Understood, @SevInf — since the flat fallback is transitional and will be cut in the very next merge, keeping the existing scan-by-table logic is fine. I'll drop this suggestion.
🧠 Learnings used
Learnt from: wmadden-electric
Repo: prisma/prisma-next PR: 765
File: packages/2-sql/5-runtime/src/sql-context.ts:476-491
Timestamp: 2026-06-08T10:59:35.251Z
Learning: In `packages/2-sql/5-runtime/src/sql-context.ts`, the `assertColumnCodecIntegrity` function's `refHasTypeParamKeys` check (lines ~476-491) uses `!Array.isArray(refTypeParams)` purely as a TypeScript type-narrowing guard within a `typeof === 'object'` branch — it is NOT there to handle real array inputs. At this call site, `ref.typeParams` (declared `JsonValue | undefined` on `CodecRef`) only ever carries what `codecRefForStorageColumn` produces: a plain object from `StorageTypeInstance.typeParams` (typed `Record<string, unknown>`) or absent. Arrays cannot reach this path in practice. Do not flag the missing array/primitive handling here as a defect.
Learnt from: wmadden-electric
Repo: prisma/prisma-next PR: 765
File: packages/2-sql/4-lanes/relational-core/src/codec-ref-for-column.ts:65-74
Timestamp: 2026-06-08T10:59:26.822Z
Learning: In `packages/2-sql/4-lanes/relational-core/src/codec-ref-for-column.ts` (prisma/prisma-next), the `isStorageTypeInstance` branch canonicalizes `StorageTypeInstance` entries with absent or empty `typeParams` (i.e., `{}`) by returning a `CodecRef` without a `typeParams` field — identical to the non-parameterized-column branch. This is intentional design: `StorageTypeInstance.typeParams` is typed as `Record<string, unknown>`, so arrays can never reach this branch via the TypeScript type system, and the `isStorageTypeInstance` guard provides additional runtime protection. Empty `{}` is canonicalized away deliberately to avoid breaking content-keyed memoization and to prevent spurious codec parameterization mismatches at runtime. Do not flag the empty-object canonicalization here as a potential array-handling bug.
Learnt from: wmadden-electric
Repo: prisma/prisma-next PR: 750
File: packages/1-framework/0-foundation/contract/src/value-set-ref.ts:20-26
Timestamp: 2026-06-07T12:46:42.592Z
Learning: In prisma/prisma-next, the `ValueSetRef` interface in `packages/1-framework/0-foundation/contract/src/value-set-ref.ts` intentionally uses a flat shape with independent `plane: 'domain' | 'storage'` and `entityKind: 'enum' | 'value-set'` fields rather than a discriminated union. This follows ADR 221's open coordinate-axis model `(plane, namespaceId, entityKind, entityName)`, which is designed to grow beyond the current two variants. Do not flag the cross-product of these fields as allowing "invalid combinations" — these refs are always emitted by the lowering pipeline (never hand-authored), so nonsensical pairings cannot arise in practice. Do not suggest collapsing this into a discriminated union.
Learnt from: CR
Repo: prisma/prisma-next PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-08T11:12:59.730Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Column access: Use `table.columns.fieldName` to avoid conflicts with table properties.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 191
File: packages/1-framework/3-tooling/cli/src/commands/contract-emit.ts:5-5
Timestamp: 2026-03-01T13:54:21.863Z
Learning: In the Prisma-next repository, prefer importing and using 'pathe' over the built-in 'node:path' module for path operations across the codebase (including CLI commands and tooling files). Apply this in TypeScript files, replacing imports from 'node:path' with 'pathe' and adjusting API usage accordingly. Ensure consistent usage across all modules and update any affected tests or tooling imports when refactoring.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 234
File: packages/2-sql/4-lanes/sql-lane/test/rich-mutation.test.ts:2-2
Timestamp: 2026-03-23T10:01:15.075Z
Learning: In the prisma/prisma-next repo, prefer using `pathe` over Node’s built-in `node:path`. For any TypeScript file (including tests, test helpers, and integration tooling) replace imports like `import { dirname, join } from 'node:path'` with `import { dirname, join } from 'pathe'`—especially when code reads files/fixtures from disk.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 234
File: packages/3-extensions/sql-orm-client/src/model-accessor.ts:83-99
Timestamp: 2026-03-23T10:01:15.206Z
Learning: When generating SQL for comparison operators in the SQL ORM client, do not emit `col = NULL` or `col <> NULL` for equality/inequality. If `value === null` and the operator is `eq`, emit `IS NULL` (via `NullCheckExpr.isNull(left)`); if the operator is `neq`, emit `IS NOT NULL` (via `NullCheckExpr.isNotNull(left)`). Otherwise, use `BinaryExpr` with `LiteralExpr.of(value)` for non-null values.
Learnt from: aqrln
Repo: prisma/prisma-next PR: 269
File: packages/3-extensions/sql-orm-client/src/types.ts:978-983
Timestamp: 2026-04-01T08:16:29.540Z
Learning: In the generated SQL contract types in prisma/prisma-next (e.g., types like `RelationParentCols`), the contract type fields are named `parentCols`/`childCols` (not `parentColumns`/`childColumns`). When writing code that consumes the generated contract type, use `parentCols`/`childCols` to match the emitted type shape; only the SQL contract builder input uses `parentColumns`/`childColumns`.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 294
File: packages/2-mongo-family/2-query/query-ast/src/read-plan.ts:4-10
Timestamp: 2026-04-04T10:08:34.220Z
Learning: In prisma/prisma-next, don’t flag declaration-emit/typecheck warnings/errors when a file uses an intentional nominal/branding pattern with a non-exported `declare const <brand>: unique symbol` as a computed property key on an exported interface (e.g., `readonly [__mongoReadPlanRow]: ...` / `readonly [aggregateResultBrand]: ...`). The `unique symbol` must remain unexported to prevent external forging, while the branded property key staying on the exported interface provides compile-time discrimination. If the build correctly emits declaration files for this pattern, treat it as intentional rather than a declaration-emit error.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 293
File: packages/1-framework/1-core/shared/contract/src/canonicalization.ts:203-216
Timestamp: 2026-04-04T12:19:05.250Z
Learning: In the prisma/prisma-next repository, do not treat `Array.prototype.sort` comparators as “potentially non-deterministic” solely because the comparator does not include explicit tie-breaking keys. This is because the repo’s required runtime is Node >= 24, where `Array.prototype.sort` is stable (equal elements retain their original relative order via a stable sort such as TimSort). Therefore, equal-comparing elements should remain deterministic without additional tie-break logic.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 334
File: packages/2-mongo-family/9-family/src/core/control-instance.ts:204-228
Timestamp: 2026-04-13T15:54:52.337Z
Learning: When reviewing TypeScript code in this repo, do not assume a property is potentially `undefined` at a call site just because an arktype schema definition uses the DSL syntax `'key?': 'type'` (a quoted string key with a trailing `?`). This DSL notation is not the same as TypeScript optional properties (`key?: type`). To determine whether `key` is truly optional/undefined-capable, verify the actual exported TypeScript type declaration (e.g., `contract-types.ts`) and the runtime validation schema (e.g., `validate-contract.ts`) for that property; only then should the review flag call-site handling for potential `undefined`.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 339
File: test/integration/test/cli.init-templates.e2e.test.ts:96-96
Timestamp: 2026-04-15T19:33:48.733Z
Learning: In prisma/prisma-next, when writing tests that run TypeScript compilation (e.g., via `tsc --noEmit`), avoid hardcoded timeout numbers (such as `30_000`). Instead, use `prisma-next/test-utils` timeout helpers—specifically `timeouts.typeScriptCompilation` (implemented in `test/utils/src/timeouts.ts`) for `tsc` compilation scenarios. Use `timeouts.spinUpPpgDev` for PostgreSQL server startup and `timeouts.databaseOperation` for database operation scenarios.
Learnt from: saevarb
Repo: prisma/prisma-next PR: 394
File: packages/1-framework/3-tooling/migration/src/graph-ops.ts:50-54
Timestamp: 2026-04-29T12:44:59.376Z
Learning: In TypeScript overload implementations, an internal “implementation-only” cast (e.g., `state as unknown as <ReturnType>`) can be safe and intentional when the public overload signatures and their structural typing prevent callers from reaching the implementation with an incompatible type. In these cases, reviewers should not flag the cast as a blind/unsafe cast, since compile-time overload enforcement substitutes for a runtime type guard. Don’t require an added `typeof`/runtime check solely to silence the cast when the overload design already prevents invalid inputs.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 417
File: packages/3-extensions/sql-orm-client/src/types.ts:309-318
Timestamp: 2026-05-08T17:07:07.145Z
Learning: In prisma/prisma-next code, prefer `ifDefined` from `prisma-next/utils/defined` for conditional object spreads instead of inline ternary-based spreads. For example, when building option objects for `ParamRef.of` (or similar codec/param construction), wrap optional values with `ifDefined(...)` so that the spread only includes the property when the value is defined, improving readability and avoiding “{...cond ? {a: b} : {}}”-style patterns. Ensure you import `ifDefined` from `prisma-next/utils/defined` and use it consistently for these conditional spreads.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 474
File: packages/2-mongo-family/9-family/src/core/mongo-target-descriptor.ts:68-74
Timestamp: 2026-05-11T05:34:02.685Z
Learning: When reviewing code in this repo (prisma/prisma-next) for the `doc-maintenance` rule/no-transient-artefacts guideline, do not flag Linear ticket IDs in source-code comments (e.g., `TML-2397`) as violations. The rule should only consider truly transient artefacts as forbidden—specifically milestone IDs, acceptance-criteria IDs (e.g., `AC11`), and sub-spec section references (e.g., `§ 3.2`).
Learnt from: wmadden
Repo: prisma/prisma-next PR: 485
File: test/integration/test/cli.init-facade-imports.e2e.test.ts:14-14
Timestamp: 2026-05-12T03:36:07.175Z
Learning: In this repo, the doc-maintenance rule allows stable reference IDs in source-code comments—e.g., Linear ticket IDs matching `TML-*` (such as `TML-2485`, `TML-2397`) and ADR references like `ADR 211`. Do not flag `TML-*` ticket ID references in code comments as violations. Instead, violations are limited to transient artifacts such as `projects/<x>/...` paths, milestone-task IDs like `T1.7` / `T2.5.3`, milestone acceptance-criteria codes like `AM12` / `AC-13`, and prose attributions like `M2 review`, `out of scope`, or `sub-spec § 4`—flag those when found in comments.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 598
File: lint-staged.config.mjs:8-8
Timestamp: 2026-05-27T15:04:44.974Z
Learning: This codebase targets macOS/Linux only (CI runs on Linux; no Windows engine qualifier). During code review, do not flag Windows-specific path concerns (e.g., backslashes vs. forward slashes) or require Windows compatibility for file paths/imports. Focus review on issues relevant to Linux/macOS execution and Node >= 20 constraints.
Learnt from: wmadden
Repo: prisma/prisma-next PR: 641
File: skills-contrib/drive-judge-harness/validate-parser.ts:3-3
Timestamp: 2026-05-30T18:55:46.083Z
Learning: In this repo, for TypeScript files that are executed directly by Node.js via native ESM TypeScript-stripping (including `node --test` and `node <file>.ts`), all relative ESM imports must include explicit `.ts` file extensions (e.g. `import { foo } from '../bar/baz.ts'`). Do not flag these `.ts` extensions as violations—removing them will break Node’s ESM resolver. This rule applies to code paths that are run under Node directly, including skills-contrib scripts, test files, and tooling.
Learnt from: wmadden-electric
Repo: prisma/prisma-next PR: 711
File: packages/2-sql/9-family/src/core/migrations/control-policy.ts:106-122
Timestamp: 2026-06-03T13:51:07.205Z
Learning: When using the Prisma Next `ifDefined` helper from `prisma-next/utils/defined`, follow its actual API: `ifDefined(key: string, value: T | undefined)`. Use it in object spreads as `...ifDefined('myKey', someValue)` (e.g., `...ifDefined('namespace', subject?.namespaceId)`) to conditionally include properties. Do NOT use callback-based forms like `ifDefined(value, (key) => ({ key }))` or any other shape that doesn’t match the `(key, value)` signature.
Overview
Slice 01 of TML-2816 (always-qualified, namespace-aware DSL/ORM surface). Adds the explicit per-namespace accessors —
sql.<ns>.<table>andorm.<ns>.<Model>— additively: the existing flat surface is retained, and its removal + the facade projection are slice 02. Branch is rebased ontoorigin/main(incl. TML-2807 —SqlModelStorage.namespaceId+ kind-agnostic storage hash).What's in this slice
Db<C>gains per-namespace facets (sql.<ns>.<table>) alongside the flat surface; a two-level proxy delegates to coordinate-aware resolvers.orm.<ns>.<Model>facets; per-namespace model resolution; ORM keys aligned to SQL storage-namespace keys.resolveStorageTable/codecRefForStorageColumn/resolveTableColumnstake a namespace coordinate; threaded through every column/codec call site so the same bare table name in two namespaces with differing columns resolves correctly through read / write / aggregate paths (discriminating tests included).db.sql.<ns>.<table>/db.orm.<ns>.<Model>reach through the postgres + sqlite facades (incl.transaction/prepare), type-locked.Why
A multi-namespace contract must be navigable by explicit namespace (
auth.usersvspublic.users), including the case where the same bare table name appears in more than one namespace. This slice adds that surface additively and makes the SQL/ORM execution pipeline namespace-aware, building on TML-2807 (storagenamespaceId) and TML-2605 (runtime qualification).Deferred to slice 02 (the breaking cut + facade projection)
dbaliased to the connector's default-namespace facet on single-namespace targets vs. the qualified surface on multi-namespace targets, driven solely bydefaultNamespaceId. This is the "bare = default namespace" ergonomic, realized at the facade as the ADR-223 "caller that supplies the default" (runtime stays target-agnostic).Db<C>facet construction (AC7).Remaining for slice 01 (to finish on this draft)
contract.jsonsnapshot-unchanged (FR7).Validation
pnpm build,pnpm typecheck(135/135),pnpm fixtures:check(no drift),pnpm lint:deps— green.mongodb-memory-serversuites, which can't run on the dev host (NixOS) — must be confirmed in CI; they're outside this slice's changed surface.Scope
Additive only — no flat-surface removal, no facade projection in this PR. Single-namespace behaviour is byte-identical throughout.
Refs: TML-2816
Summary by CodeRabbit
Release Notes
New Features
db.public.users,orm.public.User).Bug Fixes