TML-2584: target packs contribute entity descriptors with hydration and validation#552
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds required namespace ChangesUnified Namespace Discriminators and Fragments-Aware Schemas
🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
@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/extension-cipherstash
@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/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: |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/2-sql/9-family/src/core/ir/sql-contract-serializer-base.ts (1)
144-166:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftNon-
typesnamespace slots are still discarded during hydration.
hydrateSqlNamespaceEntry()only asks the registry for'types'and only re-emits{ types }. Any descriptor that registers a differentstorageSlotKeywill validate structurally, then disappear whendeserializeContract()rebuildsSqlStorage.Also applies to: 180-200
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/2-sql/9-family/src/core/ir/sql-contract-serializer-base.ts` around lines 144 - 166, The current hydration only asks for the 'types' slot and re-emits { types }, causing any other registered storage slots to be discarded during deserializeContract; update the namespace hydration (the code using hydrateNamespaceSlot in sql-contract-serializer-base.ts — e.g., the hydrateSqlNamespaceEntry logic around the hydrateNamespaceSlot('types', obj) call) to iterate over all registered storage slots (or over keys present in obj) and call hydrateNamespaceSlot for each slot key, then merge any returned hydrated slot entries into the returned namespace object via spread so non-'types' slots are preserved (also apply same fix to the similar block around lines 180-200).
🧹 Nitpick comments (3)
packages/1-framework/1-core/framework-components/src/control/control-stack.ts (1)
383-387: ⚡ Quick winUse
ifDefinedinstead of inline conditional spread.Lines 383–387 use an inline spread/ternary pattern for conditionally adding
reservedStorageSlotKeys. Replace withifDefined()per coding guidelines:+import { ifDefined } from '`@prisma-next/utils/defined`'; ... - authoringContributions: assembleAuthoringContributions(allDescriptors, { - ...(family.reservedStorageSlotKeys !== undefined - ? { reservedStorageSlotKeys: family.reservedStorageSlotKeys } - : {}), - }), + authoringContributions: assembleAuthoringContributions(allDescriptors, { + ...ifDefined('reservedStorageSlotKeys', family.reservedStorageSlotKeys), + }),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/1-framework/1-core/framework-components/src/control/control-stack.ts` around lines 383 - 387, Replace the inline conditional spread used when calling assembleAuthoringContributions (the authoringContributions property) with the standardized ifDefined utility: instead of spreading ...(family.reservedStorageSlotKeys !== undefined ? { reservedStorageSlotKeys: family.reservedStorageSlotKeys } : {}), call ifDefined(family.reservedStorageSlotKeys, v => ({ reservedStorageSlotKeys: v })) (or the project's equivalent signature) so assembleAuthoringContributions receives the same optional field via ifDefined; update the import/usages if needed to reference ifDefined in control-stack.ts.packages/2-mongo-family/2-authoring/contract-ts/src/contract-builder.ts (1)
1524-1535: 💤 Low valueDocumented cast bridges type/runtime mismatch.
The
as unknown as MongoStorageShape<string>cast is explained by the comment and marked for follow-up, but consider whether a type predicate or factory function could narrow the unsafe boundary. If class-instance construction is the intended fix, ensure it's tracked in a follow-up issue.🤖 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-mongo-family/2-authoring/contract-ts/src/contract-builder.ts` around lines 1524 - 1535, The current unsafe double-cast "as unknown as MongoStorageShape<string>" on the local variable storage (constructed from storageBody and computeStorageHash using definition.target.targetId and definition.family.familyId) hides a runtime/type mismatch; replace the ad-hoc cast with a narrow, explicit validation or factory: implement a type predicate (e.g., isMongoStorageShape(obj): obj is MongoStorageShape<string>) or a factory function (e.g., createMongoStorageShape(storageBody, computeStorageHash(...))) that constructs a class-instance or validates required fields (including handling the plain-literal `kind` vs class-instance behavior) and use that to produce storage so the unsafe cast is removed; if class-based construction is intended, ensure the factory returns a proper class instance and add a follow-up task referencing this change for tracking.packages/2-mongo-family/9-family/src/core/ir/mongo-contract-serializer-base.ts (1)
53-64: ⚡ Quick winFail fast when slot hydrators and validator fragments drift.
If a slot is registered in
namespaceSlotHydrationRegistrybut missing fromvalidatorFragments, the'+': 'reject'schema path rejects that slot beforehydrateMongoNamespaceSlot()ever runs. A constructor-time key-set check would turn this into a clear configuration error instead of a later parse failure.♻️ Proposed guard
constructor( namespaceSlotHydrationRegistry?: ReadonlyMap<string, MongoNamespaceSlotHydrationFactory>, validatorFragments?: ReadonlyMap<string, Type<unknown>>, ) { this.namespaceSlotHydrationRegistry = namespaceSlotHydrationRegistry ?? new Map(); + if (namespaceSlotHydrationRegistry !== undefined) { + const fragmentKeys = new Set(validatorFragments?.keys() ?? []); + for (const slotKey of namespaceSlotHydrationRegistry.keys()) { + if (!fragmentKeys.has(slotKey)) { + throw new Error( + `Missing validator fragment for Mongo namespace slot ${JSON.stringify(slotKey)}`, + ); + } + } + } // Mirrors the SQL base: only build a fragments-aware schema when // pack contributions exist; otherwise the cached module-level // default in `contract-schema.ts` covers the validation path. this.contractSchema = validatorFragments !== undefined && validatorFragments.size > 0🤖 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-mongo-family/9-family/src/core/ir/mongo-contract-serializer-base.ts` around lines 53 - 64, The constructor currently allows mismatched keys between namespaceSlotHydrationRegistry and validatorFragments which causes the schema to reject registered slots at parse time; in the constructor of MongoContractSerializerBase, add a guard that computes the key sets of namespaceSlotHydrationRegistry and validatorFragments (when validatorFragments is provided) and throws a clear configuration error if any key in namespaceSlotHydrationRegistry is missing from validatorFragments, referencing namespaceSlotHydrationRegistry, validatorFragments, createMongoContractSchema, contractSchema, and hydrateMongoNamespaceSlot so callers know the mismatch; perform this check before creating contractSchema so problems fail fast at construction.
🤖 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 `@drive/retro/findings.md`:
- Line 57: The H2 heading "2026-05-20 · drive-build-workflow · gap" appears more
than once (e.g., the instances at lines showing that heading around the diff and
again at 65); edit each duplicate H2 in drive/retro/findings.md to make anchors
unique by adding a short qualifier (for example append a qualifier like "—
action items", "— root cause", or a date/time suffix) so each "2026-05-20 ·
drive-build-workflow · gap" becomes distinct; ensure the qualifier is brief and
meaningful to preserve readability and update any internal links if present.
In `@packages/1-framework/1-core/framework-components/src/ir/storage.ts`:
- Line 51: Replace the unsafe double-cast used to read a dynamic slot key by
calling Reflect.get(ns, slotKey) instead of (ns as unknown as
Readonly<Record<string, unknown>>)[slotKey]; update the assignment to use
Reflect.get(ns, slotKey) for the slot variable so the result remains unknown and
the existing type-narrowing logic (the check on the next line) continues to
provide runtime safety; ensure you reference the same symbols (ns, slotKey,
slot) when making the change.
In `@packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts`:
- Around line 355-377: The top-level schema returned by the type(...) call in
contract-schema.ts currently rejects a top-level "domain" member, causing valid
contracts to fail before validateContractDomain() runs; add an optional
"domain?" entry to that top-level type (the object literal passed to return
type(...) / returned as Type<unknown>) with a permissive shape (e.g.,
Record<string, unknown> or unknown) so the schema accepts the shared
Contract.domain plane and lets validateContractDomain() perform the actual
validation.
In `@packages/2-sql/1-core/contract/src/validators.ts`:
- Around line 191-198: The Arktype schemas are out of sync with the new contract
shape: update createNamespaceEntrySchema() to require the namespace 'kind'
(remove the optional marker on 'kind') so namespace entries cannot omit it, and
update createSqlContractSchema() to accept the new optional 'domain' field (add
'domain?' with the appropriate string or specific type) instead of rejecting it;
adjust any related slot/entry merging (the spread of slotEntries) to preserve
these keys, and rely on these updated Arktype validators
(createNamespaceEntrySchema, createSqlContractSchema) so
validateSqlContractFully() uses schema validation rather than manual checks.
In `@packages/2-sql/2-authoring/contract-ts/src/contract-types.ts`:
- Around line 559-570: The namespace type declares the discriminator `kind` as
`string`, but runtime/generated contracts always use the literal
'sql-namespace'; update both branches inside the `namespaces` property (the
`__unbound__` branch and the mapped branch over
`Exclude<DefinitionNamespaces<Definition>, '__unbound__'>`) to use `readonly
kind: 'sql-namespace'` instead of `string` so the TypeScript type matches the
actual discriminator used by `Definition`/namespace implementations (refer to
`namespaces`, `__unbound__`, and `DefinitionNamespaces<Definition>` to locate
the declarations).
---
Outside diff comments:
In `@packages/2-sql/9-family/src/core/ir/sql-contract-serializer-base.ts`:
- Around line 144-166: The current hydration only asks for the 'types' slot and
re-emits { types }, causing any other registered storage slots to be discarded
during deserializeContract; update the namespace hydration (the code using
hydrateNamespaceSlot in sql-contract-serializer-base.ts — e.g., the
hydrateSqlNamespaceEntry logic around the hydrateNamespaceSlot('types', obj)
call) to iterate over all registered storage slots (or over keys present in obj)
and call hydrateNamespaceSlot for each slot key, then merge any returned
hydrated slot entries into the returned namespace object via spread so
non-'types' slots are preserved (also apply same fix to the similar block around
lines 180-200).
---
Nitpick comments:
In
`@packages/1-framework/1-core/framework-components/src/control/control-stack.ts`:
- Around line 383-387: Replace the inline conditional spread used when calling
assembleAuthoringContributions (the authoringContributions property) with the
standardized ifDefined utility: instead of spreading
...(family.reservedStorageSlotKeys !== undefined ? { reservedStorageSlotKeys:
family.reservedStorageSlotKeys } : {}), call
ifDefined(family.reservedStorageSlotKeys, v => ({ reservedStorageSlotKeys: v }))
(or the project's equivalent signature) so assembleAuthoringContributions
receives the same optional field via ifDefined; update the import/usages if
needed to reference ifDefined in control-stack.ts.
In `@packages/2-mongo-family/2-authoring/contract-ts/src/contract-builder.ts`:
- Around line 1524-1535: The current unsafe double-cast "as unknown as
MongoStorageShape<string>" on the local variable storage (constructed from
storageBody and computeStorageHash using definition.target.targetId and
definition.family.familyId) hides a runtime/type mismatch; replace the ad-hoc
cast with a narrow, explicit validation or factory: implement a type predicate
(e.g., isMongoStorageShape(obj): obj is MongoStorageShape<string>) or a factory
function (e.g., createMongoStorageShape(storageBody, computeStorageHash(...)))
that constructs a class-instance or validates required fields (including
handling the plain-literal `kind` vs class-instance behavior) and use that to
produce storage so the unsafe cast is removed; if class-based construction is
intended, ensure the factory returns a proper class instance and add a follow-up
task referencing this change for tracking.
In
`@packages/2-mongo-family/9-family/src/core/ir/mongo-contract-serializer-base.ts`:
- Around line 53-64: The constructor currently allows mismatched keys between
namespaceSlotHydrationRegistry and validatorFragments which causes the schema to
reject registered slots at parse time; in the constructor of
MongoContractSerializerBase, add a guard that computes the key sets of
namespaceSlotHydrationRegistry and validatorFragments (when validatorFragments
is provided) and throws a clear configuration error if any key in
namespaceSlotHydrationRegistry is missing from validatorFragments, referencing
namespaceSlotHydrationRegistry, validatorFragments, createMongoContractSchema,
contractSchema, and hydrateMongoNamespaceSlot so callers know the mismatch;
perform this check before creating contractSchema so problems fail fast at
construction.
🪄 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: d52333f2-6f07-4fab-b604-7ad8316cabab
⛔ Files ignored due to path filters (8)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlprojects/contract-ir-planes/adrs/0001-contract-planes.mdis excluded by!projects/**projects/contract-ir-planes/plan.mdis excluded by!projects/**projects/contract-ir-planes/slices/substrate/dispatches/01-framework-primitives.mdis excluded by!projects/**projects/contract-ir-planes/slices/substrate/dispatches/02-descriptor-mechanism.mdis excluded by!projects/**projects/contract-ir-planes/slices/substrate/plan.mdis excluded by!projects/**projects/contract-ir-planes/slices/substrate/spec.mdis excluded by!projects/**projects/contract-ir-planes/spec.mdis excluded by!projects/**
📒 Files selected for processing (39)
drive/retro/findings.mdpackages/1-framework/0-foundation/contract/src/canonicalization.tspackages/1-framework/0-foundation/contract/src/contract-types.tspackages/1-framework/1-core/framework-components/package.jsonpackages/1-framework/1-core/framework-components/src/control/control-stack.tspackages/1-framework/1-core/framework-components/src/exports/authoring.tspackages/1-framework/1-core/framework-components/src/exports/control.tspackages/1-framework/1-core/framework-components/src/exports/ir.tspackages/1-framework/1-core/framework-components/src/ir/namespace.tspackages/1-framework/1-core/framework-components/src/ir/storage.tspackages/1-framework/1-core/framework-components/src/shared/framework-authoring.tspackages/1-framework/1-core/framework-components/src/shared/framework-components.tspackages/1-framework/1-core/framework-components/test/control-stack.test.tspackages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.tspackages/2-mongo-family/1-foundation/mongo-contract/src/exports/index.tspackages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-storage.tspackages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-unbound-namespace.tspackages/2-mongo-family/1-foundation/mongo-contract/test/contract-types.test-d.tspackages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.d.tspackages/2-mongo-family/2-authoring/contract-ts/src/contract-builder.tspackages/2-mongo-family/3-tooling/emitter/src/index.tspackages/2-mongo-family/5-query-builders/orm/test/value-object-inputs.test-d.tspackages/2-mongo-family/9-family/src/core/control-descriptor.tspackages/2-mongo-family/9-family/src/core/ir/mongo-contract-serializer-base.tspackages/2-mongo-family/9-family/src/exports/ir.tspackages/2-sql/1-core/contract/src/ir/sql-storage.tspackages/2-sql/1-core/contract/src/ir/sql-unbound-namespace.tspackages/2-sql/1-core/contract/src/validators.tspackages/2-sql/2-authoring/contract-ts/src/contract-types.tspackages/2-sql/5-runtime/test/context.types.test-d.tspackages/2-sql/9-family/src/core/control-descriptor.tspackages/2-sql/9-family/src/core/ir/sql-contract-serializer-base.tspackages/2-sql/9-family/src/exports/ir.tspackages/3-extensions/sql-orm-client/test/create-input.test-d.tspackages/3-extensions/sql-orm-client/test/generated-contract-types.test-d.tspackages/3-targets/3-targets/postgres/src/core/authoring.tspackages/3-targets/3-targets/postgres/src/core/postgres-contract-serializer.tstest/integration/test/cli.emit-cli-process.e2e.test.tstest/integration/test/extension-pgvector-scenario-a.e2e.integration.test.ts
7cacb36 to
5278a6e
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/1-framework/0-foundation/contract/src/canonicalization.ts (1)
17-32:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
domainis ordered but never emitted into the canonical object.Line 26 adds
domainto top-level ordering, butcanonicalizeContractToObjectnever copiesserialized['domain']intonormalized, sodomainis dropped before ordering runs.🛠️ Proposed fix
const normalized: Record<string, unknown> = { ...ifDefined('schemaVersion', options.schemaVersion), targetFamily: serialized['targetFamily'], target: serialized['target'], profileHash: serialized['profileHash'], roots: serialized['roots'], models: serialized['models'], ...ifDefined('valueObjects', serialized['valueObjects']), + ...ifDefined('domain', serialized['domain']), storage: serialized['storage'], ...ifDefined('execution', serialized['execution']), extensionPacks: serialized['extensionPacks'], capabilities: serialized['capabilities'], meta: serialized['meta'], };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/1-framework/0-foundation/contract/src/canonicalization.ts` around lines 17 - 32, The TOP_LEVEL_ORDER includes 'domain' but canonicalizeContractToObject never copies serialized['domain'] into the normalized object so the domain section is dropped; update canonicalizeContractToObject to read serialized['domain'] (like it does for 'models', 'valueObjects', etc.) and assign it to normalized['domain'] (or build normalized.domain) before ordering/serialization so the 'domain' key is emitted in the final canonical object (refer to the TOP_LEVEL_ORDER constant and the canonicalizeContractToObject function to locate where other top-level sections are copied and mirror that logic for 'domain').
🧹 Nitpick comments (1)
packages/1-framework/1-core/framework-components/src/control/control-stack.ts (1)
383-387: 💤 Low valueUse
ifDefined()for conditional object spread.The inline ternary spread pattern can be simplified using the
ifDefinedutility.Suggested refactor
+import { ifDefined } from '`@prisma-next/utils/defined`'; ... authoringContributions: assembleAuthoringContributions(allDescriptors, { - ...(family.reservedStorageSlotKeys !== undefined - ? { reservedStorageSlotKeys: family.reservedStorageSlotKeys } - : {}), + ...ifDefined(family.reservedStorageSlotKeys, (keys) => ({ reservedStorageSlotKeys: keys })), }),Based on learnings: "prefer ifDefined from prisma-next/utils/defined for conditional object spreads".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/1-framework/1-core/framework-components/src/control/control-stack.ts` around lines 383 - 387, Replace the inline ternary object spread used when calling assembleAuthoringContributions with the ifDefined helper to make the conditional spread clearer and consistent: import ifDefined from "prisma-next/utils/defined" (or the project's defined utility), and change the spread of { reservedStorageSlotKeys: family.reservedStorageSlotKeys } controlled by family.reservedStorageSlotKeys into ifDefined(family.reservedStorageSlotKeys, () => ({ reservedStorageSlotKeys: family.reservedStorageSlotKeys })), keeping the call site authoringContributions: assembleAuthoringContributions(allDescriptors, { ...ifDefined(...) }) so the code uses the utility instead of the inline ternary.
🤖 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/1-framework/0-foundation/contract/src/contract-types.ts`:
- Around line 49-54: The documented keying for the readonly domain field
(domain[plane][namespaceId][entityKind][entityName]) has four nested map levels
but the type in contract-types.ts only defines three; update the domain type
declaration (the readonly domain? property) to four-level nesting (i.e.,
Record<string, Record<string, Record<string, Record<string, unknown>>>>) so it
matches the docstring and the expected plane→namespaceId→entityKind→entityName
shape.
In `@packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts`:
- Around line 337-343: The namespace schema currently marks 'kind' optional
('kind?': 'string') but it should be required; update the object passed to
type(...) in contract-schema.ts to use 'kind': 'string' instead of 'kind?':
'string' (i.e., remove the question mark) so the returned Type enforces a
required kind field while keeping the rest of the shape (id, collections,
slotEntries) unchanged.
---
Outside diff comments:
In `@packages/1-framework/0-foundation/contract/src/canonicalization.ts`:
- Around line 17-32: The TOP_LEVEL_ORDER includes 'domain' but
canonicalizeContractToObject never copies serialized['domain'] into the
normalized object so the domain section is dropped; update
canonicalizeContractToObject to read serialized['domain'] (like it does for
'models', 'valueObjects', etc.) and assign it to normalized['domain'] (or build
normalized.domain) before ordering/serialization so the 'domain' key is emitted
in the final canonical object (refer to the TOP_LEVEL_ORDER constant and the
canonicalizeContractToObject function to locate where other top-level sections
are copied and mirror that logic for 'domain').
---
Nitpick comments:
In
`@packages/1-framework/1-core/framework-components/src/control/control-stack.ts`:
- Around line 383-387: Replace the inline ternary object spread used when
calling assembleAuthoringContributions with the ifDefined helper to make the
conditional spread clearer and consistent: import ifDefined from
"prisma-next/utils/defined" (or the project's defined utility), and change the
spread of { reservedStorageSlotKeys: family.reservedStorageSlotKeys } controlled
by family.reservedStorageSlotKeys into ifDefined(family.reservedStorageSlotKeys,
() => ({ reservedStorageSlotKeys: family.reservedStorageSlotKeys })), keeping
the call site authoringContributions:
assembleAuthoringContributions(allDescriptors, { ...ifDefined(...) }) so the
code uses the utility instead of the inline ternary.
🪄 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: 39c33209-3b30-430a-9f12-dfecdf4fcded
⛔ Files ignored due to path filters (8)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlprojects/contract-ir-planes/adrs/0001-contract-planes.mdis excluded by!projects/**projects/contract-ir-planes/plan.mdis excluded by!projects/**projects/contract-ir-planes/slices/substrate/dispatches/01-framework-primitives.mdis excluded by!projects/**projects/contract-ir-planes/slices/substrate/dispatches/02-descriptor-mechanism.mdis excluded by!projects/**projects/contract-ir-planes/slices/substrate/plan.mdis excluded by!projects/**projects/contract-ir-planes/slices/substrate/spec.mdis excluded by!projects/**projects/contract-ir-planes/spec.mdis excluded by!projects/**
📒 Files selected for processing (41)
drive/retro/findings.mdpackages/1-framework/0-foundation/contract/src/canonicalization.tspackages/1-framework/0-foundation/contract/src/contract-types.tspackages/1-framework/1-core/framework-components/package.jsonpackages/1-framework/1-core/framework-components/src/control/control-stack.tspackages/1-framework/1-core/framework-components/src/exports/authoring.tspackages/1-framework/1-core/framework-components/src/exports/control.tspackages/1-framework/1-core/framework-components/src/exports/ir.tspackages/1-framework/1-core/framework-components/src/ir/namespace.tspackages/1-framework/1-core/framework-components/src/ir/storage.tspackages/1-framework/1-core/framework-components/src/shared/framework-authoring.tspackages/1-framework/1-core/framework-components/src/shared/framework-components.tspackages/1-framework/1-core/framework-components/test/control-stack.test.tspackages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.tspackages/2-mongo-family/1-foundation/mongo-contract/src/exports/index.tspackages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-storage.tspackages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-unbound-namespace.tspackages/2-mongo-family/1-foundation/mongo-contract/test/contract-types.test-d.tspackages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.d.tspackages/2-mongo-family/2-authoring/contract-ts/src/contract-builder.tspackages/2-mongo-family/3-tooling/emitter/src/index.tspackages/2-mongo-family/5-query-builders/orm/test/value-object-inputs.test-d.tspackages/2-mongo-family/9-family/src/core/control-descriptor.tspackages/2-mongo-family/9-family/src/core/ir/mongo-contract-serializer-base.tspackages/2-mongo-family/9-family/src/exports/ir.tspackages/2-sql/1-core/contract/src/ir/sql-storage.tspackages/2-sql/1-core/contract/src/ir/sql-unbound-namespace.tspackages/2-sql/1-core/contract/src/validators.tspackages/2-sql/2-authoring/contract-ts/src/contract-types.tspackages/2-sql/3-tooling/emitter/src/index.tspackages/2-sql/5-runtime/test/context.types.test-d.tspackages/2-sql/9-family/src/core/control-descriptor.tspackages/2-sql/9-family/src/core/ir/sql-contract-serializer-base.tspackages/2-sql/9-family/src/exports/ir.tspackages/3-extensions/sql-orm-client/test/create-input.test-d.tspackages/3-extensions/sql-orm-client/test/generated-contract-types.test-d.tspackages/3-targets/3-targets/postgres/src/core/authoring.tspackages/3-targets/3-targets/postgres/src/core/postgres-contract-serializer.tsskills/extension-author/prisma-next-extension-upgrade/upgrades/0.10-to-0.11/instructions.mdtest/integration/test/cli.emit-cli-process.e2e.test.tstest/integration/test/extension-pgvector-scenario-a.e2e.integration.test.ts
✅ Files skipped from review due to trivial changes (8)
- skills/extension-author/prisma-next-extension-upgrade/upgrades/0.10-to-0.11/instructions.md
- packages/2-sql/5-runtime/test/context.types.test-d.ts
- packages/2-sql/1-core/contract/src/ir/sql-unbound-namespace.ts
- packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-storage.ts
- packages/2-sql/9-family/src/exports/ir.ts
- packages/2-mongo-family/1-foundation/mongo-contract/test/fixtures/orm-contract.d.ts
- packages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-unbound-namespace.ts
- packages/3-extensions/sql-orm-client/test/generated-contract-types.test-d.ts
|
@coderabbitai disposition of the body-level items in your review: Outside-diff: Nitpick: Nitpick: Nitpick: |
|
Tip For best results, initiate chat on the files or code changes.
Would you like me to open a follow-up GitHub issue covering the two co-deferred items (pluralised hydration loop in both serializer bases + the Mongo fail-fast drift guard)? That would give the S1.B–S1.D work a concrete tracked handle for the hydration-completeness slice. |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts (1)
357-362:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRequire
kindon Mongo namespace envelopes.This still accepts namespace entries without
kind, so structurally invalid contracts can pass schema validation and only fail later in hydration/storage validation. The tightened contract surface makesNamespace.kindrequired, so the Mongo envelope should reject missing kinds here.Proposed fix
export function createMongoNamespaceEnvelopeSchema( fragments?: ReadonlyMap<string, Type<unknown>>, ): Type<unknown> { return type({ '+': 'reject', id: 'string', - 'kind?': 'string', + kind: 'string', 'collections?': type({ '[string]': collectionEntrySchema(fragments) }), }) as Type<unknown>; }🤖 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-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts` around lines 357 - 362, The namespace envelope schema currently allows missing Namespace.kind because it uses an optional key 'kind?' in the returned type; update the envelope in contract-schema.ts to make 'kind' required (change 'kind?' to 'kind') so the schema rejects entries without a kind and prevents invalid contracts from passing validation—locate the return in the function that constructs the namespace envelope (the type({ '+': 'reject', id: 'string', 'kind?': 'string', 'collections?': type({ '[string]': collectionEntrySchema(fragments) }) }) expression) and remove the optional marker on kind.
🤖 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/1-core/contract/src/validators.ts`:
- Around line 176-193: Fragment matches (fragments.get(kind)) currently
short-circuit the built-in validator, allowing a fragment to bypass
PostgresEnumTypeSchema; change the flow so that when a fragment is found you
still enforce the built-in enum validation for postgres-enum entries: after
computing parsed = fragment(entry), if kind === 'postgres-enum' run the
PostgresEnumTypeSchema (or the existing fallback validator that implements the
hardcoded types check) against entry and reject if it yields type.errors;
otherwise keep the existing success path for non-enum fragments. Ensure you
reference the same symbols: fragments.get(kind), fragment(entry), fallback, and
PostgresEnumTypeSchema (or the validatorSchema for 'postgres-enum') so the enum
invariant is additive rather than substituted.
In `@packages/2-sql/9-family/src/core/ir/sql-contract-serializer-base.ts`:
- Around line 162-169: The guard around processing types currently trips
whenever the types map is non-empty and 'postgres-enum' is not registered, even
if none of the entries are postgres enums; update the condition in
sql-contract-serializer-base.ts so you first scan/filter typesRaw entries for
those whose value.kind === 'postgres-enum' (or equivalent discriminator) and
only then check this.entityTypeRegistry.get('postgres-enum') === undefined and
bail; in short, replace the broad non-empty check on typesRaw with a targeted
check that at least one entry is actually a 'postgres-enum' before consulting
entityTypeRegistry, using the existing typesRaw and
this.entityTypeRegistry.get('postgres-enum') symbols to locate the logic.
---
Duplicate comments:
In `@packages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.ts`:
- Around line 357-362: The namespace envelope schema currently allows missing
Namespace.kind because it uses an optional key 'kind?' in the returned type;
update the envelope in contract-schema.ts to make 'kind' required (change
'kind?' to 'kind') so the schema rejects entries without a kind and prevents
invalid contracts from passing validation—locate the return in the function that
constructs the namespace envelope (the type({ '+': 'reject', id: 'string',
'kind?': 'string', 'collections?': type({ '[string]':
collectionEntrySchema(fragments) }) }) expression) and remove the optional
marker on kind.
🪄 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: e9ee75f2-2953-462d-acfc-609c55b7dd86
⛔ Files ignored due to path filters (7)
projects/contract-ir-planes/adrs/0001-contract-planes.mdis excluded by!projects/**projects/contract-ir-planes/plan.mdis excluded by!projects/**projects/contract-ir-planes/slices/substrate/dispatches/03-vocabulary-cleanup.mdis excluded by!projects/**projects/contract-ir-planes/slices/substrate/dispatches/04-coord-plane-axis.mdis excluded by!projects/**projects/contract-ir-planes/slices/substrate/plan.mdis excluded by!projects/**projects/contract-ir-planes/slices/substrate/spec.mdis excluded by!projects/**projects/contract-ir-planes/spec.mdis excluded by!projects/**
📒 Files selected for processing (16)
drive/retro/findings.mdpackages/1-framework/1-core/framework-components/src/ir/namespace.tspackages/1-framework/1-core/framework-components/src/ir/storage.tspackages/1-framework/1-core/framework-components/src/shared/framework-authoring.tspackages/1-framework/1-core/framework-components/test/control-stack.test.tspackages/1-framework/1-core/framework-components/test/element-coordinates.test.tspackages/1-framework/1-core/framework-components/tsconfig.jsonpackages/1-framework/1-core/framework-components/vitest.config.tspackages/2-mongo-family/1-foundation/mongo-contract/src/contract-schema.tspackages/2-mongo-family/9-family/src/core/ir/mongo-contract-serializer-base.tspackages/2-sql/1-core/contract/src/validators.tspackages/2-sql/9-family/src/core/ir/sql-contract-serializer-base.tspackages/3-targets/3-targets/postgres/src/core/authoring.tspackages/3-targets/3-targets/postgres/src/core/postgres-contract-serializer.tspackages/3-targets/3-targets/postgres/src/core/postgres-schema.tspackages/3-targets/3-targets/postgres/test/element-coordinates.test.ts
💤 Files with no reviewable changes (1)
- packages/3-targets/3-targets/postgres/src/core/authoring.ts
✅ Files skipped from review due to trivial changes (1)
- packages/3-targets/3-targets/postgres/test/element-coordinates.test.ts
…thetic Storage literal The D4-R2 test reached into SqlStorage and MongoStorage to exercise elementCoordinates against real namespace concretions. That import direction (framework → family) is a layering violation; we previously papered over it with dist-path aliases and a turbo.json build-order declaration. Both rejected on review. Rewrites the framework-side test to feed elementCoordinates a synthetic Storage literal — which is actually a stronger test, because it pins the structural contract rather than a particular family concretion. Family conformance moves to the family's own test package (mirroring the Postgres test that already lives in @prisma-next/target-postgres). Deletes the dist-path aliases and turbo.json that existed only to mask the cycle. Resolves three layering-violation comments on PR #552. Signed-off-by: Will Madden <madden@prisma.io>
…e family-owned kind When a pack contributes a `validatorSchema` fragment for the family-owned `postgres-enum` kind, the fragment must compose with the built-in `PostgresEnumTypeSchema` rather than replace it. Previously the fragment-vs-fallback dispatch was substitutive: a registered fragment for `postgres-enum` bypassed the family schema entirely, letting family-owned enum invariants drift with pack contributions. Pass the fallback kind to `namespaceSlotEntrySchema` and run both the fallback AND the fragment when the entry kind matches the fallback kind. For non-fallback kinds, the fragment alone validates (the family fallback is single-kind-specific and would reject any other shape). For entries with no matching fragment, fall through to the fallback unchanged. Addresses CodeRabbit PR #552 comment 3279123062. Signed-off-by: Will Madden <madden@prisma.io>
…m entries The serializer's legacy 'types' slot guard threw whenever the map was non-empty and the postgres-enum factory was unregistered — even if every entry carried some other contributed discriminator with a matching hydration factory. That blocked the new discriminator-based extension path behind an unrelated Postgres-specific check. Replace the bulk non-empty check with a targeted scan: only throw when at least one entry has kind === 'postgres-enum' AND the postgres-enum factory isn't registered. Entries with other kinds flow through the existing per-property structural dispatch in hydrateSqlNamespaceEntry. Addresses CodeRabbit PR #552 comment 3279123067. Signed-off-by: Will Madden <madden@prisma.io>
wmadden
left a comment
There was a problem hiding this comment.
Manual review complete
d58ce7e to
8bda0aa
Compare
…thetic Storage literal The D4-R2 test reached into SqlStorage and MongoStorage to exercise elementCoordinates against real namespace concretions. That import direction (framework → family) is a layering violation; we previously papered over it with dist-path aliases and a turbo.json build-order declaration. Both rejected on review. Rewrites the framework-side test to feed elementCoordinates a synthetic Storage literal — which is actually a stronger test, because it pins the structural contract rather than a particular family concretion. Family conformance moves to the family's own test package (mirroring the Postgres test that already lives in @prisma-next/target-postgres). Deletes the dist-path aliases and turbo.json that existed only to mask the cycle. Resolves three layering-violation comments on PR #552. Signed-off-by: Will Madden <madden@prisma.io>
…e family-owned kind When a pack contributes a `validatorSchema` fragment for the family-owned `postgres-enum` kind, the fragment must compose with the built-in `PostgresEnumTypeSchema` rather than replace it. Previously the fragment-vs-fallback dispatch was substitutive: a registered fragment for `postgres-enum` bypassed the family schema entirely, letting family-owned enum invariants drift with pack contributions. Pass the fallback kind to `namespaceSlotEntrySchema` and run both the fallback AND the fragment when the entry kind matches the fallback kind. For non-fallback kinds, the fragment alone validates (the family fallback is single-kind-specific and would reject any other shape). For entries with no matching fragment, fall through to the fallback unchanged. Addresses CodeRabbit PR #552 comment 3279123062. Signed-off-by: Will Madden <madden@prisma.io>
…m entries The serializer's legacy 'types' slot guard threw whenever the map was non-empty and the postgres-enum factory was unregistered — even if every entry carried some other contributed discriminator with a matching hydration factory. That blocked the new discriminator-based extension path behind an unrelated Postgres-specific check. Replace the bulk non-empty check with a targeted scan: only throw when at least one entry has kind === 'postgres-enum' AND the postgres-enum factory isn't registered. Entries with other kinds flow through the existing per-property structural dispatch in hydrateSqlNamespaceEntry. Addresses CodeRabbit PR #552 comment 3279123067. Signed-off-by: Will Madden <madden@prisma.io>
1e6ec81 to
5baa952
Compare
Slice spec for S1.A (Substrate): two-plane IR primitives + entity-coordinate +
pack-contributed entity-kind descriptor mechanism. First slice of the
contract-ir-planes sub-project (TML-2584); first slice of the
target-extensible-ir-namespaces umbrella overall.
What this spec captures:
- Scope: type-additive changes only — Contract gains domain plane (unpopulated),
Namespace narrows to required { id, kind }, Storage gains
elementCoordinates() generator, AuthoringContributions.entityTypes extended
with storageSlotKey + hydrate + validatorSchema. Postgres pack registers
postgresEnums slot via the new descriptor. No on-disk contract changes; no
enum-entry migration (S1.B); no cross-ref encoding migration (S1.C); no
deletion of subsumed surfaces (S1.D).
- Approach: two sequential dispatches inside the slice — D1 framework
primitives, D2 descriptor mechanism + Postgres registration.
- 15 pre-named edge cases with dispositions (handle / explicitly out / defer).
Load-bearing ones: Namespace.kind promotion exposing optional-treating
consumers (F2 territory), instanceof NamespaceBase brand-check sites (4
files audited), SQL validator hardcoded enum schema staying alongside
composition surface (F1 territory — risk pre-named so the dispatch loop
catches a relocation if it happens).
- 5 implementer-degree-of-freedom open questions with working positions
(EntityCoordinate file location, registry shape, validatorSchema
composition order, elementCoordinates yield over the types slot, hydrate
callback signature).
- Slice-DoD with N/A manual-QA + rationale (no user-observable surface
touches) + substrate-hygiene grep gates per drive/calibration/grep-library.md.
- Slice DoR + team-overlay walk: all canonical items met except slice plan
(handed off to drive-plan-slice next). Linear linkage uses parent TML-2584
per no-sub-issues rule.
Grounded in a surface inventory gathered via explore subagent: file paths,
type signatures, call-site counts, instanceof brand-check sites,
PostgresEnumStorageEntry import count (45 files), postgres-enum literal
count (32 files), SQLite rejection-path imports (3 files). Inventory itself
not committed (transient research artifact); spec embeds the load-bearing
facts.
Next step: hand off to drive-plan-slice for the two-dispatch decomposition
under projects/contract-ir-planes/slices/substrate/plan.md.
Refs: TML-2584
Signed-off-by: Will Madden <madden@prisma.io>
Two-dispatch decomposition per the project plan; refines the high-level "two dispatches" framing into DoR-ready briefs with sizing + gates + tier routing. D1 — Framework type primitives. Add EntityCoordinate + Storage elementCoordinates() (additive); add Contract.domain plane (type-only, unpopulated); narrow Namespace.kind from optional on IRNode to required on Namespace. Bounded cascade (4 brand-check sites audited; none access .kind). Re-decomposition trigger named: > 30 files cascaded halts and re-plans as D1a/D1b/D1c. M sized. D2 — Descriptor mechanism + Postgres postgresEnums registration. Extend AuthoringEntityTypeDescriptor with storageSlotKey + hydrate + validatorSchema. SQL + Mongo validators compose contributed schemas additively (no-op when nothing contributed; the existing hardcoded SQL enum schema STAYS — F1 risk pre-named to prevent silent relocation). SQL family base serializer extends entityTypeRegistry with a parallel Map for pack-contributed slot hydration. Postgres pack registers postgresEnums; serializer enum branch refactored to delegate to the registry. No on-disk contract changes. M sized. Tier routing per drive/calibration/model-tier.md: both dispatches are substrate / spec-interpretation (table row 1) → Opus. With sonnet/low banned and sonnet/mid + opus/medium not selectable from the orchestrator, both dispatches route to claude-opus-4-7-thinking-high. Every slice-spec edge case mapped to a covering dispatch (or explicit deferral); slice-DoD conditions reachable from the dispatch sequence; per-dispatch DoR + team overlay walked. Refs: TML-2584 Signed-off-by: Will Madden <madden@prisma.io>
D1 (framework primitives) brief per drive brief-discipline (8 required sections). Lives at dispatches/01-framework-primitives.md per the team plan-shape convention. Tier: Opus (substrate change per drive/calibration/model-tier.md row 1). Sized M with re-decomposition trigger at > 30 cascaded files or > 90 min wall-clock. Edge cases pulled forward from slice spec D1-portion (8 with dispositions, including F2 directly-relevant + F5 destructive-git forbidden). Grep gates pulled from drive/calibration/grep-library.md § IR substrate hygiene + an F2-territory check specific to this dispatch (rg .kind ?? packages/). Per-slice reviews/code-review.md scaffolded but gitignored repo-wide (projects/**/reviews/ in .gitignore — code-review.md is iteration-loop scratch, not a shipped artifact). Next: spawn implementer subagent with the brief, in background, model opus-high. Refs: TML-2584 Signed-off-by: Will Madden <madden@prisma.io>
R1 attempted to add elementCoordinates() as a required member on the framework Storage interface. That broke structural assignability of emitted contract.d.ts literals against Contract<SqlStorage> consumers (the printed literal carries storageHash / namespaces / types? but no method members), cascading into ~56 typecheck diagnostics across 29 fixture files and a pnpm fixtures:check byte-stability violation. R2 reframes the walk as a free elementCoordinates(storage) function dispatched on Namespace.kind via an inline lookup table in @prisma-next/framework-components/ir. The Storage interface is unchanged; emitted literals keep satisfying every consumer; the byte-stability gate holds. D2 replaces the inline table with the pack-contributed descriptor registry. The brief now enumerates: Step 0 revert of R1's 8 unstaged files (done by orchestrator), Steps 1–8 verbatim edits, Step 9 audit-only grep gates. Model tier moves from Opus 4.7 to Composer-2.5 because the redirect makes the dispatch strictly mechanical with no design latitude; hard escalation triggers re-route to Opus if any boundary breaks. Refs TML-2584. Signed-off-by: Will Madden <madden@prisma.io>
…rdinates walk D1 (projects/contract-ir-planes/slices/substrate/spec.md, TML-2584): ship EntityCoordinate, the free elementCoordinates(storage) generator with a hardcoded sql/mongo slot-key table, Contract.domain?, and canonicalizer TOP_LEVEL_ORDER — no Storage interface members, no fixture drift. Signed-off-by: Will Madden <madden@prisma.io>
D1 (projects/contract-ir-planes/slices/substrate/spec.md, TML-2584): require kind on Namespace/NamespaceBase, flip four namespace-payload declarations, tighten BuiltStorage and mongo contract-builder literals, remove mongo-emitter F2 fallback, and align type-test fixtures with the narrowed substrate. Signed-off-by: Will Madden <madden@prisma.io>
D1 (projects/contract-ir-planes/slices/substrate/spec.md, TML-2584): avoid enumerable kind on mongo contract-ts storage hash input; resolve namespace kind for d.ts emission when plain builder envelopes omit it. Signed-off-by: Will Madden <madden@prisma.io>
…hrough Remove FamilyDescriptor.reservedStorageSlotKeys and control-stack assembly options; update entityTypes merge tests for discriminator-keyed surface. TML-2584; projects/contract-ir-planes/slices/substrate/spec.md D3. Signed-off-by: Will Madden <madden@prisma.io>
…ityTypeRegistry Remove namespaceSlotHydrationRegistry; hydrate namespace envelopes structurally and dispatch entries through entityTypeRegistry keyed by entry.kind. TML-2584; projects/contract-ir-planes/slices/substrate/spec.md D3. Signed-off-by: Will Madden <madden@prisma.io>
…discriminator Fold contributed validatorSchema fragments by entry kind; hardcoded SQL types? slot stays for F1 additive coexistence. TML-2584; projects/contract-ir-planes/slices/substrate/spec.md D3. Signed-off-by: Will Madden <madden@prisma.io>
…egistry Drop storageSlotKey from postgresAuthoringEntityTypes; collect hydrate and validator fragments by discriminator. Make PostgresSchema.kind non-enumerable for the structural walk invariant. TML-2584; projects/contract-ir-planes/slices/substrate/spec.md D3. Signed-off-by: Will Madden <madden@prisma.io>
Assert elementCoordinates yields table coordinates for Postgres-promoted namespaces whose runtime kind is schema, not sql-namespace. TML-2584; projects/contract-ir-planes/slices/substrate/spec.md D3. Signed-off-by: Will Madden <madden@prisma.io>
…/spec/plan edits, deferred-to-tickets) Lands the substrate-altitude outputs of the artefact-review architect + principal-engineer passes (under projects/contract-ir-planes/reviews/ artifacts-pre-d3/): - ADR Decision 1 (A05): frame symmetric extensibility - both domain and storage planes are pack-extensible through the same descriptor surface; the current asymmetry is content, not mechanism. - ADR Decision 3 (A03): extend EntityCoordinate with a plane axis (domain | storage); honest about what the coordinate addresses; domain -> storage directional invariant lives in a separate validator, not the coord shape. - ADR Decision 5 (A07 + A08): record the rejected storageSlotKey? alternative + slot-key naming convention (essence + singular: enum, policy, role, ...). Future pack contributions follow the convention from S1.B onward. - Project plan (F09): add Risk #5 (inherited "decided" fields propagate without challenge); name brief-assembly pre-flight questions for S1.B+. - Project spec OQ1 (A08): lock to essence + singular. - All references to postgresEnums in the project + slice spec/plan flipped to enum (the slot S1.B introduces from the start). - Slice spec/plan: add D4 dispatch (coord plane axis code change + 1 test); explicitly deferred A01 (slot rename) and A02b (.entries lift) to standalone tickets. - D4 brief at slices/substrate/dispatches/04-coord-plane-axis.md. Standalone tickets filed for the deferred cleanup work: - TML-2634 (A01 substrate rename: tables -> table, etc.) - TML-2636 (A02b namespace concretion .entries lift) Both relatedTo TML-2584; both in the "Target-Extensible IR + Namespaces" project per the no-sub-issues rule. Refs: TML-2584 Related: TML-2634, TML-2636 Signed-off-by: Will Madden <madden@prisma.io>
Extend EntityCoordinate with plane: 'domain' | 'storage' as the first field and populate plane: 'storage' in elementCoordinates(storage) yields. TML-2584 (S1.A substrate D4) Signed-off-by: Will Madden <madden@prisma.io>
…storage' for SQL/Mongo/Postgres concretions Add element-coordinates.test.ts covering SqlStorage, MongoStorage, and PostgresSchema namespace walks. Wire dist path aliases for cross-family fixtures without framework→target package dependencies. TML-2584 (S1.A substrate D4) Signed-off-by: Will Madden <madden@prisma.io>
…del-routing over-correction
Two findings from S1.A:
1. F7 candidate: Composer-2.5 D4 hit an unbriefed structural blocker
(Turbo build cycle on cross-family devDep) and worked around it with
dist-path aliases including a framework→target alias that hides a
forbidden dep edge from lint:deps. The 5-file refusal trigger fired
(diff was 6 files); the orchestrator accepted the deviation post-hoc
instead of treating the fire as the halt signal. Reviewer caught it,
returned NEEDS-REWORK option (2). The meta-lesson: refusal triggers
are calibration instruments; post-hoc acceptance retunes them
downward each time. Suggested action: pre-survey the dep graph for
any single-test-imports-from-siblings dispatch; treat trigger fires
as halt-and-reconfirm, not accept-after-the-fact.
2. friction: orchestrator over-routed D3 to Opus despite the brief
being fully-settled mechanical retirement (Composer-sized).
Operator corrected ("Can you use the Composer-2.5 executor
please?"). Suggested action: the dispatch brief Model tier
paragraph must name the calibration check explicitly; if the
one-sentence justification reads as "substrate work" without
naming a specific design-judgment site, the tier is probably
over-routed.
Both upstream-candidate.
TML-2584 (S1.A substrate)
Signed-off-by: Will Madden <madden@prisma.io>
…; move Postgres-promoted coverage to the postgres package (D4-R2) The framework→target path alias hid a forbidden framework→target dependency behind resolution. Postgres-promoted namespace coverage belongs in target-postgres, which already asserts plane: storage for that case. TML-2584 Signed-off-by: Will Madden <madden@prisma.io>
… test aliases (CI typecheck on fresh checkout) CI runs typecheck on a fresh checkout where sibling dist/ artefacts do not exist yet. tsconfig path aliases to those dist files create an implicit build-order dependency Turbo cannot see from package.json alone; declare it explicitly in turbo.json so typecheck and test wait for sql-contract and mongo-contract builds. TML-2584. Signed-off-by: Will Madden <madden@prisma.io>
…thetic Storage literal The D4-R2 test reached into SqlStorage and MongoStorage to exercise elementCoordinates against real namespace concretions. That import direction (framework → family) is a layering violation; we previously papered over it with dist-path aliases and a turbo.json build-order declaration. Both rejected on review. Rewrites the framework-side test to feed elementCoordinates a synthetic Storage literal — which is actually a stronger test, because it pins the structural contract rather than a particular family concretion. Family conformance moves to the family's own test package (mirroring the Postgres test that already lives in @prisma-next/target-postgres). Deletes the dist-path aliases and turbo.json that existed only to mask the cycle. Resolves three layering-violation comments on PR #552. Signed-off-by: Will Madden <madden@prisma.io>
…attern Signed-off-by: Will Madden <madden@prisma.io>
…e family-owned kind When a pack contributes a `validatorSchema` fragment for the family-owned `postgres-enum` kind, the fragment must compose with the built-in `PostgresEnumTypeSchema` rather than replace it. Previously the fragment-vs-fallback dispatch was substitutive: a registered fragment for `postgres-enum` bypassed the family schema entirely, letting family-owned enum invariants drift with pack contributions. Pass the fallback kind to `namespaceSlotEntrySchema` and run both the fallback AND the fragment when the entry kind matches the fallback kind. For non-fallback kinds, the fragment alone validates (the family fallback is single-kind-specific and would reject any other shape). For entries with no matching fragment, fall through to the fallback unchanged. Addresses CodeRabbit PR #552 comment 3279123062. Signed-off-by: Will Madden <madden@prisma.io>
…m entries The serializer's legacy 'types' slot guard threw whenever the map was non-empty and the postgres-enum factory was unregistered — even if every entry carried some other contributed discriminator with a matching hydration factory. That blocked the new discriminator-based extension path behind an unrelated Postgres-specific check. Replace the bulk non-empty check with a targeted scan: only throw when at least one entry has kind === 'postgres-enum' AND the postgres-enum factory isn't registered. Entries with other kinds flow through the existing per-property structural dispatch in hydrateSqlNamespaceEntry. Addresses CodeRabbit PR #552 comment 3279123067. Signed-off-by: Will Madden <madden@prisma.io>
…builders The contract-ts and contract-psl builders previously constructed plain object literals for namespaces, bypassing MongoNamespacePayload's non-enumerable kind materialization. The Mongo emitter then needed a fallback helper to guess 'mongo-namespace' when the plain-literal input lacked kind. Flips both builders to `new MongoStorage(...)`, mirroring the SQL pattern in @prisma-next/sql-contract-ts. The constructor materializes kind non-enumerably, so JSON.stringify emits the same bytes (fixture byte-stability preserved) but the runtime IR exposes kind reliably. MongoStorage.kind is also installed non-enumerably (matching SqlNode) so in-memory contracts pass arktype reject validation during emit. Deletes the mongoNamespaceKindForDts helper (now unreferenced) and the MONGO_NAMESPACE_KIND constant. Updates the mongo contract-schema JSDoc to note that the optional `kind?` on the validator is intentional — wire-shape gated, not IR-shape gated. Closes the Mongo portion of TML-2648; SQLite analogue still pending. Signed-off-by: Will Madden <madden@prisma.io>
…EntityTypeDescriptor
The hydrate field's function body was always a typed-input wrapper around
output.factory's body; the family-base serializer can call output.factory
directly with the validated raw value, with ctx synthesized from the pack's
own bootstrap-time {family, target} knowledge.
Same F6 pattern as the retired storageSlotKey? field — a descriptor surface
field carrying information another descriptor field already carries.
Signed-off-by: Will Madden <madden@prisma.io>
…spaceEntry with an arktype schema The hydration loop's cast-chain (`raw as Record<string, unknown>` + `obj['id'] as string | undefined ?? nsId` + per-slot `as` casts) is exactly what arktype exists to replace. A small NamespaceRawSchema narrows `raw` once; the slot loop iterates typed properties after. Hydration still runs after the family-base validator, so the schema here is ergonomics + defence-in-depth — behavior is byte-identical. Signed-off-by: Will Madden <madden@prisma.io>
…rageHashBase brand
D6 R2 swapped the `interpreter.test.ts` storage expectation to `new MongoStorage({
storageHash: expect.stringMatching(/^sha256:/) as unknown as `sha256:${string}`,
...
})` but the bridge cast only landed on the template-literal portion of the
storageHash type, not the `StorageHashBase<…>` brand the constructor accepts.
Local + CI typecheck has been failing on `tsc` at `interpreter.test.ts:803`
ever since.
Extend the cast through to `StorageHashBase<`sha256:${string}`>` (and import
the type alongside the existing `@prisma-next/contract/types` imports). Test
output is identical (the matcher still passes the same runtime value through
the constructor); the change is purely the compile-time bridge.
Signed-off-by: Will Madden <wmadden@users.noreply.github.com>
Signed-off-by: Will Madden <madden@prisma.io>
Snapshot the briefs that drove the two in-PR cleanups, for the audit trail drive-build-workflow expects: - `06-mongo-builder-class-lift.md` — Mongo TS builder + PSL interpreter switch from plain object literals to `new MongoStorage(...)`, deleting the `mongoNamespaceKindForDts` emitter helper. Re-dispatched as R2 after R1 caught a fixtures-check fail from the missed PSL site. - `07-hydrate-and-arktype-cleanup.md` — drop the redundant `AuthoringEntityTypeDescriptor.hydrate?` (Part A) and replace the hand-rolled raw narrowing in `hydrateSqlNamespaceEntry` with an arktype `NamespaceRawSchema` (Part B). Signed-off-by: Will Madden <madden@prisma.io>
5baa952 to
c2cf4ab
Compare
…thetic Storage literal The D4-R2 test reached into SqlStorage and MongoStorage to exercise elementCoordinates against real namespace concretions. That import direction (framework → family) is a layering violation; we previously papered over it with dist-path aliases and a turbo.json build-order declaration. Both rejected on review. Rewrites the framework-side test to feed elementCoordinates a synthetic Storage literal — which is actually a stronger test, because it pins the structural contract rather than a particular family concretion. Family conformance moves to the family's own test package (mirroring the Postgres test that already lives in @prisma-next/target-postgres). Deletes the dist-path aliases and turbo.json that existed only to mask the cycle. Resolves three layering-violation comments on PR #552. Signed-off-by: Will Madden <madden@prisma.io>
…e family-owned kind When a pack contributes a `validatorSchema` fragment for the family-owned `postgres-enum` kind, the fragment must compose with the built-in `PostgresEnumTypeSchema` rather than replace it. Previously the fragment-vs-fallback dispatch was substitutive: a registered fragment for `postgres-enum` bypassed the family schema entirely, letting family-owned enum invariants drift with pack contributions. Pass the fallback kind to `namespaceSlotEntrySchema` and run both the fallback AND the fragment when the entry kind matches the fallback kind. For non-fallback kinds, the fragment alone validates (the family fallback is single-kind-specific and would reject any other shape). For entries with no matching fragment, fall through to the fallback unchanged. Addresses CodeRabbit PR #552 comment 3279123062. Signed-off-by: Will Madden <madden@prisma.io>
…m entries The serializer's legacy 'types' slot guard threw whenever the map was non-empty and the postgres-enum factory was unregistered — even if every entry carried some other contributed discriminator with a matching hydration factory. That blocked the new discriminator-based extension path behind an unrelated Postgres-specific check. Replace the bulk non-empty check with a targeted scan: only throw when at least one entry has kind === 'postgres-enum' AND the postgres-enum factory isn't registered. Entries with other kinds flow through the existing per-property structural dispatch in hydrateSqlNamespaceEntry. Addresses CodeRabbit PR #552 comment 3279123067. Signed-off-by: Will Madden <madden@prisma.io>
Today, every new storage-entity kind a target wants to ship — Postgres enums, future Postgres policies, SQLite emulated enums, MongoDB indexes — needs a bespoke serializer override that knows where in the contract those entries live, how to rehydrate them into IR class instances, and how to validate the on-the-wire shape. The override is hand-written, family-specific, and duplicates work the framework already does for built-in entities.
This PR lets a target pack declare those entries as descriptors instead. Postgres is the first consumer:
The descriptor's
discriminatorfield (already required for every pack contribution) is the single key the family-base serializer and the family's arktype validator both look up. The existingoutput.factorydoubles as the hydration constructor — given the raw envelope it returns the IR instance, so no separatehydratefield is needed. Pack-contributedvalidatorSchemafragments compose additively with the family's built-in validator — both must accept the entry — so contributions extend the framework's validation surface rather than replacing it.Decision
This PR ships three coupled pieces that together let pack contributions reach the same end-to-end completeness as built-in entities:
AuthoringEntityTypeDescriptor—validatorSchema?— wired through the family arktype validators. Hydration reuses the existingoutput.factoryfield (no parallelhydrate?field); the family-base hydration registry stores the factory partial-applied with a per-targetAuthoringEntityContext. Keyed on the descriptor's existingdiscriminatorfield.elementCoordinates(storage)in@prisma-next/framework-components/iryields{ plane, namespaceId, entityKind, entityName }tuples over anyStorage-shaped value, walking each namespace's own-enumerable properties structurally — no family-name lookup table, no up-front knowledge of slot vocabularies.Namespace.kindnarrowed to required. Codifies the structural invariant the walk depends on (every namespace carries its family discriminator). Runtime non-enumerableObject.defineProperty(this, 'kind', { value, enumerable: false })is unchanged, so emitted JSON envelopes don't gain akindfield — only the type-side narrows. The Mongo TS builder and PSL interpreter both constructnew MongoStorage(...)instances now, so Mongokindis materialized at runtime alongside SQL.Contractalso gains an optionaldomain?plane field as scaffolding for the upcoming domain-plane work; it is empty in this PR.Mechanism
The descriptor surface
AuthoringEntityTypeDescriptor<Input, Output>gains one field:validatorSchema?: Type<unknown>— an arktype schema fragment for the entry's on-the-wire shape. The family validator looks up the fragment by the entry'skindand composes it with the family's built-in fallback validator (both must pass) when the kind matches the family-owned kind; otherwise it validates against the pack fragment alone. Composition is keyed ondiscriminator, so two packs contributing different entity kinds never collide.Hydration reuses the existing
output.factory(input, ctx)field — the family-base serializer wraps it as(raw) => factory(raw, ctx)and stores the wrapped function inentityTypeRegistry. The per-targetctx({ family: 'sql', target: 'postgres' }for Postgres) is a module-level constant in the target pack's serializer.Authors don't need to know about the registry: declaring the descriptor in a pack's
AuthoringContributions.entityTypesis enough — the registry boot wires the rest.packages/1-framework/1-core/framework-components/src/shared/framework-authoring.tspackages/2-sql/9-family/src/core/ir/sql-contract-serializer-base.tspackages/2-sql/1-core/contract/src/validators.tspackages/3-targets/3-targets/postgres/src/core/postgres-contract-serializer.tsThe namespace-entry hydration path uses an arktype
NamespaceRawSchema({ id: 'string', 'kind?': 'string' }) to narrow the raw envelope, replacing the chain of hand-rolledraw as Record<string, unknown>+obj['id'] as stringcasts that previously prefixed every slot iteration.The namespace walk
elementCoordinates(storage)iterates each namespace's own-enumerable properties structurally — it doesn't need to know what slots a family or target has contributed. Whatever entity-bearing maps a namespace exposes (tables,collections,enum,policy, future pack contributions) get walked uniformly. The function is free, not a method on theStorageinterface, because emittedContract<SqlStorage>literals are structurally checked againstSqlStorageat every consumption site, and emitted literals carry no method members; adding a method to the interface would break every emitted-fixture consumer.The yielded coordinate carries a
planeaxis ('storage'for this walk;'domain'reserved for the upcoming sibling walk over the domain plane).packages/1-framework/1-core/framework-components/src/ir/storage.tsPostgres delegates instead of overriding
PostgresContractSerializer's hand-rolled enum-branch override is gone; the family-base hydration loop dispatches toentityTypeRegistry.get(entry.kind)for every namespace entry, including Postgres enums. The registered factory wrapspostgresEnumDescriptor.output.factory(input, POSTGRES_AUTHORING_CTX)so hydration produces the samePostgresEnumTypeinstance the prior override did — byte-identical fixtures, zero drift.The family-base guard that previously rejected any
types-slot population whenpostgres-enumwas unregistered now scopes the rejection to entries whosekind === 'postgres-enum'; entries with a different contributedkindand a matching factory pass through to their own hydration path.packages/3-targets/3-targets/postgres/src/core/authoring.tspackages/3-targets/3-targets/postgres/src/core/postgres-contract-serializer.tsMongo authoring lift
The Mongo TS builder and PSL interpreter previously constructed plain object literals for namespaces, bypassing
MongoStorage's class-sidekindmaterialization. Both now constructnew MongoStorage(...)instances;MongoStorage.kindandMongoNamespacePayload.kindare installed non-enumerably (mirroringSqlNode), so runtime walks seekind: 'mongo-storage'/kind: 'mongo-namespace'while on-disk envelopes stay byte-identical. ThemongoNamespaceKindForDtsemitter helper that bridged the gap is deleted.packages/2-mongo-family/2-authoring/contract-ts/src/contract-builder.tspackages/2-mongo-family/2-authoring/contract-psl/src/interpreter.tspackages/2-mongo-family/1-foundation/mongo-contract/src/ir/mongo-storage.tsWhat changed (behaviour + evidence)
AuthoringEntityTypeDescriptorgainsvalidatorSchema?; the existingoutput.factorydoubles as the hydration constructor. Existing pack contributions compile unchanged (the new field is optional). Evidence: Postgres enum descriptor consumesvalidatorSchema; postgres serializer round-trip tests pass byte-identically.namespaceSlotEntrySchema(fallbackKind, fragments)runs the family fallback AND the contributed fragment when the entry'skindmatchesfallbackKind; runs the fragment alone for non-fallback kinds. Evidence: SQL family validator tests pass with the widened composition; postgres pack registration continues to validate enum entries byte-identically.hydrateSqlNamespaceEntrynarrows the raw envelope viaNamespaceRawSchemarather than chains ofraw as Record<string, unknown>casts. Evidence:family-sqltest suite (296 tests) green; postgres / mongo / sqlite fixture round-trips all pass.elementCoordinates(storage)walks anyStorage-shaped value. Evidence: syntheticStorageliteral inpackages/1-framework/1-core/framework-components/test/element-coordinates.test.tspins the structural contract;packages/2-sql/1-core/contract/test/element-coordinates.test.ts,packages/2-mongo-family/1-foundation/mongo-contract/test/element-coordinates.test.ts, andpackages/3-targets/3-targets/postgres/test/element-coordinates.test.tsverify each family/target concretion satisfies the structural promise (including Postgres-promoted namespaces withkind === 'schema').Namespace.kindis required. Codifies the invariant every existing code path already maintains. Type cascade through ~6 source files + ~6.test-d.ts/ handcrafted.d.tsfixture files. On-disk JSON envelopes unchanged.kindat runtime. Both TS builder and PSL interpreter constructMongoStorageclass instances;MongoStorage.kind/MongoNamespacePayload.kindare non-enumerable so emitted envelopes stay unchanged. The emitter helpermongoNamespaceKindForDtsis deleted.Contract.domain?field exists. Empty in this PR; populated when the sibling domain-plane walk lands.What stays out of scope
pnpm fixtures:checkpasses with zero drift.tables,types,collectionskeep their existing names; new pack contributions use the essence + singular convention (Postgres enums register asenum, notenumsorpostgresEnums). Rename of the existing plurals is deferred — see follow-ups.storage.<ns>.typesslot still carries Postgres enum entries. Migration tostorage.<ns>.enumlands in the next slice; the existing hardcoded SQL'types?'validator entry coexists additively with the descriptor-driven path until then.elementCoordinates(domain)walk.Compatibility & risk
validateSqlContractFully(json)gains an additive optional second parameter:options?: { contractSchema?: Type<unknown> }. Existing call sites compile unchanged.Namespace.kindtype-side narrowing only. External consumers readingnamespace.kindasstring | undefinednow read it asstring. The runtime non-enumerable defineProperty pattern is unchanged, so emitted JSON envelopes don't carry akindfield. (SQLite authoring builders still construct plain object literals that don't materializekind; the SQLite arktype validator keeps its existing'kind?': 'string'accordingly — see TML-2648 for the SQLite analogue of the Mongo lift landed here.)contract.jsonfiles.Testing
pnpm typecheck,pnpm lint:deps,pnpm fixtures:checkall pass. Per-package:framework-components,sql-contract,mongo-contract,mongo-contract-psl,mongo-contract-ts,mongo-emitter,target-postgres,family-sql,family-mongoall green.pnpm test:packageshas pre-existing flakes inadapter-postgresintegration /cipherstash/cli-telemetry/cliredirects that reproduce onorigin/mainand are unrelated.Two grep gates pass clean:
rg 'looksLikeFlat|normalizeStorageForHydration|stampNamespaceOnTable|normalizeStorageEnvelopeShape|isFlatTablesInput|isFlatTypesInput' packages/(no relocated-dual-shape leftovers) andrg 'storageSlotKey|reservedStorageSlotKeys|namespaceSlotHydrationRegistry|SLOT_KEYS_BY_NAMESPACE_KIND' packages/(no remnants of the retired surface — see alternatives). Two additional D7 grep gates:rg '\.hydrate\b|hydrate\?:|hydrate: \(raw' packages/(zero — the redundanthydrate?field is gone) andrg 'raw as Record<string, unknown>|obj\[.id.\] as string' packages/2-sql/9-family/(zero — the hand-rolled narrowing inhydrateSqlNamespaceEntryis gone).Follow-ups
This PR is the first of four slices under TML-2584:
storage.<ns>.typesslot tostorage.<ns>.enumper the essence-singular convention; regenerate the Postgres fixtures that change in the rename; drop the hardcoded SQL'types?'validator entry.models,valueObjects); add the siblingelementCoordinates(domain)walk; delete the now-subsumed surfaces (findSqlTable,extractStorageElementNames,UnboundTables<C>, etc.).Three deferred cleanups have their own tickets, paired to this slice:
tables→table,types→type,collections→collection) to match the essence-singular convention. ~150 consumer call sites + ~50–80 fixture regens; deferred from this slice because of blast radius..entriesso the structural-walk invariant becomes invariant-by-construction. Stack on TML-2634.kindis materialized at runtime; then tighten the SQLite arktype validator to requirekind. (Mongo half of this work shipped in this PR.)Alternatives considered
hydrate?as a separate descriptor field. An earlier shape required the pack to declare bothoutput.factory(input, ctx)(the authoring-time constructor) andhydrate(raw)(the serializer-time rehydrator). The two were always one-line wrappers around the same constructor call, withhydrate(raw) = (raw) => new T(raw as TInput)adding only the cast back fromunknown. Replaced by storing(raw) => output.factory(raw, ctx)directly in the registry; the registration call site does the one cast once.elementCoordinates()as a required method on theStorageinterface. Adding the method would changeSqlStorage's structural type; emittedContract<SqlStorage>literals carry no method members; every emitted-fixture consumer's structural check would fail (~56 typecheck diagnostics across 29 emittedcontract.d.tsfiles). The free-function shape keeps the interface's surface unchanged. Rationale inprojects/contract-ir-planes/adrs/0001-contract-planes.mdDecision 3.storageSlotKey?field on the descriptor + parallel slot-keyed registry +FamilyDescriptor.reservedStorageSlotKeys+ collision validator. An earlier iteration of this PR shipped all four pieces. They were retired in a follow-up cleanup once the redundancy withdiscriminatorbecame clear:storageSlotKeycarried no informationdiscriminatordidn't already carry for every entry the registry hydrated; the parallel registry doubled the test surface for zero invariant the single registry didn't already enforce;reservedStorageSlotKeyswas a framework-layer table redundantly encoding what contract hydration's structural validation already provides; the collision validator caught nothing the family base's hardcoded built-in slot list didn't already enforce structurally. The shipping shape is the cleaned-up substrate — singleentityTypeRegistry, keyed ondiscriminator.elementCoordinates(storage). An earlier shape hardcoded'sql-namespace'/'mongo-namespace'into the framework layer (layering violation) and silently failed for Postgres-promoted namespaces (PostgresSchema, runtimekind === 'schema'). The structural property walk needs no up-front knowledge of slot names — contract hydration has already enforced the structural shape; the walk just enumerates entity-bearing properties.discriminator, with the contributor opting out by not registering a fragment.enums) or target-prefixed (postgresEnums). Settled at essence + singular (enum). The slot key reads as the entity kind;discriminatorcarries the contributor identity. Singular form is future-compatible with the rename of the existing plural slots (TML-2634).hydrateSqlNamespaceEntry. The previous shape ranraw as Record<string, unknown>thenobj['id'] as stringat the top of every namespace hydration, with theidfallback to the namespace's outer key buried in the cast chain. Replaced with an arktypeNamespaceRawSchema({ id: 'string', 'kind?': 'string' }) and an explicittypeof rawRecord['id'] === 'string' ? rawRecord['id'] : nsIdfallback ahead of the parse — the schema validates what it owns (the envelope shape); the call site owns the fallback policy. Undeclared keys (tables,types, etc.) pass through via arktype's default ignore behavior.Linked issue
Refs TML-2584.
Skill update
n/a — internal substrate. No CLI flags, no
prisma-next.config.tsfields, no public TypeScript API breaking changes; theAuthoringEntityTypeDescriptorfield additions are all optional and target pack authors, not end-user contract authors.Checklist
git commit -s) per the DCOTML-NNNN: <sentence-case title>form