Recording a coverage gap found while fixing #13382. Not a defect in current behaviour — #13382's own repair is landed with pins that fail loudly. This is about which backends those pins ever run against, which is a CI-topology decision rather than a bug fix, so it is filed instead of expanded into that PR.
The record-data optimistic-concurrency gate (assertVersionOf / assertVersionMatch in packages/metadata-protocol/src/protocol.ts) compared the record's updated_at against the caller's token with String(v) on both sides. On Postgres — the production default driver — updated_at arrives as a JS Date, so the comparison ran against Sun Aug 30 2026 18:19:25 GMT+0800 (China Standard Time) while the client echoed back the 2026-08-30T10:19:25.947Z its own GET had served. Every guarded save conflicted, on records nobody had touched.
Why it survived, stated as a coverage fact
Every existing OCC pin drives ISO text on both sides of the comparison — a memory or mocked engine, or SQLite, all of which round-trip canonical ISO strings. Those pins were green the entire time the production default driver refused every write. The discriminating input is the driver's Date, and nothing in the repo ever produced it into this seam.
The gap that is still open after the fix
Temporal Conformance (live PG + MySQL) — the required job where a real Postgres exists — runs pnpm --filter @objectstack/driver-sql test. The OCC seam lives in @objectstack/metadata-protocol, which has no driver dependency and must not grow one (the layering runs the other way). So:
- the seam's new regression suite pins the behaviour given each input shape, using a hand-made
Date — which is correct and sufficient to catch a revert of the repair;
- but the composed fact that made it a production bug — this driver, against this seam, hands over a
Date — is asserted by nothing. It was measured by hand against a real PostgreSQL 16 while fixing the card, and that measurement is not something the repo carries.
The class is the one AGENTS.md already names elsewhere: a green suite that is not a suite, on the path a double was introduced for.
Options, for whoever triages this
- A driver-side characterisation pin in
driver-sql. Assert, under the live matrix, that a row's updated_at read back on Postgres/MySQL is a Date whose String() loses milliseconds and carries the process zone. Cheapest by a wide margin, lands in a package already inside the job, and pins the exact fact the seam's assumption was wrong about. It pins a decision the driver states deliberately (withPostgresCalendarDayAsText: "timestamptz / timestamp are deliberately untouched: those are instants, a Date is the right materialisation for them"), so it is not over-pinning.
- An end-to-end OCC cell in the live job, from a package that legitimately depends on both sides (
packages/rest and packages/runtime both do). Highest fidelity, but it widens a required job's package set, which is why this is a decision and not a patch.
- Accept the gap and rely on the seam-level pins. Defensible; worth recording as a decision rather than leaving it as an accident.
Recommendation: option 1, and only option 1 unless someone wants the wider cell for its own reasons.
Wider question this raises, deliberately not answered here
updated_at is not the only value whose runtime type differs between the Date-returning drivers (Postgres, MySQL, MongoDB) and the ISO-text ones (the SQLite family, memory). Whether other consumers compare or format it while only ever being tested on the text side is a sweep, not a fix, and would be its own card.
Recording a coverage gap found while fixing #13382. Not a defect in current behaviour — #13382's own repair is landed with pins that fail loudly. This is about which backends those pins ever run against, which is a CI-topology decision rather than a bug fix, so it is filed instead of expanded into that PR.
What #13382 was
The record-data optimistic-concurrency gate (
assertVersionOf/assertVersionMatchinpackages/metadata-protocol/src/protocol.ts) compared the record'supdated_atagainst the caller's token withString(v)on both sides. On Postgres — the production default driver —updated_atarrives as a JSDate, so the comparison ran againstSun Aug 30 2026 18:19:25 GMT+0800 (China Standard Time)while the client echoed back the2026-08-30T10:19:25.947Zits own GET had served. Every guarded save conflicted, on records nobody had touched.Why it survived, stated as a coverage fact
Every existing OCC pin drives ISO text on both sides of the comparison — a memory or mocked engine, or SQLite, all of which round-trip canonical ISO strings. Those pins were green the entire time the production default driver refused every write. The discriminating input is the driver's
Date, and nothing in the repo ever produced it into this seam.The gap that is still open after the fix
Temporal Conformance (live PG + MySQL)— the required job where a real Postgres exists — runspnpm --filter @objectstack/driver-sql test. The OCC seam lives in@objectstack/metadata-protocol, which has no driver dependency and must not grow one (the layering runs the other way). So:Date— which is correct and sufficient to catch a revert of the repair;Date— is asserted by nothing. It was measured by hand against a real PostgreSQL 16 while fixing the card, and that measurement is not something the repo carries.The class is the one AGENTS.md already names elsewhere: a green suite that is not a suite, on the path a double was introduced for.
Options, for whoever triages this
driver-sql. Assert, under the live matrix, that a row'supdated_atread back on Postgres/MySQL is aDatewhoseString()loses milliseconds and carries the process zone. Cheapest by a wide margin, lands in a package already inside the job, and pins the exact fact the seam's assumption was wrong about. It pins a decision the driver states deliberately (withPostgresCalendarDayAsText: "timestamptz/timestampare deliberately untouched: those are instants, aDateis the right materialisation for them"), so it is not over-pinning.packages/restandpackages/runtimeboth do). Highest fidelity, but it widens a required job's package set, which is why this is a decision and not a patch.Recommendation: option 1, and only option 1 unless someone wants the wider cell for its own reasons.
Wider question this raises, deliberately not answered here
updated_atis not the only value whose runtime type differs between theDate-returning drivers (Postgres, MySQL, MongoDB) and the ISO-text ones (the SQLite family, memory). Whether other consumers compare or format it while only ever being tested on the text side is a sweep, not a fix, and would be its own card.