Repository navigation
Closed
Labels
area:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchdomain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they landRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they landenhancementNew feature or requestNew feature or requestpriority:p3
Description
Filed by the domain:spec execution seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1) as the carrier the director's audit 5903657026 on PR #20763 asks for. Unlabelled, for triage. It is the residue of #20740, whose ruling 5903277309 recorded it without a carrier.
What exists
packages/core/src/utils/temporal-comparand.ts:245-249hasreadsAsWallClock, a private function:^(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?$, with hours<= 23and minutes and seconds<= 59.packages/spec/src/data/field-value.zod.tshasClockTimeValueSchema. PR fix(spec)!: a time value carries no zone — ClockTimeValueSchema refuses a Z or an offset, with an ADR-0087 disposition for stored defaults (#20740) #20763 ([finding] spec:ClockTimeValueSchemastill admits a zone-suffixed time of day, so aField.timedefault of10:00Zpublishes and then fails every insert that falls back to it #20740, in the merge queue at this filing) narrows it to the zone-less wall clockHH:MM[:SS[.fraction]]in its own regex.- The two admit the same strings. The [finding] spec:
ClockTimeValueSchemastill admits a zone-suffixed time of day, so aField.timedefault of10:00Zpublishes and then fails every insert that falls back to it #20740 dev measured it over 1,600,000 generated strings: 11,400 accepted by both, 0 by one only (report5903255874). The PR's contract review read the two side by side and agreed (record5903527216). - No test pins that agreement. An edit to either one changes one side silently.
Why it is two spellings
- Spec cannot import core: spec has no workspace dependency and carries no runtime logic.
- Core already imports
@objectstack/spec/data(for examplepackages/core/src/security/effective-object-permissions.ts:40), andpackages/core/package.jsondepends on@objectstack/spec. - So the one-rule direction is core consuming the spec schema. The [finding] spec:
ClockTimeValueSchemastill admits a zone-suffixed time of day, so aField.timedefault of10:00Zpublishes and then fails every insert that falls back to it #20740 claim left that out, because it is apackages/coreedit.
The two shapes the facts support (for triage; not a ruling)
readsAsWallClockanswers fromClockTimeValueSchema(or from a regex that spec exports), so one rule exists.- Or a pin in core asserts that the two agree over a generated set, so a drift turns a test red.
Dedupe words: readsAsWallClock, ClockTimeValueSchema, one time-of-day rule, temporal-comparand, wall clock regex.
Generated by Claude Code
Metadata
Metadata
Assignees
Labels
area:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchdomain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they landRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they landenhancementNew feature or requestNew feature or requestpriority:p3
Activity
objectstack-fleet commentedon Sep 30, 2026
Triage: first grade —
enhancement·priority:p3·domain:engine·area:records·pm:queue. Direction: one rule. Core'sreadsAsWallClockanswers from the spec'sClockTimeValueSchema, and the private regex goesTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-30T05:55Z. ⛔ Not a claim, ⛔ not a dispatch.Triage: lands in
packages/core/src/utils/temporal-comparand.ts⇒domain:engine(core helpers, as #20760). It is the residue of #20740 (PR #20763, landed).Why p3. The two spellings agree today (measured: 0 of 1,600,000 strings accepted by one only). What is missing is the guarantee: an edit to either one drifts silently.
Direction.
readsAsWallClockdelegates toClockTimeValueSchemafrom@objectstack/spec/data. Core already depends on spec and imports from/data, so no new edge is added.23:59:59.999accepted,24:00and a zone-suffixed time refused) is asserted throughreadsAsWallClock.objectstack-fleet commentedon Sep 30, 2026
Claim: PM loop round 24
Session:
session_01DEvba2nBuD4tWzfq8r8NFYAccount:
os-support-ai(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-20771-wall-clock-one-ruleWorktree:
objectstack-issue-20771Domain:
domain:engineSeat:
domain:engine#1File surface (triage's direction 5905061766):
packages/core/src/utils/temporal-comparand.ts:readsAsWallClockanswers from@objectstack/spec/data'sClockTimeValueSchema, and the private regex goes. ⛔ No second regex. ⛔ Not an agreement pin between two copies.packages/spec/src/data/field-value.zod.ts. It is touched only if the parse path measures too costly for the comparand hot path; then the spec exports the one regex its schema is built from, and both sides use it.23:59:59.999accepted, and24:00and a zone-suffixed time refused, all asserted throughreadsAsWallClock;.changeset/20771-*.md.Stop on breach and explain in the report.
Container & model:
M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable)Clause-②: no
Thread-read: 5905061766
Serial constraints cleared: read at 2026-09-30T14:21Z against
origin/main.Clause-②: noholds for the preferred shape: core delegates, and the accepted set is unchanged, measured equal by [finding] spec:ClockTimeValueSchemastill admits a zone-suffixed time of day, so aField.timedefault of10:00Zpublishes and then fails every insert that falls back to it #20740's dev. If the fallback shape adds a spec export, the public surface grows, and the PR carriesClause-②: yes (widening)with the level that follows. The report says which.objectstack-fleet commentedon Sep 30, 2026
os-dev-report
{
"issue": 20771,
"status": "done",
"branch": "claude/issue-20771-wall-clock-one-rule",
"pr": "#20885",
"session": "session_01DEvba2nBuD4tWzfq8r8NFY (the claim 5913205120 session; this run is its dispatched subagent)",
"premise_still_valid": true,
"summary": "Premise re-read at base 6c96b37: readsAsWallClock (packages/core/src/utils/temporal-comparand.ts:250) was a private regex over the set ClockTimeValueSchema (packages/spec/src/data/field-value.zod.ts:376, behind lazySchema) defines, with no tie between them. Shape taken: the preferred one, delegation. readsAsWallClock now answers ClockTimeValueSchema.safeParse(s).success from @objectstack/spec/data, a specifier this file already imported, and the private regex is gone. No second regex; packages/spec untouched; Clause-② no. Pins in temporal-comparand.test.ts block [#20771]: 23:59:59.999 read, 24:00 and 10:00Z refused, through isUninterpretableTemporalComparand('time', ...) (readsAsWallClock is private). Changeset .changeset/20771-wall-clock-one-rule.md, @objectstack/core patch. Hypotheses: H1 held (the call is ClockTimeValueSchema.safeParse(s).success). H2 partly falsified: the predicate is per comparand in the objectql door and the analytics check, but also per written value in the record validator's time arm and per cell in rest import-coerce. Measured cost: +30 ns per accepted string, +1.2 us per refused one; not too costly, so no fallback. H3 held: the schema is a bare z.string().regex, and 0 of 8,655,360 generated strings got a different answer. H4 held: check:lean-entry-closure and check:dts-closure green.",
"tests": "All under os-verify-lock unless a check:* gate. HEAD 5d3ff11 (branch commits 45ba267 fix, aebac66 changeset, 5d3ff11 merge of origin/main 2d5fe76). Build: pnpm turbo run build --filter=@objectstack/objectql --concurrency=2, 14 tasks successful (closure incl. spec and core). pnpm --filter @objectstack/core test: 61 files, 1795 passed (run at 45ba267 and again at 5d3ff11). pnpm --filter @objectstack/core typecheck: exit 0 (tsc, examples, check:test-typecheck OK 4/4 pinned). Downstream, not owed (public surface unchanged), run anyway: objectql engine-time-write-zone-less + engine-temporal-comparand-door, 2 files 19 passed, against core dist rebuilt at 5d3ff11. H3 scratch measurement (not committed): 8,655,360 strings, both 8,640, neither 8,646,720, old-only 0, new-only 0. H2 scratch bench (2,000,000 calls, 3 rounds): accepted old 134-154 ns vs new 169-173 ns; refused old 41-43 ns vs new 1,215-1,303 ns; mixed old 94-124 ns vs new 734-796 ns. Ablation (implementation committed first; scripts/ablation-replace.mjs wrap mode; each leg restored with blob == HEAD and git diff HEAD empty): leg B core widened to a bare HH:MM prefix test, 1 red (the 24:00/10:00Z pin); leg B2 core answers false, 4 red (the 23:59:59.999 pin plus [#20264], [#20549], [#20480] wall-clock pins); leg A spec ClockTimeValueSchema re-admits a trailing Z, spec rebuilt, ablation-dist-preflight marker present in 28 built files, core 10:00Z pin red (1 failed 25 passed); control in the same window with core put back to the old private regex: 26 passed (the old shape was blind to a spec edit); restore leg: spec rebuilt, preflight --absent marker in 0 of 230 files, tree clean, 26 passed. eslint --no-inline-config --format json on the 2 changed .ts files at 5d3ff11: 2 files, 0 errors, 0 warnings; population from eslint.config.mjs (/*.{ts,...} and packages/ blocks, --print-config resolves both); parserOptions only ecmaVersion/sourceType, no type-aware linting, so no untouched file's verdict can move. Control-byte self-scan of the 3 files: 0 hits.",
"gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 5d3ff11: 61 commands (same list as at aebac66). All 61 run at 5d3ff11, each exit captured before any pipe. --ran: 61 derived, 59 run exit 0, 2 NOT MEASURED, 0 UNRUN. NOT MEASURED: check:dual-build-cjs-loads and check:type-check-debt, exit 3 PREREQUISITE NOT MET, because they need every workspace package built (80+ packages), beyond the foreground cap; CI builds it. Named by the dispatch: check:lean-entry-closure exit 0 (objectql/core import 15 packages 201 modules, require 15 packages 199 modules, admitted set held, 6 denied absent); check:dts-closure exit 0 (71/71 across 14 built packages); check:nul-bytes exit 0; check:test-source-alias exit 0. Tree note: after the merge, origin/main moved 1 commit (9905e61), changing only scripts/adr-anchors/packages__metadata-protocol__src__protocol.ts.json. No family in this list reads it, so it was not chased. check-widening-tells --declaration no: 3 files NOT MEASURED (no declared surface covers them). CI at one read at 15:10Z of 5d3ff11: success 11, skipped 3 (Console Pin Gate, Build Docs, Packed-tarball smoke), in_progress 17 (Test Core 1-6, Dogfood 1-3, Dogfood Verify CLI, Temporal Conformance, Build Core, Lint & Repo Gates, 4 Type Check lanes). Convergence is the PM's.",
"line_budget": "Changed lines 45 (+41 / -4) over 3 files against the human-merge threshold 5000: under. No skills/, .claude/ or other governed path touched, so no skill line ratchet applies.",
"files_changed": [
"packages/core/src/utils/temporal-comparand.ts (+9 -4)",
"packages/core/src/utils/temporal-comparand.test.ts (+18 -0)",
".changeset/20771-wall-clock-one-rule.md (+14 -0)"
],
"deviations": [
"The pins are asserted through isUninterpretableTemporalComparand('time', ...), the only public door to readsAsWallClock, not by calling readsAsWallClock directly. It is private, and core's index re-exports this module with export *, so exporting it would widen core's public surface and make this Clause-② yes. None of the three strings is an ISO instant, so the verdict is the wall-clock half's alone, and ablation legs B and B2 show the pins go red on it.",
"origin/main 2d5fe76 was merged into the branch (5d3ff11) because dispatch-gates reported a stale tree. The merge brought no change to core or to field-value.zod.ts. The gates, core tests and typecheck were re-run in full on the merged head.",
"Commit trailers use the AGENTS.md model-free pair (Co-authored-by: Claude), not the harness reminder spelling with a model name. AGENTS.md forbids a model identifier in that pair, and the pre-push hook enforces it. The PR footer uses the AGENTS.md session-URL form, not the reminder form.",
"No labels written: the dispatch named none and a changeset exists. The PR carries documentation, size/s, tests and tooling. This run did not write them; the Auto Label check ran on this head."
],
"mcp_calls": "0",
"api_writes": "3 relay strokes, each a POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]: pr_create (POST /repos/objectstack-ai/objectstack/pulls, #20885, draft; 6189 bytes sent, 6189 stored, identical); assign via scripts/pm/label-write.mjs (POST /repos//issues/20885/assignees, os-support-ai, read back); comment (POST /repos//issues/20771/comments, this report). Plus git push of the branch (not a REST write). Reads: 3 payload-tier issue/PR pages, 1 REST GET of check-runs.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed. packages/core/src/utils/temporal-storage-form.ts canonicalTimeOfDay spells its own wall-clock pattern with capture groups. It needs the groups to write HH:MM:SS, so a safeParse cannot replace it, and it is the storage rule readsAsWallClock mirrors. It admits the same set today, so it is unexercised drift, not a defect. It is outside this card's file surface. It is in the PR's Acceptance notes. Dedupe words: canonicalTimeOfDay, temporal-storage-form, wall clock regex, one time-of-day rule."
],
"cleanup": "After this comment: node_modules removed and git worktree remove of ../objectstack-issue-20771. The final hand-back states the result."
}
Generated by Claude Code
objectstack-fleet commentedon Sep 30, 2026
ACCEPT — PR #20885 @
5d3ff119cdomain:engine#1·session_01DEvba2nBuD4tWzfq8r8NFY· 2026-09-30T15:32Z. The seat is the reviewer of record. Everything below was read on GitHub and onorigin/main, not taken from the report.Fixes #20771, thenClause-②: no. The changeset is@objectstack/corepatch. The PR assignee isos-support-ai. 3 files, +41/-4; not governed.readsAsWallClocknow answersClockTimeValueSchema.safeParse(s).successfrom@objectstack/spec/data, a specifier the file already imported. The private regex is gone, andpackages/specis untouched.timewrite arm and the import cell.Z, now turns core's pin red. The old private regex was blind to that edit.23:59:59.999is read;24:00and a zone-suffixed time are refused. They are asserted throughisUninterpretableTemporalComparand('time', …), because the helper is private and exporting it would widen core's surface.Local-runs: none).temporal-storage-form.tscanonicalTimeOfDayspells the same wall-clock set a third time, with capture groups. It needs those groups to writeHH:MM:SS, so asafeParsecannot replace it. It admits the same set today, and no answer is wrong.carrier: none, not filed: a finding with no measured reach is not a card. It is dormant drift, recorded here and in the PR's Acceptance notes.canonicalTimeOfDay, or a card that finds a divergence, takes it.success, 3skipped, all rostered;check:dual-build-cjs-loadsandcheck:type-check-debt, not measured locally, are covered by CI's build and type-check lanes, allsuccess.Landing: ready and auto-merge in this act, as two separate relay acts.
Generated by Claude Code
objectstack-fleet commentedon Sep 30, 2026
Landed — PR #20885 as
6b004c0bddomain:engine#1·session_01DEvba2nBuD4tWzfq8r8NFY· 2026-09-30T16:04Z.main:6b004c0bdis a squash with one parent (27bf358ec) and an ancestor oforigin/main.ClockTimeValueSchema.safeParseappears inpackages/core/src/utils/temporal-comparand.tsat the squash and not at its parent. 3 files, +41/-4, as reviewed.pr_readyandautomerge_enableas two separate relay acts at the reviewed head5d3ff119c(PASS 5914312353). The 3 skipped checks were rostered.added_to_merge_queueat 15:40Z; merged by the queue at 16:04Z.mainnow: core'sreadsAsWallClockanswers from the spec'sClockTimeValueSchema. The private regex is gone, so there is one rule for the time-of-day set.temporal-storage-form.tscanonicalTimeOfDayspells the same set with capture groups. It is dormant drift with no measured reach;carrier: none(ACCEPT 5914480515).Fixes #20771did not close it through the queue, the seventh time this term. The seat closes it ascompletedin this act and removespm:dispatched.Generated by Claude Code
fix(core): readsAsWallClock answers from the spec's ClockTimeValueSch…