You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
record validator: a time field written "+010000-01-01T10:00:00Z" is stored verbatim (201 on SQLite, 500 on PostgreSQL), and "10:00Z" reads back differently per backend — the write-side twin of #20480 #20671
Filing gate: ① a product defect with a measured reach:. Finding class (a). reach: is POST /api/v1/data/:object, measured by the #20549 / #20480 dev through the REST create handler on PR #20668's head 0adb1bf84, under TZ=America/New_York, on SQLite and a live PostgreSQL 16 (os-dev-report on #20549, out_of_scope_findings[0]). The readings are the dev's; the seat re-read the named code on origin/main, not the runs.
Filed by the domain:engine execution seat 1 (session_01DEvba2nBuD4tWzfq8r8NFY, os-support-ai). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim. Reader: triage, then this lane's seat (the fix lands in packages/objectql).
What happens
written time value
SQLite
PostgreSQL 16
InMemoryDriver (engine.insert)
"+010000-01-01T10:00:00Z"
201, reads back "+010000-01-01T10:00:00Z" verbatim
500 DATABASE_ERROR
stored verbatim
"10:00Z"
201, reads back "10:00Z"
201, reads back "10:00:00"
stored verbatim
Why
The record validator's time arm (packages/objectql/src/validation/record-validator.ts, the time-of-day block):
tests hasDate with an unanchored /\d{4}-\d{2}-\d{2}/, which matches inside "+010000-01-01", so an extended-year instant passes as "has a date";
its time-of-day pattern admits a Z / offset suffix that the time storage rule (core temporalStorageForm(…, 'time'), canonicalTimeOfDay) does not read, so the suffixed value reaches the driver as written.
So the time WRITE door is wider than the time COMPARAND door that #20480 (PR #20668) closes: a filter refuses a value that a write stores.
Why it is its own card
PR #20668 folds #20549 and #20480, which are both comparand-door cards. The dev did not fix this in place: matching core's rule on the write side would also refuse offset wall clocks ("10:00+08:00"), a narrowing no triage ruling pins. That fails the in-place condition "a mechanical fix whose shape is already pinned".
triage decides whether an offset wall clock is read (converted to UTC) or refused;
pins on memory, SQLite and PostgreSQL, with a plain "10:00:00" control.
Serial after PR #20668, which moves the predicates this would ask.
Dedupe
search_issues in objectstack-ai/objectstack, open and closed, with a control that hits (#20549 on "temporal comparand door impossible day rolled over"):
"time field write stores extended year verbatim record validator time arm offset suffix 10:00Z stored verbatim" gives 0 hits;
Dedupe words: time write door extended year stored verbatim · record-validator time arm hasDate unanchored · time field 10:00Z stored verbatim sqlite postgres differ
Filing gate: ① a product defect with a measured
reach:. Finding class (a).reach:isPOST /api/v1/data/:object, measured by the #20549 / #20480 dev through the REST create handler on PR #20668's head0adb1bf84, underTZ=America/New_York, on SQLite and a live PostgreSQL 16 (os-dev-reporton #20549,out_of_scope_findings[0]). The readings are the dev's; the seat re-read the named code onorigin/main, not the runs.Filed by the
domain:engineexecution seat 1 (session_01DEvba2nBuD4tWzfq8r8NFY,os-support-ai). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim. Reader: triage, then this lane's seat (the fix lands inpackages/objectql).What happens
timevalueengine.insert)"+010000-01-01T10:00:00Z""+010000-01-01T10:00:00Z"verbatimDATABASE_ERROR"10:00Z""10:00Z""10:00:00"Why
The record validator's
timearm (packages/objectql/src/validation/record-validator.ts, the time-of-day block):hasDatewith an unanchored/\d{4}-\d{2}-\d{2}/, which matches inside"+010000-01-01", so an extended-year instant passes as "has a date";Z/ offset suffix that thetimestorage rule (coretemporalStorageForm(…, 'time'),canonicalTimeOfDay) does not read, so the suffixed value reaches the driver as written.So the
timeWRITE door is wider than thetimeCOMPARAND door that #20480 (PR #20668) closes: a filter refuses a value that a write stores.Why it is its own card
PR #20668 folds #20549 and #20480, which are both comparand-door cards. The dev did not fix this in place: matching core's rule on the write side would also refuse offset wall clocks (
"10:00+08:00"), a narrowing no triage ruling pins. That fails the in-place condition "a mechanical fix whose shape is already pinned".Suggested shape (⛔ not a ruling)
As #20525 did for
date/datetime:timewrite arm asks core's one rule (temporalStorageForm(…, 'time')keeping a time of day, or the comparand predicate core temporal rule: atimecomparand spelled with an extended-ISO year (+010000-01-01T10:00:00Z) is compared as text on memory and SQLite:$gtanswers 3 of 3 rows,$lt0, where the same wall clock as a 2026 instant answers 2 / 1 #20480 extended), and refuses what it cannot read withVALIDATION_FAILED/ 400 naming the field;"10:00:00"control.Serial after PR #20668, which moves the predicates this would ask.
Dedupe
search_issuesinobjectstack-ai/objectstack, open and closed, with a control that hits (#20549 on "temporal comparand door impossible day rolled over"):timecomparand spelled with an extended-ISO year (+010000-01-01T10:00:00Z) is compared as text on memory and SQLite:$gtanswers 3 of 3 rows,$lt0, where the same wall clock as a 2026 instant answers 2 / 1 #20480 is the comparand twin; temporal values outside the years a four-digit text or a backend holds: adatetimecomparand for year 10000 or −1 misorders on memory/SQLite and 500s on PostgreSQL; adatein year 0000 500s on PostgreSQL; adatewrite stores+010000-…verbatim #20264 coversdatewrites of+010000, closed; record validator: the temporal write arms trust Date.parse — date2026-02-30is stored verbatim (500 on PostgreSQL), datetime2026-02-30T10:00:00Zrolls over to March 2, and a non-ISO datetime is read in the host zone #20525 and record validator: adatefield written as a non-ISO string ("2026/07/15") answers 201 and is stored verbatim as"2026/07/15", a non-day, on memory and SQLite, because thedatearm admits anyDate.parse-readable string #20481 cover thedate/datetimewrite arms, closed; Field.time repeats the #3912 pattern: writes unnormalised, repaired only on read — window filters and ORDER BY are silently wrong on SQLite #3994 coverstimenormalisation on read, closed.None is the
timewrite arm.Dedupe words:
time write door extended year stored verbatim·record-validator time arm hasDate unanchored·time field 10:00Z stored verbatim sqlite postgres differGenerated by Claude Code