diff --git a/.changeset/isqlite-comment-drift-sweep-round-two.md b/.changeset/isqlite-comment-drift-sweep-round-two.md new file mode 100644 index 0000000000..30e9b2ab1a --- /dev/null +++ b/.changeset/isqlite-comment-drift-sweep-round-two.md @@ -0,0 +1,43 @@ +--- +'@objectstack/cli': patch +--- + +Correct the remaining out-of-package comments that still described +`SqlDriver#formatOutput`'s two timestamp passes as gated on `if (this.isSqlite)`. + +Since ADR-0053 D-F1 (#13973) both passes — the `AUDIT_TIMESTAMP_COLUMNS` pass and the +`normalizeSqliteDatetimeOutput` pass over `datetimeFields` — run on every dialect, so the +record read door presents the builtin audit columns and every declared `Field.datetime` +as canonical ISO-8601-`Z` text on Postgres and MySQL as well as SQLite. Measured on the +tree rather than recalled: in `packages/drivers/driver-sql/src/sql-driver.ts` the +`if (this.isSqlite)` arm inside `formatOutput` opens at line 16965 and closes at 17026, +covering only the JSON codec and the numeric-scalar repair, while the audit-column loop +(17046) and the `normalizeSqliteDatetimeOutput` loop (17061) both sit at the method's top +level, below that closing brace. + +Two of the corrected comments were load-bearing rather than merely stale. The +`service-storage` one drew a conclusion for a live read door from the false premise, and +it also claimed that folding at the driver's read boundary "would reverse the deliberate +`withPostgresCalendarDayAsText` decision" — which is what #13973 ruled and did. The two +`packages/cli` ones attached the wrong reason to a true fact: the holder probe reads +through the raw-SQL seam, so `formatOutput` never runs on that path at all, and the +dialect divergence there survives the ruling for that reason and not because of a gate. + +Comments only — no runtime behaviour, no exported symbol and no public type changes. +`@objectstack/cli` is the one package named here because its per-file build carries the +amended text verbatim into `dist/commands/migrate/duplicates.js` and +`dist/commands/migrate/duplicates.d.ts`, so its published output changes. +`@objectstack/metadata-protocol` is deliberately NOT named: its edits are all in test +files, which are not published. `@objectstack/service-storage` and `@objectstack/metadata` +are deliberately NOT named either: their source edits are JSDoc blocks on the internal +`usableCreatedAt` and `canonicalTimestampText`, and both bundles strip them — measured +absent from `dist/`, with each package's identifier found in the same `dist/` (and the +exported `StrandedOrphanInventoryEngine` docblock present in `dist/index.d.ts`) as the +firing control that the probe works. + +Three carve-outs are preserved rather than flattened: `withPostgresCalendarDayAsText` is +untouched by that ruling (D-F2 — the client library still materialises `timestamptz` / +`DATETIME(3)` as a `Date`); the Invalid `Date` residue still stands (D-F3 — the one `Date` +shape with no canonical text leaves the read door unchanged, so no sentence claims the +read door never hands out a `Date`); and the ruled-B consumer arms stay, with only the +prose explaining why they exist corrected. diff --git a/packages/cli/src/commands/migrate/duplicates.created-at-canonical.test.ts b/packages/cli/src/commands/migrate/duplicates.created-at-canonical.test.ts index 70c061aafd..193e77cfbe 100644 --- a/packages/cli/src/commands/migrate/duplicates.created-at-canonical.test.ts +++ b/packages/cli/src/commands/migrate/duplicates.created-at-canonical.test.ts @@ -7,10 +7,9 @@ * * `DuplicateHolder.createdAt` is declared `string | null`, and the mapper built * it with `String(row.created_at)`. `created_at` is a BUILTIN audit column — not - * in `datetimeFields`, and `SqlDriver#formatOutput` repairs it only inside its - * `if (this.isSqlite)` arm — and the holder probe reads through the raw-SQL seam, - * so no presentation runs on this path at all. The dialect therefore decides what - * arrives: + * in `datetimeFields`, so no declared-field coercion reaches it — and the holder + * probe reads through the raw-SQL seam, so `formatOutput` does not run on this + * path at all. The dialect therefore decides what arrives: * * - **Postgres / MySQL** materialise a JS `Date`, so `String()` ran * `Date.prototype.toString`: `Sun Aug 30 2026 18:19:25 GMT+0800 (China @@ -43,8 +42,18 @@ * expression really produced. * * ⛔ Not a `??` fallback and not a driver change: `withPostgresCalendarDayAsText` - * is a deliberate driver decision and is untouched. The CLI is a leaf consumer - * with a declared `string | null`, so the canonical spelling is owed here. + * is a deliberate driver decision and is untouched ([ADR-0053 D-F2]). The CLI is + * a leaf consumer with a declared `string | null`, so the canonical spelling is + * owed here. + * + * ⚠️ What keeps this file live is the SEAM, ⛔ not an `if (this.isSqlite)` gate + * inside `formatOutput`. That gate is gone: #13973 ([ADR-0053 D-F1]) lifted both + * of `formatOutput`'s timestamp passes — the `AUDIT_TIMESTAMP_COLUMNS` pass and + * the `normalizeSqliteDatetimeOutput` pass over `datetimeFields` — out of it and + * they run on EVERY dialect, so the RECORD read door presents the canonical text + * everywhere. The holder probe does not go through that door; §B3 of the pin + * named above reads the same row raw through knex and still gets the dialect's + * `Date` on the live cells, which is the fact §A1 drives. */ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; diff --git a/packages/cli/src/commands/migrate/duplicates.ts b/packages/cli/src/commands/migrate/duplicates.ts index 3e42719f29..277c168680 100644 --- a/packages/cli/src/commands/migrate/duplicates.ts +++ b/packages/cli/src/commands/migrate/duplicates.ts @@ -578,14 +578,23 @@ export function answeringSeam(exec: SeedTenancyExec): SeedTenancyExec { /** * The canonical `createdAt` spelling for one holder row. * - * `created_at` is a BUILTIN audit column: it is not in `datetimeFields`, and - * `SqlDriver#formatOutput` repairs it only inside its `if (this.isSqlite)` arm — - * and the holder probe reads through the raw-SQL seam anyway, so no presentation - * runs on this path at all. The DIALECT therefore decides what lands in - * `row.created_at`, and both sides of that asymmetry are pinned in - * `packages/drivers/driver-sql/src/sql-driver-13567-audit-stamp-materialisation.test.ts`: - * Postgres and MySQL materialise a JS `Date`, SQLite and its siblings hand back - * canonical ISO-8601 UTC text. + * `created_at` is a BUILTIN audit column: it is not in `datetimeFields`, so no + * declared-field coercion reaches it — and the holder probe reads through the + * raw-SQL seam anyway, so `formatOutput` does not run on this path at all. The + * DIALECT therefore decides what lands in `row.created_at`: Postgres and MySQL + * materialise a JS `Date`, SQLite and its siblings hand back canonical ISO-8601 + * UTC text. Pinned in + * `packages/drivers/driver-sql/src/sql-driver-13567-audit-stamp-materialisation.test.ts` + * §B3, which reads the same row raw through knex and still gets the dialect's + * `Date` on the live cells. + * + * ⚠️ The reason is the SEAM, ⛔ not an `if (this.isSqlite)` gate inside + * `formatOutput`. That gate is gone: #13973 ([ADR-0053 D-F1]) lifted both of + * `formatOutput`'s timestamp passes — the `AUDIT_TIMESTAMP_COLUMNS` pass and + * the `normalizeSqliteDatetimeOutput` pass over `datetimeFields` — out of it, + * and they run on EVERY dialect now, so the record read door presents the + * canonical text everywhere. This path simply never reaches that door, which is + * why the divergence survives HERE after the driver closed it there. * * `DuplicateHolder.createdAt` is declared `string | null`, so this leaf consumer * is the side that owes the canonical spelling — the same form the `occurredAt` diff --git a/packages/metadata-protocol/src/protocol-14038-list-commits-created-at-iso.test.ts b/packages/metadata-protocol/src/protocol-14038-list-commits-created-at-iso.test.ts index 302f9814d9..99554acd99 100644 --- a/packages/metadata-protocol/src/protocol-14038-list-commits-created-at-iso.test.ts +++ b/packages/metadata-protocol/src/protocol-14038-list-commits-created-at-iso.test.ts @@ -10,15 +10,27 @@ * ## The defect * * `created_at` is an engine-injected audit column: it is not in - * `datetimeFields`, and `SqlDriver#formatOutput` repairs it (both the - * builtin-audit-column repair and the `datetimeFields` fold) only inside its - * `if (this.isSqlite)` arm (`sql-driver.ts`, `formatOutput`). Postgres and - * MySQL therefore hand this column out of the record read door as a JS - * `Date`, while the SQLite family hands out canonical ISO-Z text — pinned - * live in - * `packages/drivers/driver-sql/src/sql-driver-13567-audit-stamp-materialisation.test.ts`. - * So on the production default driver, `listCommits` handed every - * in-process consumer a `Date` in a field the type says is a `string`. + * `datetimeFields`, and when this landed `SqlDriver#formatOutput` repaired it + * (both the builtin-audit-column repair and the `datetimeFields` fold) only + * inside its `if (this.isSqlite)` arm (`sql-driver.ts`, `formatOutput`). + * Postgres and MySQL therefore handed this column out of the record read door + * as a JS `Date`, while the SQLite family handed out canonical ISO-Z text. So + * on the production default driver, `listCommits` handed every in-process + * consumer a `Date` in a field the type says is a `string`. + * + * #13973 ([ADR-0053 D-F1]) has since lifted BOTH passes out of that gate — they + * run on EVERY dialect now, so the record read door presents the canonical + * text — and the pin that recorded the asymmetry records that contract instead + * (`packages/drivers/driver-sql/src/sql-driver-13567-audit-stamp-materialisation.test.ts` + * §B1, inverted on purpose). + * + * ⚠️ That does not make the cases below historical. `withPostgresCalendarDayAsText` + * is untouched by that ruling ([ADR-0053 D-F2]) — the CLIENT still materialises + * `timestamptz` / `DATETIME(3)` as a `Date`, and what moved is where the driver + * folds it — and the `Date` domain at this mapper did not close: `driver-sql` + * hands an INVALID `Date` through unchanged ([ADR-0053 D-F3]) and non-SQL + * drivers materialise their own. What these cases own is the mapper's behaviour + * per INPUT SHAPE, which outlives the dialect fact. * * ## Why the fixture drives a hand-made `Date` * diff --git a/packages/metadata-protocol/src/protocol.commit-timeline-instant-order.test.ts b/packages/metadata-protocol/src/protocol.commit-timeline-instant-order.test.ts index 736716d74a..a26dea3085 100644 --- a/packages/metadata-protocol/src/protocol.commit-timeline-instant-order.test.ts +++ b/packages/metadata-protocol/src/protocol.commit-timeline-instant-order.test.ts @@ -8,10 +8,20 @@ // The defect // --------------------------------------------------------------------------- // `created_at` is an engine-injected audit column: it is not in `datetimeFields` -// and `SqlDriver#formatOutput` repairs it only inside `if (this.isSqlite)`, so -// the live SQL dialects hand it out of the record read door as a JS `Date` while -// the SQLite family hands out canonical ISO-Z text. Pinned one layer down by -// `packages/drivers/driver-sql/src/sql-driver-13567-audit-stamp-materialisation.test.ts`. +// and, when this landed, `SqlDriver#formatOutput` repaired it only inside +// `if (this.isSqlite)`, so the live SQL dialects handed it out of the record +// read door as a JS `Date` while the SQLite family handed out canonical ISO-Z +// text. #13973 ([ADR-0053 D-F1]) has since lifted both of `formatOutput`'s +// timestamp passes out of that gate — they run on EVERY dialect now — and the +// pin one layer down records that contract instead +// (`packages/drivers/driver-sql/src/sql-driver-13567-audit-stamp-materialisation.test.ts` +// §B1, inverted on purpose). +// +// The `Date` this file drives is still a shape `compareAuditInstants` receives, +// so what is pinned below stays a live comparator arm: `withPostgresCalendarDayAsText` +// is untouched by that ruling ([ADR-0053 D-F2]) so the CLIENT still materialises +// the column as a `Date`, `driver-sql` hands an INVALID `Date` through unchanged +// ([ADR-0053 D-F3]), and non-SQL drivers materialise their own. // // Both timeline consumers in `protocol.ts` compared `String(created_at)`: // diff --git a/packages/metadata-protocol/src/sys-metadata-repository-13997-authored-at-canonicalisation.test.ts b/packages/metadata-protocol/src/sys-metadata-repository-13997-authored-at-canonicalisation.test.ts index d0d9a3e5af..6ffd385592 100644 --- a/packages/metadata-protocol/src/sys-metadata-repository-13997-authored-at-canonicalisation.test.ts +++ b/packages/metadata-protocol/src/sys-metadata-repository-13997-authored-at-canonicalisation.test.ts @@ -17,12 +17,22 @@ * - `rowToItem()` (reached by `get()`) — `updated_at` / `created_at`, the * BUILTIN audit columns. * - * On Postgres and MySQL both arrive out of the record read door as a JS - * `Date`: `SqlDriver#formatOutput` repairs the audit columns and folds - * declared `datetime` columns only inside its `if (this.isSqlite)` arm, and - * `withPostgresCalendarDayAsText` leaves `timestamptz` / `timestamp` - * deliberately untouched. That dialect fact is pinned live in - * `packages/drivers/driver-sql/src/sql-driver-13567-audit-stamp-materialisation.test.ts`. + * When this landed, both arrived out of the record read door as a JS `Date` on + * Postgres and MySQL: `SqlDriver#formatOutput` repaired the audit columns and + * folded declared `datetime` columns only inside its `if (this.isSqlite)` arm. + * #13973 ([ADR-0053 D-F1]) has since lifted both passes out of that gate — they + * run on EVERY dialect — and the pin that recorded the asymmetry records the + * canonical-text contract instead + * (`packages/drivers/driver-sql/src/sql-driver-13567-audit-stamp-materialisation.test.ts` + * §B1, inverted on purpose). + * + * ⚠️ `withPostgresCalendarDayAsText` is untouched by that ruling + * ([ADR-0053 D-F2]) and still leaves `timestamptz` / `timestamp` deliberately + * alone — the CLIENT still hands back a `Date`; what changed is that the driver + * folds it at its own read boundary. The `Date` this file plants therefore + * still reaches both adapters: `driver-sql` hands an INVALID `Date` through + * unchanged ([ADR-0053 D-F3]) and non-SQL drivers materialise their own, so + * what is pinned below is a live adapter arm rather than a historical one. * * ## Why nothing reported it, and what that costs THIS file * diff --git a/packages/metadata/src/loaders/database-loader.test.ts b/packages/metadata/src/loaders/database-loader.test.ts index bf04ddbc52..8f7e58f950 100644 --- a/packages/metadata/src/loaders/database-loader.test.ts +++ b/packages/metadata/src/loaders/database-loader.test.ts @@ -1514,10 +1514,20 @@ describe('MetadataManager auto-configuration', () => { * (`packages/spec/src/system/metadata-persistence.zod.ts`) — stricter than the * sibling `MetadataItem.authoredAt`, which is a bare `z.string()`. `stat()` * built it from `record.updatedAt ?? record.createdAt`, and `created_at` / - * `updated_at` are BUILTIN audit columns: not in `datetimeFields`, and - * `SqlDriver#formatOutput` repairs them only inside its `if (this.isSqlite)` - * arm. On Postgres and MySQL they arrive as a JS `Date`, pinned live in - * `packages/drivers/driver-sql/src/sql-driver-13567-audit-stamp-materialisation.test.ts`. + * `updated_at` are BUILTIN audit columns: not in `datetimeFields`, and when + * this landed `SqlDriver#formatOutput` repaired them only inside its + * `if (this.isSqlite)` arm, so on Postgres and MySQL they arrived as a JS + * `Date`. #13973 ([ADR-0053 D-F1]) has since lifted both of `formatOutput`'s + * timestamp passes out of that gate — they run on EVERY dialect — and the pin + * that recorded the asymmetry records the canonical-text contract instead + * (`packages/drivers/driver-sql/src/sql-driver-13567-audit-stamp-materialisation.test.ts` + * §B1, inverted on purpose). + * + * ⚠️ The `Date` these cases drive is still a shape `stat()` receives, so they + * pin a LIVE arm: `withPostgresCalendarDayAsText` is untouched by that ruling + * ([ADR-0053 D-F2]) so the CLIENT still materialises the column as a `Date`, + * `driver-sql` hands an INVALID `Date` through unchanged ([ADR-0053 D-F3]), + * and non-SQL drivers materialise their own. * * ⚠️ `rowToRecord` reaches `createdAt` / `updatedAt` through an unchecked * `row.created_at as string | undefined` cast, so the `string` in diff --git a/packages/metadata/src/migrations/migrate-sys-notification-to-event.ts b/packages/metadata/src/migrations/migrate-sys-notification-to-event.ts index 7fe32d2d6b..dc21ee6737 100644 --- a/packages/metadata/src/migrations/migrate-sys-notification-to-event.ts +++ b/packages/metadata/src/migrations/migrate-sys-notification-to-event.ts @@ -491,20 +491,32 @@ async function recordNotificationEventReceipt( * `formatOutput`, so none of its repairs apply here on any dialect: * * - `created_at` is a BUILTIN audit column, so it is never in `datetimeFields` - * and no declared-field coercion reaches it; `formatOutput` repairs it only - * inside its `if (this.isSqlite)` arm (`repairNaiveUtcAuditTimestamp` over - * `AUDIT_TIMESTAMP_COLUMNS`). + * and no declared-field coercion reaches it; what repairs it is + * `formatOutput`'s own `AUDIT_TIMESTAMP_COLUMNS` pass + * (`repairNaiveUtcAuditTimestamp`) at the RECORD read door — a door this + * path does not go through. * - `read_at` is a LEGACY column ADR-0030 removed from the object, so it is * not declared either — it can never enter `datetimeFields`, and it is not * an audit column, so no arm of `formatOutput` could reach it even at the * record read door. * + * ⚠️ The reason no repair reaches this path is the SEAM, ⛔ not an + * `if (this.isSqlite)` gate inside `formatOutput`. Both of `formatOutput`'s + * timestamp passes — the `AUDIT_TIMESTAMP_COLUMNS` pass and the + * `normalizeSqliteDatetimeOutput` pass over `datetimeFields` — sat inside that + * arm until #13973 ([ADR-0053 D-F1]) lifted them out, and they run on EVERY + * dialect now. So the record read door presents canonical text everywhere while + * this raw-SQL door still hands back whatever the client materialised, which is + * why the divergence below survives the ruling HERE and nowhere upstream of it. + * * On SQLite both arrive as canonical ISO text and `String()` is the identity — * which is why every test in this directory stayed green. On Postgres and * MySQL an instant column materialises as a JS `Date` - * (`withPostgresCalendarDayAsText` leaves the instant types alone deliberately; - * pinned in `sql-driver-13567-audit-stamp-materialisation.test.ts`), and - * `String(date)` spells + * (`withPostgresCalendarDayAsText` leaves the instant types alone deliberately, + * [ADR-0053 D-F2]; pinned in + * `sql-driver-13567-audit-stamp-materialisation.test.ts` §B3, which reads the + * same row raw through knex and still gets the dialect's `Date` on the live + * cells), and `String(date)` spells * * Sun Aug 30 2026 18:19:25 GMT+0800 (China Standard Time) * diff --git a/packages/services/service-storage/src/stranded-orphan-inventory.test.ts b/packages/services/service-storage/src/stranded-orphan-inventory.test.ts index 95d9539972..923dde656d 100644 --- a/packages/services/service-storage/src/stranded-orphan-inventory.test.ts +++ b/packages/services/service-storage/src/stranded-orphan-inventory.test.ts @@ -513,11 +513,23 @@ describe("[#10950] the sweep cannot nominate a stranded orphan — the card's pr * ## Why this file could not have caught the defect before * * `created_at` is a BUILTIN audit column, so no declared-field coercion - * reaches it and `SqlDriver#formatOutput` repairs it only inside its - * `if (this.isSqlite)` arm. The record read door therefore hands it back as - * canonical ISO-Z TEXT on SQLite and as a JS `Date` on Postgres and MySQL — - * pinned at that door, per dialect, in driver-sql's - * `sql-driver-13567-audit-stamp-materialisation.test.ts` (§B0/§B1). + * reaches it and `SqlDriver#formatOutput` USED TO repair it only inside its + * `if (this.isSqlite)` arm. While that gate stood, the record read door handed + * it back as canonical ISO-Z TEXT on SQLite and as a JS `Date` on Postgres and + * MySQL. #13973 ([ADR-0053 D-F1]) has since lifted both of `formatOutput`'s + * timestamp passes out of that gate — they run on EVERY dialect now — and the + * pin that recorded the asymmetry records the canonical-text contract instead + * (driver-sql's `sql-driver-13567-audit-stamp-materialisation.test.ts` + * §B0/§B1, inverted on purpose). + * + * ⚠️ The `Date` these cases drive is still a shape `usableCreatedAt` receives, + * so they pin a LIVE arm rather than a historical one: `driver-sql` hands an + * INVALID `Date` through unchanged ([ADR-0053 D-F3] — the one shape with no + * canonical text) and a non-SQL driver materialises its own. + * `withPostgresCalendarDayAsText` is untouched by that ruling + * ([ADR-0053 D-F2]), so the CLIENT still materialises `timestamptz` / + * `DATETIME(3)` as a `Date`; what changed is that the driver folds it at its + * own read boundary. * * ⚠️ This repo's default test backend is SQLite, and every fixture above * spells `created_at` as an ISO STRING — the one shape the old diff --git a/packages/services/service-storage/src/stranded-orphan-inventory.ts b/packages/services/service-storage/src/stranded-orphan-inventory.ts index e9837b32ba..c146624f66 100644 --- a/packages/services/service-storage/src/stranded-orphan-inventory.ts +++ b/packages/services/service-storage/src/stranded-orphan-inventory.ts @@ -175,28 +175,48 @@ function usableSize(value: unknown): number | undefined { * ## Why a `typeof v === 'string'` test alone is wrong here * * `created_at` is a BUILTIN audit column: it is not in `datetimeFields`, so no - * declared-field coercion reaches it, and `SqlDriver#formatOutput` repairs the - * audit columns only inside its `if (this.isSqlite)` arm. So the read door the - * walk below goes through hands this value back as canonical ISO-Z TEXT on - * SQLite and as a JS `Date` on Postgres and MySQL — the production default - * drivers. Pinned per dialect, at that door, in driver-sql's - * `sql-driver-13567-audit-stamp-materialisation.test.ts` (§B1). + * declared-field coercion reaches it, and `SqlDriver#formatOutput` USED TO + * repair the audit columns only inside its `if (this.isSqlite)` arm. While that + * gate stood, the read door the walk below goes through handed this value back + * as canonical ISO-Z TEXT on SQLite and as a JS `Date` on Postgres and MySQL — + * the production default drivers — and that asymmetry is what this guard was + * written against. #13973 ([ADR-0053 D-F1]) has since lifted BOTH of + * `formatOutput`'s timestamp passes out of that gate — the + * `AUDIT_TIMESTAMP_COLUMNS` pass and the `normalizeSqliteDatetimeOutput` pass + * over `datetimeFields` — so they run on EVERY dialect and the read door + * presents the canonical text. The pin that recorded the asymmetry records the + * new contract instead: driver-sql's + * `sql-driver-13567-audit-stamp-materialisation.test.ts` §B1, inverted on + * purpose. * - * A bare string test therefore answers FALSE for EVERY row on the live - * dialects: a field the walk explicitly projects (`fields: [… 'created_at']`) + * A bare string test answered FALSE for EVERY row on the live dialects while + * that gate stood: a field the walk explicitly projects (`fields: [… 'created_at']`) * was asked for from the driver and then silently discarded, so every sample * in the operator's report carried `createdAt: undefined` there while looking * correct on the SQLite the tests run. The sibling guards on `key` and `name` * are NOT this — those are text columns on every dialect. Only the timestamp - * straddles the divergence, which is why reading the code did not show it. + * straddled the divergence, which is why reading the code did not show it. + * + * ## Why the `Date` arm is still live after that ruling + * + * ⚠️ The `Date` domain did not close, so under [ADR-0053 D-F1]'s B1 ruling this + * consumer arm became a NO-OP for the valid-`Date` case, ⛔ never dead code and + * ⛔ never a conflict with the driver-side fold. `driver-sql` still hands an + * INVALID `Date` through unchanged — the one shape with no canonical text to + * fold to ([ADR-0053 D-F3], `isoFromValidDate`), measured reachable on both + * live dialects — and a non-SQL driver materialises its own (`driver-mongodb` + * stamps `new Date()` and BSON round-trips it). * * ## Why the consumer owes the canonical spelling * - * Normalising at the driver's read door instead would reverse the deliberate - * `withPostgresCalendarDayAsText` decision — that a `timestamptz` IS an - * instant and a `Date` is the right materialisation for it. So the repair is - * the one `@objectstack/metadata-protocol` already carries for `occurred_at`: - * accept both shapes where the value is consumed. + * ⚠️ `withPostgresCalendarDayAsText` is UNCHANGED by that ruling + * ([ADR-0053 D-F2]): a `timestamptz` IS an instant, a `Date` is the right + * materialisation for it at the CLIENT parser, and the driver still leaves that + * parser alone. What #13973 moved is WHERE the fold happens — at the driver's + * own read boundary, not at the parser — so folding there never reversed the + * D-F2 decision. Accepting both shapes where the value is consumed, the repair + * `@objectstack/metadata-protocol` already carries for `occurred_at`, is what + * covers the residue above. * * ⛔ NOT `row.created_at ?? undefined`. That reads as fixed and is worse: it * puts a raw `Date` into a field declared `string | undefined`, trading a