docs(engine): correct seven out-of-package comments that still gate formatOutput's timestamp passes on isSqlite - #16817
Conversation
…t's timestamp passes Since #13973 / PR #16619 (ADR-0053 D-F1) both of `SqlDriver#formatOutput`'s timestamp passes -- the AUDIT_TIMESTAMP_COLUMNS pass and the normalizeSqliteDatetimeOutput pass over datetimeFields -- run on every dialect. Seven out-of-package comments still described them as gated on `if (this.isSqlite)`. The rest-server.ts one was not merely stale: it warned future authors that "a declared Field.datetime is therefore NOT protected on Postgres/MySQL", which invites the tolerant consumer-side coercion ADR-0053 / #16619 expressly forbid. Corrected first, per the triage ruling on the card. Both carve-outs are preserved rather than flattened: - withPostgresCalendarDayAsText is untouched (D-F2) -- the client still hands back a Date; only "nothing folds it" was false. - the Invalid `Date` residue still stands (D-F3) -- no sentence claims the read door never hands out a Date. Comments only; no runtime behaviour changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
…e changes
Measured, not assumed: the amended comment text reaches
packages/rest/dist/index.{js,cjs} and packages/metadata-protocol/dist/index.{js,cjs}
verbatim, so those two publish changed bytes. @objectstack/metadata is deliberately
not named -- its edits are all JSDoc blocks, which its bundle strips.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
📓 Docs Drift CheckThis PR changes 3 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin f4e42e59934eae1ce965d732ccdc874451fc8041 && git checkout f4e42e59934eae1ce965d732ccdc874451fc8041
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 30b0990787ef633cfff724951ceeee08c4fc05b9 6e6f67d88447bd897c7d5f221c83f21a768053a3 && git checkout -B drift-repro 30b0990787ef633cfff724951ceeee08c4fc05b9 && git merge --no-ff 6e6f67d88447bd897c7d5f221c83f21a768053a3
node scripts/docs-audit/affected-docs.mjs --json 30b0990787ef633cfff724951ceeee08c4fc05b9
|
Fixes #16728
Comment prose only. Not one line of runtime code moves, in any package.
What changed underneath the prose
PR #16619 (card #13973, ADR-0053 D-F1) made both of
SqlDriver#formatOutput's timestamppasses unconditional on every dialect. Verified by reading
formatOutputon the currenttree, not by grepping it: the
if (this.isSqlite)arm opens at the JSON codec, closes afterthe numeric-scalar repair, and both the
AUDIT_TIMESTAMP_COLUMNSpass and thenormalizeSqliteDatetimeOutputpass overdatetimeFieldssit at top level below it. Sevenout-of-package comments still described them as gated on that arm.
packages/rest/src/rest-server.tswas repaired first, per the triage ruling on the card:it is not stale description but a warning aimed at a future author — "A declared
Field.datetimeis therefore NOT protected on Postgres/MySQL" — and the action it invites(a tolerant coercion at the consumer) is what ADR-0053 and that card expressly forbid. A
paragraph was arguing for the reverse of its own governing ruling.
Deliverable 1 — re-measurement, beside triage's numbers
Triage measured at
origin/main5e53d73d. This branch measured at56ee7aef1d, afterPR #16716 (#16609) and PR #16710 (#16570) landed on this surface.
git grep -n normalizeSqliteDatetimeOutput -- 'packages/**'— 18, distribution identicalto triage's 10 + 1 + 7:
driver-sqlhomesql-driver.ts7 (the firing control),sql-driver-timestamp-format.test.ts1,sql-driver-13973-canonical-iso-read-door.test.ts1, itsCHANGELOG.md1packages/rest/CHANGELOG.md— untouchedThe firing control holds:
sql-driver.tsstill answers 7, so the query hits where it canhit and the out-of-package 7 is a real reading, not a dead query. Nothing had been repaired
by anyone else, and no site had moved into
packages/metadata-protocol/src/protocol.ts(heldby PR #16796 — untouched here, confirmed by file list). Census is byte-stable after the fix:
still 18, same distribution,
driver-sqland both CHANGELOGs untouched.The premise is WIDER than the card, in one direction the symbol query cannot see
The census keys on
normalizeSqliteDatetimeOutput. Prose that asserts the same false gatingwithout naming that symbol is invisible to it. Sweeping the symbol that cannot be split by
line-wrapping either —
if (this.isSqlite)— finds 11 further occurrences in 10 more files,same defect class, all false since PR #16619, all outside this card's declared file surface:
packages/cli/src/commands/migrate/duplicates.ts·duplicates.created-at-canonical.test.ts·packages/metadata-protocol/src/protocol.ts(x2, held by PR #16796) ·protocol-14038-list-commits-created-at-iso.test.ts·protocol.commit-timeline-instant-order.test.ts·sys-metadata-repository-13997-authored-at-canonicalisation.test.ts·packages/metadata/src/loaders/database-loader.test.ts·packages/metadata/src/migrations/migrate-sys-notification-to-event.ts·packages/services/service-storage/src/stranded-orphan-inventory.ts· its.test.tsNot repaired here — outside the declared file surface, and
protocol.tsis another PR's. Filedseparately; see the acceptance notes.
Three of the seven files also carried a second occurrence of the same false claim, invisible
to the symbol census for the same reason. Those are in-file and in scope, so the diff rewrites
11 sentences across the 7 declared files, not 7.
The seven sites — before to after
Each was verified by reading, and each rewrite states the same three facts: the passes are
unconditional since #13973 / ADR-0053 D-F1;
withPostgresCalendarDayAsTextis untouched(D-F2); the Invalid
Dateresidue stands (D-F3).packages/rest/src/rest-server.ts(the ruled-first one) — "both sit INSIDE itsif (this.isSqlite)arm, so neither runs here. A declaredField.datetimeis therefore NOTprotected on Postgres/MySQL" to: both passes run on every dialect, the driver folds the
client's
Dateat its own read boundary, and exactly oneDateshape still arrives — anInvalid
Date, which keeps the arm live rather than dead. The sibling bullet claiming thecanonical-text arm is SQLite-only was corrected with it; leaving it would have contradicted
the new text in the same docblock.
packages/rest/src/import-job-dto-timestamp-canonical.test.ts— the defect narrative movedto past tense and gained the reason these cases are still live (mongo
Date, InvalidDate);the "deliberately NOT claimed" section no longer cites
formatOutput's "isSqlitebracketing"as the current dialect fact.
packages/metadata/src/loaders/database-loader.ts(x2 — thecanonicalIsoInstantdocblockand the Five more adapter-boundary sites cast a driver
Dateinto a declared ISO-string timestamp —MetadataEvent.ts,MetadataHistoryRecord.recordedAt,MetadataRecord.createdAt/updatedAt#14037 one) — both now say the pass was lifted out of the gate, and that thisproducer-side arm became a no-op, not a conflict, under the B1 ruling.
packages/metadata/src/loaders/database-loader-14037-adapter-boundary-iso.test.ts— same,plus why the planted
Dateis still a shape a driver produces.packages/metadata-protocol/src/sys-metadata-repository.ts(x3 — thecanonicalIsoInstantdocblock, the
rowToEventone, and thelistDraftsbody comment). The first also quoted aclause from
withPostgresCalendarDayAsText— thatField.datetime"depends on" theDatematerialisation — which [finding] Sweep: which consumers compare or format a value whose runtime type differs between the Date-materialising drivers and the ISO-text ones #13973 checked against the tree, found did not hold, and removed; the
comment was quoting deleted prose, so the rewrite says so. The
listDraftscomment cited"[finding] Sweep: which consumers compare or format a value whose runtime type differs between the Date-materialising drivers and the ISO-text ones #13973's two standing prohibitions"; one of the two (no unilateral reshape at the driver's
read door) was discharged by the B1 ruling, which made that reshape the central fix. The
surviving prohibition — no tolerant
??at a consumer — is kept and marked as the one that stands.packages/metadata-protocol/src/sys-metadata-repository-14037-event-ts-canonicalisation.test.tspackages/metadata-protocol/src/sys-metadata-repository-14938-list-drafts-updated-at.test.ts— its section heading "Why the value is a
Dateon the live dialects" was itself the falseclaim, so the heading changed too.
Five of the seven cited
sql-driver-13567-audit-stamp-materialisation.test.tsas pinning "bothcolumn classes arrive as a
Date". Reading that fossil first, as the standing instructionrequires: its own header says §B "now pins that contract" and "is therefore inverted on purpose".
Those citations were doubly false — the claim, and the pin invoked for it. Each now points at the
inverted §B and says what it pins today. The fossil agrees with this direction; no conflict to report.
Both un-flattenable items survive — verified mechanically
withPostgresCalendarDayAsTextis still untouched (D-F2). All six sites that mentioned itstill do (the seventh never did); every one keeps "the client hands back a
Date" as trueand corrects only "nothing folds it".
driver-sqlis not in this diff at all.Dateresidue stands (D-F3). All seven files carry the carve-out explicitly.Repo-wide grep for the overstatement PR feat(driver-sql): the read door presents datetime values and audit stamps as canonical ISO-Z text on every dialect (#13973, B1 narrow) #16619's own contract review returned a finding for —
any claim that the read door never hands out a
Date— matches zero lines.Changeset route — decided on a measurement, not on size
Governing text:
.github/workflows/pr-automation.yml, the WHICH LEVEL block (lines ~660-690).Text rejected: route 2, the
skip-changesetlabel. Its scope is "it releases nothing", andAGENTS.md:1028sets the floor — that label is for "a diff that publishes nothing from anyreleased package". Measured against built output, that is false here:
.d.tsof all three packages: no amended text reaches one — zero occurrences ofisSqlite,D-F3or any amended phrase in anydist/*.d.ts/.d.cts. No public surface moves.packages/rest/dist/index.jsand.cjs, andpackages/metadata-protocol/dist/index.{js,cjs}:carry the amended comment text verbatim.
D-F3occurs zero times in either package'ssrcat the merge base and appears in both bundles after the change, so the published bytes do change.
packages/metadata/dist: JSDoc blocks are stripped by its bundle (control: 288//lines survive,the touched block comments do not appear). Its published output is unchanged, so it is deliberately
not named in the changeset.
Route taken: route 1, a
patchchangeset naming@objectstack/metadata-protocoland@objectstack/rest.patchand notminorbecause the WHICH LEVEL block reservesminorfor apurely additive widening of a published public surface, and nothing here widens one.
Verification
Gate coverage, reported verbatim from
node scripts/pm/dispatch-gates.mjs --ran ... --repo objectstack-ai/objectstack:That line accounts for coverage, not verdicts. Verdicts, stated separately:
pnpm check:dual-build-cjs-loads("this gate reads built output, and some package has no dist/…This is NOT a pass: nothing was measured") and
pnpm check:type-check-debt("--re-measure cannotrun: 6 workspace dependenc(ies) … have no built type entry point on disk"). Both need a
whole-workspace build, which is CI's run, not this branch's. Declared narrowing, not a skip.
Tests and typecheck, at
6e6f67d884, all underscripts/pm/os-verify-lock.sh(VERDICT command-exit 0):@objectstack/resttsc --noEmit+check:test-typecheck(0 files / 0 errors)@objectstack/metadata-protocoltsc --noEmitDone@objectstack/metadatatsc --noEmitDoneDependency closure built first (
pnpm --filter 'PKG^...' build, each package's upstream closure),then the three packages themselves, so the
distmeasurement above reads real emitted bytes ratherthan a stale tree. No ablation: this diff adds no guard and moves none, so there is nothing whose
failure could be demonstrated by reverting it.
验收备注
if (this.isSqlite)prose across 10 files inpackages/cli,packages/metadata-protocol,packages/metadataandpackages/services/service-storage, invisible to this card's symbolcensus. Outside the declared file surface, and one of the files belongs to PR fix(metadata-protocol): stop persisting derived provenance keys, and restate tenant authorship at hydration for every type #16796, so not
repaired here.
defect class is keyed on a phrase. The symbol was the right choice against line-wrapping, but it
under-counts the class by roughly a factor of two — worth knowing for the next propagation card.
packages/rest/CHANGELOG.mdandpackages/drivers/driver-sql/CHANGELOG.md(history); the five ruled-B The shared canonical-ISO normaliser turns an InvalidDatefrom a driver into a 500, whereString()served text #14078 consumer arms andthe seven service-queue: the publish idempotency window never expires on Postgres/MySQL —
String(row.created_at) >= windowStartcompares aDate.toString()against ISO text #13993-os migrate duplicatesreports each holder'screatedAtas aDate.toString()spelling on Postgres/MySQL #13999 per-site normalisations (no-ops, not conflicts, under B1 — retiring themis separate deliberate work);
content/docs/releases/; ADR-0053 itself (card docs(adr-0053): D-F1 saysfindWithWindowFunctionsapplies no read presentation — false once #16716 merges (governed, docs-only) #16782's work);packages/metadata-protocol/src/protocol.ts(PR fix(metadata-protocol): stop persisting derived provenance keys, and restate tenant authorship at hydration for every type #16796);packages/drivers/*,packages/objectql/src/plugin.tsandpackages/platform-objects/src/apps/translations/(sibling claims [finding]
SqlDriverreads keys off caller objects through(obj as any)at 7 sites while 3 parameter types declare none of them — a class, not a third coincidence (after #4311tenancy, #16570indexes) #16711, The audit binder launders a caller-supplied created_by on an ordinary create, the same way #15964 does created_at #16311, platform-objects(i18n): es-ES and ja-JP carry the SAME source-parity defect as zh-CN at nine of the 26 enumerated metadata-form leaves — the "zh-CN is alone" reading holds only at the two leaves it was measured on #16227).packages/**plus one.changeset/entry.Clause-②: no — comment prose only, no exported symbol, no key on a published payload. The
measurement above agrees with the seat's declaration: zero amended text reaches any
dist/*.d.ts.Generated by Claude Code