Skip to content

Three isoFromValidDate copies still stand beside the now-total shared canonical-ISO spelling — the collapse their docblocks promise is a decision #14078 did not make #16422

Description

@zhuangjianguo

Filed from the #14078 implementation seat while executing the maintainer ruling (comment 5504838971, option B). ⛔ Not addressed there, and that card does not close this one.

What #14078 did, and what it left standing

#14078 made the Date arm of the shared canonical-ISO spelling total on all five arms at once: canonicalIsoInstant in sys-metadata-repository.ts and in database-loader.ts, canonicalIsoStamp and formatCsvCell in rest-server.ts, and the occurredAt arm inside auditMetaItem in protocol.ts.

It did not touch a second, near-identical family that three separate cards landed beside those arms:

copy landed by call sites
packages/metadata-protocol/src/sys-metadata-repository.tsisoFromValidDate #14037 1 (rowToEvent)
packages/metadata/src/loaders/database-loader.tsisoFromValidDate #14037 4 (rowToRecord ×2, two history adapters)
packages/metadata-protocol/src/protocol.tsisoFromValidDate #14038 1 (listCommits)

Three copies, six call sites. All three docblocks say, verbatim: "When #14078 rules, this helper collapses into the shared spelling."

#14078 ruled, and the collapse did not happen. This card is that unclosed sentence, filed rather than performed, because the collapse is not the mechanical act the docblocks imply.

Why it is not mechanical — measured on origin/main 0ea5f9d9f

The two helpers differ in more than the Invalid-Date shape, and after #14078 that is the ONLY shape on which they now agree:

  • canonicalIsoInstant(value: unknown): string | undefined — rewrites the whole domain: nullish becomes undefined, a valid Date becomes ISO text, a string passes through, everything else becomes String(value).
  • isoFromValidDate(value: unknown): unknown — rewrites exactly one shape: a valid Date becomes ISO text, and every other input is returned untouched, null, a number and an opaque column included.

So swapping one for the other changes behaviour at six call sites for inputs nobody analysed during the #14078 ruling. Three specifics:

  1. MetadataHistoryRecord.recordedAt has no ruled-valid terminal value. It is a REQUIRED z.string().datetime() (packages/spec/src/system/metadata-persistence.zod.ts), fed at database-loader.ts from isoFromValidDate(row.recorded_at) as string in two places. Neither of the ruling's two answers validates there: the visible text "Invalid Date" fails the datetime() refinement, and undefined fails the required field. Today an Invalid Date object reaches it and also fails. That is a real defect, and it needs a third answer (a caller-side default, most likely) that only a decision can supply.
  2. protocol.ts's copy carries an explicit contrary contract. Its docblock states that callers of listCommits "are promised the RAW value back untouched when it is not a valid Date — an absent/opaque column must still reach sort's fallback branch and any in-process reader exactly as before". Collapsing it silently reverses that.
  3. Three in-repo NEUTRALITY pins assert the un-collapsed behaviour and are written to go red on exactly this swapsys-metadata-repository-14037-event-ts-canonicalisation.test.ts §C, database-loader-14037-adapter-boundary-iso.test.ts §D, and protocol-14038-list-commits-created-at-iso.test.ts §D. They exist so the swap has to be a deliberate act with its own evidence, which is what this card asks for.

Also worth deciding while the family is open

database-loader.ts rowToRecord assigns isoFromValidDate(row.created_at) as string | undefined into MetadataRecord.createdAt / .updatedAt, both z.string().datetime().optional(). An Invalid Date therefore arrives as a Date object wearing a string | undefined cast — the same class of unchecked assertion #14037 was filed to remove, surviving on the one input shape it deliberately did not decide.

Options

  • A — collapse, with a per-call-site terminal value, exactly as The shared canonical-ISO normaliser turns an Invalid Date from a driver into a 500, where String() served text #14078 did for its five arms: follow each of the six sites to its declared schema and its reader, and choose text / undefined / a caller default per site. Rewrites the three neutrality pins as ruled pins. Largest, and the one that actually ends the family.
  • B — collapse only where the two are already equivalent, and leave listCommits alone under its stated pass-through contract. Smaller, leaves two spellings in the repo but with a documented reason each.
  • C — keep both, permanently, and correct the three docblocks to describe two deliberate helpers rather than one pending merge. Cheapest; the sentence about collapsing is then simply retired.

⛔ No option removes the need to answer point 1 above — recordedAt is a required datetime() field with no valid terminal value today, whichever way the consolidation goes.

Re-check commands

git grep -n "isoFromValidDate" -- packages/metadata-protocol/src packages/metadata/src
git grep -n "recordedAt" -- packages/spec/src/system/metadata-persistence.zod.ts

Backlinks: #14078 (the ruling, and where the docblocks' promise was made), #14037 and #14038 (which landed the three copies). None are addressed here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions