Commit 14060ca
feat(codec-registration-completion): unify codec registration (TML-2357) (#417)
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
> **Status: ready for review.** All milestones complete; all 14
acceptance criteria PASS or LANDED at HEAD; validation gates green
workspace-wide. Post-implementation surface cleanup (F9 + F10) and
post-feedback hygiene round (F11–F39) landed on top.
Completes the registration-side migration that the parent
codec-registry-unification project (merged via [ADR
208](docs/architecture%20docs/adrs/ADR%20208%20-%20Higher-order%20codecs%20for%20parameterized%20types.md))
deliberately deferred. (TML-2357)
## Spec & plan
-
[`projects/codec-registration-completion/spec.md`](projects/codec-registration-completion/spec.md)
— eight ACs (AC-0..AC-7), three pinning cases.
-
[`projects/codec-registration-completion/plan.md`](projects/codec-registration-completion/plan.md)
— five milestones with validation gates and risks.
-
[`projects/codec-registration-completion/specs/class-based-codec-design.spec.md`](projects/codec-registration-completion/specs/class-based-codec-design.spec.md)
— six implementation-level ACs (AC-CB-1..6) covering the abstract-class
hierarchy.
## Milestones
| # | Goal | Spec ACs | Status |
|---|---|---|---|
| **M0** | Class-based codec migration + per-codec helpers + Strength 3
deletion sweep | AC-0, AC-1, AC-CB-1..6 | **SATISFIED** at `a210fa1c5` |
| **M1** | Narrow runtime `Codec` instance + descriptor-keyed metadata
reads | AC-3 | **LANDED** at `1be7564c4`; reverified through M4 |
| **M2** | Native descriptor migration + bridge / `aliasCodec` /
`arktypeJsonEmitCodec` deletion | AC-2, AC-4 | **ABSORBED into M0**
(Phase B + Phase C) |
| **M3** | `ParamRef.refs` plumbing + encode-side `forColumn` +
`forCodecId` retirement | AC-5 | **SATISFIED** at `3f0ec224a` |
| **M4** | Delete `JsonSchemaValidatorRegistry`; retire
`'json-validator'` trait | AC-6 | **SATISFIED** at `e055c9455` |
| **F9 + F10** | Public-API surface cleanup: registry-only public
surface; drop transition vocabulary | hygiene | **SATISFIED** at
`c4d81ad1c` |
| **F11–F39** | Post-feedback hygiene round: 27 findings closed; F38
rejected; SQL `CodecRegistry` consolidated into
`CodecDescriptorRegistry` | hygiene + correctness | **SATISFIED** at
`0e3aafc0b` |
| **AC-7** | Validation gates green | AC-7 | **PASS** at every milestone
close + every hygiene-round close |
## What landed
- **Class-based codec hierarchy.** `interface CodecDescriptor<P>` +
`abstract class CodecDescriptorImpl<P>`, paired with `interface
Codec<Id, Traits, Wire, Input>` + `abstract class CodecImpl<...>`.
Per-codec column helpers (e.g. `vectorColumn(N)`) directly invoke
`descriptor.factory(params)` to preserve method-level generics;
`satisfies ColumnHelperFor<D>` ties helpers to their descriptor without
polymorphism. `AnyCodecDescriptor` (alias for `CodecDescriptor<any>`) is
the canonical heterogeneous-storage type.
- **Strength 3 forcing-function deletion.** Every legacy carrier
deleted: `mkCodec`, `defineCodec`, `defineCodecGroup`,
`defineCodecBundle`, `CodecDefBuilder*`,
`synthesizeNonParameterizedDescriptor`, instance-keyed
`ExtractCodecTypes`, `byScalar` maps, `dataTypes` exports,
`sqlCodecDefinitions`, `codecDescriptorDefinitions`,
`pgVectorRepresentativeCodec` placeholder, `parameterizedCodecs:` slot,
`CodecParamsDescriptor`, `arktypeJsonEmitCodec`, `aliasCodec`,
`aliasDescriptor` function form, the `'json-validator'` trait,
`JsonSchemaValidatorRegistry` infrastructure, `arktypeParamsSchema`
helper, the SQL-family `CodecRegistry` interface (consolidated into
`CodecDescriptorRegistry`). Closing-grep zero call-sites.
- **`ParamRef` + `ProjectionItem` carry `refs?: { table; column }`**;
`validateParamRefRefs` builder-pipeline pass enforces refs for
parameterized codec ids; encode/decode dispatch consults `metadata.refs`
first via `contractCodecs.forColumn(table, column)`.
- **JSON validation lives in the resolved codec's `decode` body**
(arktype-json's inline pattern from TML-2229). Decode-error envelope
equivalence verified via `codec-async.test.ts:408-456`.
- **`ColumnTypeDescriptor` relocated** from
`@prisma-next/contract-authoring` (layer 2) to
`@prisma-next/framework-components` (layer 1) so codec base types live
with the framework primitives.
- **F8 portability fix** (`fae3bd688`): `CodecTypes` exposed at the
public `exports/codec-types.ts` entry point with a `Resolve<T>`
materializer to break tsdown's chunk-private path reference and restore
consumer-side typecheck.
- **F9 + F10 surface cleanup** (`3d57f9ecd..c4d81ad`): each
codec-shipping package now exposes only column helpers + a
`<package>CodecRegistry` instance + `type` re-exports of descriptor
types. Internal `codecDescriptorMap` / `codecDescriptorClassList` no
longer surface through public exports. Transition vocabulary (`Class`
suffixes, `class-form` / `class-based` in comments, `codecs-class.ts`
filenames) scrubbed.
## Post-feedback hygiene round (F11–F39)
19 commits between `c4d81ad1c..0e3aafc` close 27 findings surfaced
from orchestrator-principal review and 30 GitHub PR review threads.
Highlights:
- **F11** (`f725db1f8`) — rename `ast/sql-codecs-class.ts` →
`ast/sql-codecs.ts`, split helpers into `ast/sql-codec-helpers.ts`.
- **F12** (`7d56a39b4`) — move parameterization predicate onto
`CodecDescriptorImpl.isParameterized` getter; retire the standalone
`IsParameterizedCodecId` callback type.
- **F13 + F14 + F20** (`cfa078a23`) — align arktype params schemas with
`TParams` optionality (`'length?': '...'`); delete `arktypeParamsSchema`
helper; consumer sites direct-assign with `: StandardSchemaV1<TParams>`.
The `arktype` runtime dep stays in `@prisma-next/contract` because
`validate-contract.ts` consumes it directly for structural validation —
that's by design, not a follow-up.
- **F15** (`1051f24e8`) — replace `unique symbol` trait phantom with a
string-key phantom property (`__codecTraits`) to avoid Node bundling
failure modes.
- **F16 + F17** (`083fff350`, `a278bd034`) — rewrite
codec-authoring-guide alias section + fix self-referential JSDoc
helper-source reference.
- **F18** (`7ee52fae0`) — assert leaf scalar type in
`no-emit-typed-flow.test-d.ts` to make AC-CB-6's literal claim explicit.
- **F19 + F31 + F32 + F33** (`81c55248f`, `bc4e8d716`) — harden
`extractCodecLookup`: lift inline imports, tighten `id` to non-optional
`string`, refine the silent catch so non-parameterized codecs throw
immediately while parameterized factories that tolerate empty params
still materialise representatives.
- **F21** (`f1be14d55`) — `createStubAdapter` returns a stable codec
registry instance (no per-call rebuild).
- **F22 + F26 + F29** (`20f99bffd`) — dispatch correctness:
`refsFromLeft` walks via `collectColumnRefs` to preserve refs for
wrapping expressions; encode-side `forColumn` fall-through is
structurally safe (F19 + `ambiguousCodecIds` rejection + `byCodecId`
column-correct materialisation); pgvector `length` threaded into
`PgVectorCodec` constructor with `assertVector` validating the dimension
at every ingress.
- **F23 + F24 + F25** (`48ed1d135`) — type predicate
`isArktypeSchemaLike` replaces blind cast in `rehydrateSchema`;
`@ts-expect-error` replaces `as any` + biome-ignore in tests; `toExtend`
replaces deprecated `toMatchTypeOf` matcher.
- **F27** (`21b4cbca7`, `0e3aafc0b`) — retire SQL-family
`CodecRegistry.register()` mutation surface in favour of
`buildCodecRegistry(descriptors)` builder; phase-2 deletes the
`CodecRegistry` interface entirely. Single registry surface in the SQL
family is now `CodecDescriptorRegistry`.
- **F28** (`9881a7efe`) — `buildCodecDescriptorRegistry` throws on
duplicate `codecId`.
- **F30** (`7a3faf20f`) — `codecDescriptorMap` relocated to
`core/codec-type-map.ts`; `Resolve<T>` materialisation kept at the
`exports/` boundary per F8.
- **F34 + F35 + F36** (`4178072fa`) — postgres render hygiene: scale
validation in `pgNumericRenderOutputType`; ISO 8601 regex for
timestamps; string validation for enum values.
- **F37** (`182d10c88`) — `SqliteDatetimeCodec` rejects `Invalid Date`
in decode/decodeJson.
- **F38** — **rejected.** Default identity codecs on `CodecImpl` would
constrain its type signature and obscure where real conversion work
happens. Convention stays: explicit identity overrides at codec-author
site.
- **F39** (`80ba4fd60`) — `enumParamsSchema` and `EnumParams` tightened
to `readonly string[]`.
Closure-mechanism note for F22: the implementer chose a structural
argument over fail-fast on `forColumn` miss — F19's refinement makes
`extractCodecLookup` skip parameterized descriptors that don't tolerate
empty params; `buildContractCodecRegistry`'s `ambiguousCodecIds` set
throws `RUNTIME.TYPE_PARAMS_INVALID` on multi-instance ids; for the
non-ambiguous parameterized case `byCodecId` stores the column-correct
per-instance codec. Reviewer cross-checked all three legs and accepted.
## Notable side effect: 16 pre-existing e2e failures resolved by M3
Before M3, the e2e suite ran 75/91 with 16 failures of the form `Codec
'...' resolves to multiple parameterized instances; column-aware
dispatch is required.` — top-level field shortcuts
(`select('vectorCol')`) emitted `IdentifierRef` AST that didn't carry
`(table, column)` context, so decode-side `resolveProjectionCodec` fell
back to `forCodecId` and threw. M3's `ProjectionItem.refs` extension +
decode-side parity in `decoding.ts` closed the path. **e2e is now
91/91.**
## Validation gates at HEAD `0e3aafc0b`
| Gate | Result |
|---|---|
| `pnpm typecheck` | PASS — 123/123 |
| `pnpm lint:deps` | PASS — 727 modules / 1444 deps / 0 violations |
| `pnpm fixtures:check` | PASS — zero drift (demo emit byte-identical
against `origin/main`) |
| `pnpm build` | PASS — 62/62 |
| `pnpm test:e2e` | **PASS — 91/91** (16 pre-existing failures resolved
by M3) |
| `pnpm test:packages` | PASS in scope (residual: pre-existing 7
sql-orm-client pgvector wire-format + TML-2402 parallel-flake) |
## Review artifacts
`projects/codec-registration-completion/reviews/` carries three review
artifacts produced by the reviewer subagent during orchestration. After
user feedback that the initial structure didn't match the canonical
skill output, all three were rewritten to conform to
`/drive-pr-local-review` (flat F-numbered findings + AC verification
table) and `/drive-pr-walkthrough` (intent-first semantic narrative).
Final reviewer verdict at HEAD `0e3aafc0b`: **SATISFIED** with 12 PASS /
2 WEAK on the AC scoreboard. The two WEAKs are AC-7 (acknowledged
baseline test failures in sql-orm-client pgvector wire format + TML-2402
parallel flake) and AC-CB-5 (a single internal `descriptor as unknown as
AnyDescriptor` cast inside `buildCodecDescriptorRegistry` — purely a
registry-internal heterogeneous-storage erasure, not a public surface
concern).
## Linear follow-ups filed during close-out
- **[TML-2402](https://linear.app/prisma-company/issue/TML-2402)** —
`pnpm test:packages` parallel-execution flake (`adapter-postgres` /
`cli` / `sql-orm-client`; passes cleanly in isolation). P3.
- **[TML-2403](https://linear.app/prisma-company/issue/TML-2403)** —
Turbo cache-keying gap on transitive AST/type-system changes (worked
around with `pnpm build --force`). P4.
- **[TML-2405](https://linear.app/prisma-company/issue/TML-2405)** —
Codec dispatch follow-up: reference codec instances on the lowered Plan
instead of carrying `(table, column)` lookup keys on the AST. The
current shape's validator pass is a smell; instance-on-Plan would retire
`forColumn`/`forCodecId` from the runtime dispatch surface entirely.
Architectural successor to this work. P3.
## Out of scope
- **Mongo codec registration migration** — folded into
[TML-2324](https://linear.app/prisma-company/issue/TML-2324) (Mongo
runtime `forColumn` plumbing).
- **Renaming `Codec`** — type name stays; only the field set narrows.
- **Reshaping the async codec runtime** ([ADR
204](docs/architecture%20docs/adrs/ADR%20204%20-%20Single-Path%20Async%20Codec%20Runtime.md))
or `CodecCallContext` ([ADR
207](docs/architecture%20docs/adrs/ADR%20207%20-%20Codec%20call%20context%20per-query%20AbortSignal%20and%20column%20metadata.md)).
- **`pgEnumCodec` placeholder factory audit** (already clean at HEAD;
documented in ADR 208 § Future work).
- **Retiring `CodecLookup.get(id)` and `ProjectionItem.refs` /
`ParamRef.refs` lookup-key carriers from the AST** — TML-2405.
## Note on project artifacts
Per the user's mid-flight directive at close-out,
`projects/codec-registration-completion/` is **preserved in-tree** (the
standard transient-directory deletion was reverted at `5b0113a5a`). The
directory's review artifacts under `reviews/` are gitignored and don't
appear in the diff; they live in the working tree only as historical
context.
<!-- CURSOR_AGENT_PR_BODY_END -->
<div><a
href="https://cursor.com/agents/bc-a00fe249-d674-4cb5-8939-5b9d17b36650"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a
href="https://cursor.com/background-agent?bcId=bc-a00fe249-d674-4cb5-8939-5b9d17b36650"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </div>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Unified descriptor-driven codec system and class-based codec
authoring; per-codec column helpers.
* Codecs now receive per-call context (AbortSignal + column provenance).
* **Documentation**
* Added codec authoring reference and multiple ADR updates clarifying
descriptor and async model.
* **Improvements**
* Stronger parameter validation and improved output-type rendering for
parameterized codecs.
* Query builder/runtime now propagate column refs into expressions and
parameter encoding.
* **Tests**
* Expanded type and runtime tests covering descriptor-driven flows and
helpers.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>1 parent c48ad71 commit 14060ca
253 files changed
Lines changed: 11626 additions & 7406 deletions
File tree
- .agents/rules
- .cursor/rules
- .vscode
- docs
- architecture docs
- adrs
- subsystems
- reference
- examples/prisma-next-demo/test
- packages
- 1-framework
- 0-foundation/contract
- 1-core/framework-components
- src
- control
- exports
- test
- 2-authoring
- contract
- src
- test
- ids
- src
- 3-tooling
- cli
- src/control-api
- test
- control-api
- emitter
- src
- test
- 2-mongo-family
- 1-foundation/mongo-codec
- src
- exports
- test
- 2-authoring/contract-psl
- src
- test
- 7-runtime
- src
- test
- codecs
- 2-sql
- 2-authoring
- contract-psl
- src
- test
- contract-ts
- src
- test
- helpers
- 3-tooling/emitter/test
- 4-lanes
- relational-core
- src
- ast
- exports
- test
- ast
- sql-builder
- src/runtime
- test/runtime
- 5-runtime
- src
- codecs
- middleware
- test
- 3-extensions
- arktype-json
- src
- core
- exports
- test
- pgvector
- src
- core
- exports
- test
- sql-orm-client
- src
- test
- integration
- 3-mongo-target
- 1-mongo-target/src/exports
- 2-mongo-adapter
- src
- core
- exports
- test
- 3-targets
- 3-targets
- postgres
- src
- core
- exports
- test
- sqlite
- src
- core
- exports
- test
- 6-adapters
- postgres
- src
- core
- exports
- test
- sqlite
- src
- core
- exports
- test
- projects/codec-registration-completion
- specs
- test
- integration/test
- authoring/parity
- cross-package
- mongo
- utils
- src
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
| 78 | + | |
78 | 79 | | |
79 | 80 | | |
80 | 81 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| 26 | + | |
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
297 | 297 | | |
298 | 298 | | |
299 | 299 | | |
300 | | - | |
| 300 | + | |
301 | 301 | | |
302 | 302 | | |
303 | 303 | | |
| |||
330 | 330 | | |
331 | 331 | | |
332 | 332 | | |
333 | | - | |
334 | | - | |
335 | | - | |
336 | | - | |
| 333 | + | |
337 | 334 | | |
338 | 335 | | |
339 | 336 | | |
| |||
969 | 966 | | |
970 | 967 | | |
971 | 968 | | |
972 | | - | |
973 | | - | |
974 | | - | |
| 969 | + | |
975 | 970 | | |
976 | 971 | | |
977 | 972 | | |
| |||
Lines changed: 1 addition & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
164 | 164 | | |
165 | 165 | | |
166 | 166 | | |
167 | | - | |
| 167 | + | |
168 | 168 | | |
169 | | - | |
170 | 169 | | |
171 | 170 | | |
172 | 171 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
Lines changed: 5 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
3 | 5 | | |
4 | 6 | | |
5 | 7 | | |
6 | 8 | | |
7 | 9 | | |
8 | | - | |
| 10 | + | |
9 | 11 | | |
10 | 12 | | |
11 | 13 | | |
| |||
42 | 44 | | |
43 | 45 | | |
44 | 46 | | |
45 | | - | |
| 47 | + | |
46 | 48 | | |
47 | 49 | | |
48 | 50 | | |
| |||
119 | 121 | | |
120 | 122 | | |
121 | 123 | | |
122 | | - | |
| 124 | + | |
123 | 125 | | |
124 | 126 | | |
125 | 127 | | |
| |||
Lines changed: 4 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
3 | 5 | | |
4 | 6 | | |
5 | 7 | | |
| |||
124 | 126 | | |
125 | 127 | | |
126 | 128 | | |
127 | | - | |
| 129 | + | |
128 | 130 | | |
129 | 131 | | |
130 | 132 | | |
| |||
210 | 212 | | |
211 | 213 | | |
212 | 214 | | |
213 | | - | |
| 215 | + | |
214 | 216 | | |
215 | 217 | | |
216 | 218 | | |
| |||
Lines changed: 7 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
3 | 5 | | |
4 | 6 | | |
5 | 7 | | |
| |||
63 | 65 | | |
64 | 66 | | |
65 | 67 | | |
66 | | - | |
| 68 | + | |
67 | 69 | | |
68 | 70 | | |
69 | 71 | | |
| |||
74 | 76 | | |
75 | 77 | | |
76 | 78 | | |
77 | | - | |
| 79 | + | |
78 | 80 | | |
79 | 81 | | |
80 | 82 | | |
81 | 83 | | |
82 | 84 | | |
83 | 85 | | |
84 | 86 | | |
85 | | - | |
| 87 | + | |
86 | 88 | | |
87 | 89 | | |
88 | 90 | | |
89 | 91 | | |
90 | 92 | | |
91 | 93 | | |
92 | 94 | | |
93 | | - | |
| 95 | + | |
94 | 96 | | |
95 | 97 | | |
96 | 98 | | |
| |||
103 | 105 | | |
104 | 106 | | |
105 | 107 | | |
106 | | - | |
| 108 | + | |
107 | 109 | | |
108 | 110 | | |
109 | 111 | | |
| |||
0 commit comments