Repository navigation
finding(types,components): objectui#7759 group D date-value keys — CalendarSchema.defaultValue / .value cross the JSON/TS boundary: the mirror takes ISO strings or Date while the declaration says Date #10293
Description
Activity
- addeddomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
on Sep 24, 2026 objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsClaim: PM loop round 7 — objectui#10293 (#7759 group D date-value keys, D1-(iii)):
CalendarSchema.defaultValue/.value, with an ISO-string mirror, aDatedeclaration and renderer coercion
Session:session_01877XiBYSaRCk2CU7cMSg3S
Branch:claude/issue-10293-calendar-date-values
Worktree:objectui-issue-10293
Domain:domain:spec
Seat:domain:spec#1
File surface:packages/types/src/form.tsandzod/form.zod.ts(CalendarSchemaonly, about:1140/:534), thecalendarrenderer inpackages/components(the read site's coercion only), the matching parity-ledger rows, pins, and one.changeset/10293-…md
Container & model:S,mode:subagent,model: opus(TIER_DEFAULT)
Clause-②: yes
Thread-read: none
Serial constraints cleared: re-measured at 2026-09-24T16:13Z.form.zod.ts/form.tsare also held by #7759-E (:158/:181/:824), objectui#10286 (FormSchema, about:882) and PR objectui#10292 (SliderSchema, about:436).CalendarSchemais a separate region. The parity ledger is shared, and whichever lands later mergesmain.Dispatch authority: objectstack-ai/objectstack#19980 (lane-scoped gate), and 「任务很多,并发保持3」 (the maintainer, in this seat's session). This fills the slot freed by objectui#10280's dev (PR objectui#10292).
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 10293,
"status": "done",
"branch": "claude/issue-10293-calendar-date-values",
"pr": "#10302",
"session": "session_01877XiBYSaRCk2CU7cMSg3S (subagent, so this is the parent seat's id)",
"premise_still_valid": true,
"summary": "Implemented ruling D1-(iii) for CalendarSchema.defaultValue / .value. The TS declaration went fromDate | Date[]toDate | string. The mirror keepsz.union([z.string(), z.date()]); only its describe text changed, now naming the ISO 8601 authoring type. The z.date() arm stays on in-tree precedent: DatePickerSchema.defaultValue/.value and this schema's minDate/maxDate. The ui:calendar renderer now coerces a string through toDisplayDate from @object-ui/core (the objectui#10183 helper). The three ledger rows (KnownDrift, WiderThanDeclared, WIDER_ARMS) are removed, and the pinned header figures are re-derived with history sentences. The docs page is updated and the changeset is minor on types and components. Premise correction, measured on base 5ea623e: (1) the declaration wasDate | Date[], notDateas the issue said. (2) A string already reached DayPicker, which parses it through date-fns, so an ISO string already selected a day. What was missing was the date-only convention:2026-09-15selected the 14th in America/Los_Angeles and the 15th in UTC. The coercion fixes that and nothing else. Every other value selects the same day as before, including invalid strings, which select nothing. Following the ruling text and the dispatch, the Date[] arm left the declaration. That is flagged as open question 1. Worktree removed. Labels and assignees untouched.",
"tests": "At HEAD 248f23d. (1)pnpm --filter @object-ui/types type-check(tsc + examples + tsconfig.test.json): VERDICT command-exit 0. (2)pnpm exec vitest run packages/types/ packages/components/src/renderers/form/__tests__/calendar.dateValueZone-10293.test.tsx scripts/__tests__/date-only-zone-pins-10183.test.ts --maxWorkers=2:Test Files 217 passed (217),Tests 4939 passed | 7 skipped (4946), VERDICT command-exit 0. The 7 skipped are the driven zone cases in the normal run; the driver ran them in its forks child and passed. (3)pnpm --workspace-concurrency=2 --filter '@object-ui/components^...' build(Scope: 8 of 47): exit 0. Thenpnpm --filter @object-ui/components type-checkplusvitest run packages/components/src/renderers/form/:Test Files 62 passed (62),Tests 427 passed | 7 skipped, VERDICT command-exit 0. (4) eslint --no-inline-config --format json on the 7 changed lintable files. The population comes from the diff and the count from the JSON output: 7 files, 0 errors, 15 warnings, all pre-existing no-explicit-any (calendar.tsx 3 on origin/main, 3 now). Invariance: eslint.config.js sets no parserOptions.project, so linting is not type-aware and this diff cannot move the verdict for any untouched file. (5) node scripts/check-changeset-presence.mjs, check:spec-symbols, check:new-line-citations (VERDICT ... 0 new citation(s)), check:doc-types, check:doc-fences, check:doc-example-ids, check:changeset-claims, check:pending-changeset-literals, check:control-bytes, check:component-surface-parity, check:test-path-roots, check-changeset-no-major: all exit 0. NOT MEASURED: check:doc-snippets and check:doc-examples, reason: PRECONDITION NOT MET (exit 2), needs a 34-package build; declared to CI. objectui has no dispatch-gates.mjs, so the gate list was derived by hand from package.json. Renderer ablation: fix committed first, then objectstack scripts/ablation-replace.mjs in WRAP mode, anchorselected={toSelected(schema.value || schema.defaultValue) as any}changed to the raw read. Anchor x1 to x0, blob 6e729e18d454 to 3323137ca363. Drivervitest run scripts/__tests__/date-only-zone-pins-10183.test.ts:Tests 2 failed | 4 passed (6), and the calendar pin had exactly 1 failing case, the west date-only case. Restore: blob == HEAD andgit diff HEADempty. The renderer is imported by relative source path, so no dist build was needed for this leg. Declaration ablation:value?: Date | stringchanged toDate | Date[], thentsc -p tsconfig.test.jsonexit 2 with 7 errors: 4 in calendar-date-value-boundary-10293.test.ts, and 3 in zod-mirror-parity.test.ts, whereform.zod.ts#CalendarSchemais not assignable to never. Restore: blob == HEAD andgit diff HEADempty. Reverse-verification direction observed: turns red, as expected.",
"mcp_calls": "0",
"api_writes": "2 relay writes plus 1 git push. (1) git push of branch claude/issue-10293-calendar-date-values: the empty probe push, then commit 248f23d. (2) fleet-write pr_create, which is POST /repos/objectstack-ai/objectui/pulls (draft), run 36028956775, PR #10302, body read back byte-identical at 7186 bytes. (3) fleet-write comment, POST /repos//issues/10293/comments, this report. The first pr_create attempt was refused locally by validate (actions must be an array); it sent nothing. Zero label writes, because the dispatch said not to touch labels and no objectui gate reads them.",
"open_questions": [
{
"question": "CalendarSchema.defaultValue/.value: should the TS declaration keep theDate[]arm? On main the declaration wasDate | Date[], which the issue body missed. Both the ruling (TS records the implementation, written as string | Date) and the dispatch namedDate | string, so this PR drops Date[]. The renderer still forwards an in-process Date[] unchanged, and mode multiple still renders with it (measured).",
"options": [
"A: keep the drop, as in this PR. Faces are equal, all three ledger rows leave, and the change is breaking for TS only: a Date[] caller no longer type-checks. There are zero in-tree callers.",
"B: keep Date[] in the declaration (Date | Date[] | string). The mirror stays string | Date, and one KnownDrift row stays, re-described as an in-process-only list arm that JSON cannot carry.",
"C: widen both faces to a list (the mirror gains an array of string or Date, and the renderer coerces element-wise). This gives multiple mode an authorable value, but it adds authoring surface beyond D1-(iii)."
],
"recommendation": "A, because it is what the ruling text and the dispatch both name, the mirror never accepted a list, no in-tree caller passes one, and the runtime still tolerates it. B is a two-line revert if the contract review prefers it. The four-axis decision framework was not carried in the dispatch, so this recommendation is not scored on those axes."
},
{
"question": "Should the mirror's string arm be refined to ISO 8601 (for example z.iso.date() or z.iso.datetime()) rather than a plain z.string()?",
"options": [
"A: keep z.string(), as in this PR, matching every sibling date key (DatePickerSchema, minDate/maxDate, CalendarEvent start/end), and document ISO in the describe text.",
"B: add a format refinement. This narrows the mirror and would reject non-ISO strings that the engine parses today; it would be the only date key with one."
],
"recommendation": "A, because there is no in-tree precedent for a format refinement, and a narrowing should be decided for all date keys at once, not for one pair."
}
],
"out_of_scope_findings": [
"class: a · evidence: on base 5ea623e and on 248f23d, ui:calendar with {mode: 'multiple', value: '2026-09-15'} or with a Datevalue/defaultValuethrowsselected?.some is not a functionat render. The mirror accepts both documents (CalendarSchema.safeParse succeeds), so a document the mirror accepts crashes the node. Seam: spec:form.zod.ts#CalendarSchema.mode+value → renderer:packages/components ui:calendar (DayPicker multiple mode expects an array) · dedupe words: calendar multiple mode scalar value throws, selected some is not a function, ui:calendar mode multiple crash",
"class: c · evidence: ui:calendar with {mode: 'range'} and any value the mirror accepts (an ISO string or a Date; a Date[] was also tried) selects no day. DayPicker range mode reads {from, to}, which neither face can express, so the authored value is silently dropped. The producer is any JSON author or AI that writes ui:calendar metadata. Seam: spec:form.zod.ts#CalendarSchema.mode+value → renderer:packages/components ui:calendar · dedupe words: calendar range mode value ignored, ui:calendar range from to, calendar range selected nothing",
"carrier: none (承接者:无) · not filed · the ui:calendar primitive does not navigate to a selected date outside the current month (no defaultMonth). This was true before the change and is unchanged; recorded in PR Acceptance notes only",
"carrier: none (承接者:无) · not filed · deviation: commit 248f23d carries the harness-shaped trailerCo-Authored-By: Claude Opus 5.5(it names the model). The os-dev contract asks for model-free trailer pairs. History is not rewritten, per the contract; this is reported only"
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsACCEPT: objectui#10293, delivered by PR objectui#10302 at head
248f23d. Sessionsession_01877XiBYSaRCk2CU7cMSg3S,domain:spec@ objectui seat.The change (ruling D1-(iii)):
- The declaration becomes
Date | string. - The mirror keeps
string | Date, with ISO 8601 named in its describe text. ui:calendarcoerces strings throughtoDisplayDate(the objectui#10183 helper), so2026-09-15selects the 15th west of UTC instead of the 14th. No other value changes the day it selects.- All three ledger rows are removed.
Premise corrected:
main's declaration wasDate | Date[], notDate. DroppingDate[]is TS-only, has 0 in-tree callers, and is judged faithful by the review.Review: Clause-② yes. The isolated contract review record at this head, on the PR, says PASS. It answers both open questions: drop
Date[](A), and no ISO refinement (A).Tests:
- types: 4939.
- components form renderers: 427.
- Zone pins driven west and east.
- The renderer and the declaration were each reverse-verified by ablation.
Out-of-scope findings, filed as objectui#10304 (
pm:queue, serial after this PR):mode: 'multiple'with a scalar value crashes (class a).mode: 'range'silently drops every accepted value (class c).
Noted but not filed: no
defaultMonthnavigation for a selection outside the current month.Landing: flip ready and enqueue once every check on this head is green or an expected skip.
⚠️ The Spec Main Shape Gate is red on every objectui head because of objectui#10287.
Generated by Claude Code
- The declaration becomes
- added a commit that references this issue
on Sep 28, 2026
Execution split of #7759, group D, the date-value keys. It follows ruling 5617221514 as adopted by the director (5617465269 and 5617614225), item D1-(iii): 「日期值键裁一次 JSON/TS 边界(镜像声明 ISO 串、声明保留
Date、渲染器补缺失的 coercion)」. Priority and domain are inherited from the parent (priority:p2,domain:spec). Filed by thedomain:spec@ objectui execution seat, sessionsession_01877XiBYSaRCk2CU7cMSg3S. Siblings: group E is on the parent, objectui#10280 is group B, and objectui#10286 is the decidable C/D subset.The keys
form.zod.ts#CalendarSchema(about:534) /form.ts#CalendarSchema(about:1140)defaultValue,valuez.union([z.string(), z.date()])and the declaration isDate, so the two faces are disjoint on the string armDate). The declaration keepsDatefor TS authors and adds the ISO string the mirror accepts. The renderer adds the missing string →Datecoercion at its read site, so an authored ISO string actually selects the day.The dev measures the read site (the
calendarrenderer inpackages/components) before and after: an ISO string must select the same day as the equivalentDate. Watch for west-of-UTC day drift on date-only strings; objectui#10183 is the in-tree precedent for rendering a date-only value as the day it names.Done when
Both rows leave
WiderThanDeclared,WIDER_ARMSandKnownDrift. A pin reddens on revert. A renderer test proves an ISO string selects the named day. There is one changeset (minoron@object-ui/types, plus@object-ui/componentsfor the coercion). Clause-② yes.