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
{{ message }}
Repository navigation
bug(components): ui:calendar crashes on a scalar value in mode: 'multiple' and silently drops every value in mode: 'range' — the mirror accepts documents the renderer cannot honour #10304
Surfaced by the dev executing objectui#10293 (PR objectui#10302, #7759 group D date values). Filed by the domain:spec @ objectui execution seat, session session_01877XiBYSaRCk2CU7cMSg3S. ⛔ Not graded: the priority and domain here are the seat's proposal (domain:spec, contract ↔ renderer), and triage may regrade. Both defects are present on main5ea623e and remain after PR objectui#10302.
1 · class a: a document the mirror accepts crashes the node
ui:calendar {mode: 'multiple', value: '2026-09-15'} → throws "selected?.some is not a function"
ui:calendar {mode: 'multiple', value: <Date> | defaultValue} → same
CalendarSchema.safeParse succeeds on both. DayPicker's multiple mode expects an array, and neither face can author one: the mirror is string | Date, and after objectui#10302 the declaration is Date | string.
2 · class c: a value the mirror accepts is silently dropped
ui:calendar {mode: 'range'} with any value the mirror accepts (an ISO string or a Date; a Date[] was also tried) selects no day. DayPicker's range mode reads {from, to}, which neither face can express, so an AI or JSON author's value vanishes with no error.
Direction (to be decided; ruling 5617465269 rule 2: CalendarSchema is objectui-own, so the read site is the truth)
(i) Make value / defaultValue mode-dependent on both faces: a list for multiple, {from, to} for range, with element-wise coercion through toDisplayDate. This adds authoring surface.
(ii) Refuse, at the mirror, a value whose shape does not fit its mode, and keep single-date authoring only. This narrows the accept set.
(iii) Retire mode: 'multiple' | 'range' if no in-tree author uses them.
The seat's reading: measure authored usage of multiple / range first (in examples/, content/, apps/ and the designer inputs), then choose. Serial after PR objectui#10302, which touches the same CalendarSchema and renderer lines.
Claim: PM loop round 11 — objectui#10304: ui:calendarmode: 'multiple' | 'range' values: make the authoring faces express what the read site reads, so a mirror-accepted document neither crashes nor silently drops its value
Session: session_01877XiBYSaRCk2CU7cMSg3S
Branch: claude/issue-10304-calendar-modes
Worktree: objectui-issue-10304
Domain: domain:spec
Seat: domain:spec#1
File surface:
packages/types/src/form.ts and packages/types/src/zod/form.zod.ts (CalendarSchema.value / defaultValue only)
the ui:calendar renderer in packages/components
the zod-mirror-parity.test.ts ledger figures, only if the pair's shape entry moves
Direction: #7759 ruling 5617465269 rule 2. CalendarSchema is objectui-own, so the read site is the truth. The read site (DayPicker) reads a list for multiple and {from, to} for range, which is direction (i): mode-dependent value / defaultValue on both faces, with element-wise coercion through toDisplayDate. The dev measures in-tree authored usage first. If multiple / range have no author AND the designer does not offer them, the dev stops and reports, because (iii), retirement, would then be the candidate and needs a PM decision. Dispatch: objectstack-ai/objectstack#19980 and 「任务很多,并发保持3」.
os-dev-report
{
"issue": 10304,
"status": "done",
"branch": "claude/issue-10304-calendar-modes",
"pr": "#10376",
"session": "session_01877XiBYSaRCk2CU7cMSg3S",
"premise_still_valid": true,
"summary": "Measured before coding: mode multiple/range are authored in-tree (examples/schema-catalog components-form-calendar multiple-dates.json and date-range.json, both shown by content/docs/components/form/calendar.mdx) and the ui:calendar registration inputs offer both modes, so the stop condition did not trigger and direction (i) was implemented. value/defaultValue now admit one day, a list of days, or a strict {from, to} range (to optional) on both faces; the zod mirror (CalendarSchema and UiCalendarSchema, built from one unexported key set because zod 4 refuses .extend() overwriting a key on a refined object) refuses a shape its mode does not read via a node superRefine (code custom, path = offending key). The ui:calendar renderer reads each mode's shape and coerces every day through toDisplayDate; a misfit shape selects nothing instead of throwing and is not reshaped. Docs page and a minor changeset for types+components added. Deviation from the dispatch wording: the TS declaration stays an interface with the key-level union (mode pairing documented, enforced by the mirror only) rather than a mode-discriminated union; see open_questions. zod-mirror-parity ledger unchanged (both faces moved to the same key-level set), no figures re-derived. Assignee on the card was os-bill at start (not written by me). Worktree removed after push; the shared checkout is on claude/vibrant-newton-02en1o with a clean tree.",
"tests": "All at head dd123d7 (code final since b1c42ef; the later commit only adds docs and the changeset). Run under os-verify-lock. vitest packages/types/: 225 files, 5056 tests passed. vitest packages/components/: 292 files passed, 1 skipped; 2821 tests passed, 17 skipped. vitest examples/schema-catalog/: 33 files, 2182 tests passed. Zone driver scripts/tests/date-only-zone-pins-10183.test.ts: 6/6, including 3 new driven cases west of UTC in calendar.dateValueZone-10293.test.tsx. type-check for @object-ui/types and @object-ui/components: exit 0, after building the closure of @object-ui/components and its dependencies; tsc --listFilesOnly confirms the 3 calendar test files are in the test programs. eslint . in both packages: exit 0. Ablations (each committed first, restored with git checkout HEAD --; each restore proven by an empty git diff HEAD and a HEAD blob hash match; mutation proven on disk by grep -c of the marker, 1 before and 0 after): (1) renderer set back to its 8c10f4f bytes: calendar.selectionModes-10304 1 failed / 5 passed, the multiple-plus-one-day crash case. The range cases stay green in UTC because DayPicker's own parse lands on the named day there, hence (2). (2) per-day coercion replaced by new Date(value), run on the forked pool with OBJECTUI_DATE_ZONE_CHILD=1: 3 failed / 10 passed (single-day west, multiple west, range west). (3) mirror refinement forced to always pass: 7 failed / 10 passed across calendar-selection-mode-10304 and calendar-date-value-boundary-10293. Gates exit 0: check:spec-symbols, designer-field-key-parity, component-surface-parity, new-line-citations, control-bytes, doc-types, doc-fences, doc-example-ids, prompt-keys, registry-bare-names, changeset-claims, pending-changeset-literals, test-path-roots, handler-key-reads, check-changeset-presence, check-changeset-no-major, spec-main-shape-gate --self-test 26/26. NOT MEASURED: spec-main-shape-gate inject. The documented usage requires --tarball, --sha and --upstream-checkout, i.e. a spec packed from objectstack main plus a repo-wide typecheck; the bare 'inject' as dispatched exits 2 with a usage message. That is the CI job, and the diff touches no @objectstack/spec import. NOT MEASURED: check:doc-examples, doc-snippets, skill-examples, readme-exports; PREREQUISITE NOT MET, they need a whole-tree build, and they read ts/tsx fences and READMEs only, none of which were edited. Substitute: the 3 JSON examples added to calendar.mdx were parsed against UiCalendarSchema and all 3 are accepted. Local narrowing declared: repo-wide pnpm lint is left to CI.",
"mcp_calls": "0",
"api_writes": "2 fleet-write relay strokes, each a POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create, which became POST /repos/objectstack-ai/objectui/pulls (draft #10376); (2) comment, which became POST /repos//issues/10304/comments (this report). No label write: objectui path labels come from labeler.yml, and the dispatch named no label. Plus git pushes (not REST).",
"open_questions": [
{
"question": "The dispatch asked for value/defaultValue to be mode-dependent on BOTH faces. The TS declaration is still an interface whose keys are the union of all three shapes, so TypeScript accepts {mode:'multiple', value:'2026-09-15'} and only the mirror refuses it. Should the TS face become a real discriminated union on mode?",
"options": [
"A: keep it as is. The interface carries the key-level union with the pairing documented, and the mirror enforces it. Cost: TS authors get no compile-time pairing check. Every gate that looks declarations up as InterfaceDeclaration keeps working.",
"B: turn CalendarSchema into a type alias: a base interface intersected with a union of Single, Multiple and Range members. Cost: it changes a published declaration kind, and name-keyed InterfaceDeclaration lookups (check-component-surface-parity, check-readme-exports, check-designer-field-key-parity, the zod-mirror-parity ledger lookups) must learn type aliases. UiCalendarSchema's Omit already collapses to the index signature, so it gains nothing."
],
"recommendation": "A. The mirror is the authoring gate that JSON and AI authors hit, and B widens the diff into several gates for a TS-only benefit. If B is wanted, it should be its own card."
}
],
"out_of_scope_findings": [
"class: c · evidence: the ui:calendar registration inputs (the designer, packages/components/src/renderers/form/calendar.tsx) offer mode enum ['default','single','multiple','range'], and UiCalendarSchema.safeParse({type:'ui:calendar', mode:'default'}) fails with invalid_value at path mode. The designer offers a value the mirror refuses. Runtime effect of 'default' on react-day-picker 10 not measured. Seam: spec:form.zod.ts#CalendarSchema.mode -> renderer:packages/components ui:calendar registration inputs · dedupe words: ui:calendar mode default; calendar inputs enum default; calendar mode invalid_value designer · noted in PR Acceptance notes, not filed"
]
}
Activity
objectstack-fleet commentedon Sep 24, 2026
Claim: PM loop round 11 — objectui#10304:
ui:calendarmode: 'multiple' | 'range'values: make the authoring faces express what the read site reads, so a mirror-accepted document neither crashes nor silently drops its valueSession:
session_01877XiBYSaRCk2CU7cMSg3SBranch:
claude/issue-10304-calendar-modesWorktree:
objectui-issue-10304Domain:
domain:specSeat:
domain:spec#1File surface:
packages/types/src/form.tsandpackages/types/src/zod/form.zod.ts(CalendarSchema.value/defaultValueonly)ui:calendarrenderer inpackages/componentszod-mirror-parity.test.tsledger figures, only if the pair's shape entry moves.changeset/10304-…mdContainer & model:
S,mode:subagent,model: opus(TIER_DEFAULT)Clause-②: yes. A published type and the mirror accept set change. An isolated at-tier review happens before enqueue.
Thread-read: none
Serial constraints cleared: re-measured at 2026-09-24T19:52Z. PR objectui#10302, which this card named as serial-before, merged at
8c10f4f7. If the ledger moves, this PR joins the ledger chain after feat(types)!: DetailViewFieldSchema.options reads the spec's authoring SelectOptionSchema (#7759 group F) #10307, fix(types): settle FilterField.operators, Container.maxWidth and HeaderBar.variant between mirror and declaration (objectui#10286) #10300 and fix(types): dashboard dateRange takes the spec authoring member on both faces (objectui#10334) #10338, and re-derives the figures on merge.Direction: #7759 ruling 5617465269 rule 2.
CalendarSchemais objectui-own, so the read site is the truth. The read site (DayPicker) reads a list formultipleand{from, to}forrange, which is direction (i): mode-dependentvalue/defaultValueon both faces, with element-wise coercion throughtoDisplayDate. The dev measures in-tree authored usage first. Ifmultiple/rangehave no author AND the designer does not offer them, the dev stops and reports, because (iii), retirement, would then be the candidate and needs a PM decision. Dispatch: objectstack-ai/objectstack#19980 and 「任务很多,并发保持3」.objectstack-fleet commentedon Sep 24, 2026
os-dev-report
{
"issue": 10304,
"status": "done",
"branch": "claude/issue-10304-calendar-modes",
"pr": "#10376",
"session": "session_01877XiBYSaRCk2CU7cMSg3S",
"premise_still_valid": true,
"summary": "Measured before coding: mode multiple/range are authored in-tree (examples/schema-catalog components-form-calendar multiple-dates.json and date-range.json, both shown by content/docs/components/form/calendar.mdx) and the ui:calendar registration inputs offer both modes, so the stop condition did not trigger and direction (i) was implemented. value/defaultValue now admit one day, a list of days, or a strict {from, to} range (to optional) on both faces; the zod mirror (CalendarSchema and UiCalendarSchema, built from one unexported key set because zod 4 refuses .extend() overwriting a key on a refined object) refuses a shape its mode does not read via a node superRefine (code custom, path = offending key). The ui:calendar renderer reads each mode's shape and coerces every day through toDisplayDate; a misfit shape selects nothing instead of throwing and is not reshaped. Docs page and a minor changeset for types+components added. Deviation from the dispatch wording: the TS declaration stays an interface with the key-level union (mode pairing documented, enforced by the mirror only) rather than a mode-discriminated union; see open_questions. zod-mirror-parity ledger unchanged (both faces moved to the same key-level set), no figures re-derived. Assignee on the card was os-bill at start (not written by me). Worktree removed after push; the shared checkout is on claude/vibrant-newton-02en1o with a clean tree.",
"tests": "All at head dd123d7 (code final since b1c42ef; the later commit only adds docs and the changeset). Run under os-verify-lock. vitest packages/types/: 225 files, 5056 tests passed. vitest packages/components/: 292 files passed, 1 skipped; 2821 tests passed, 17 skipped. vitest examples/schema-catalog/: 33 files, 2182 tests passed. Zone driver scripts/tests/date-only-zone-pins-10183.test.ts: 6/6, including 3 new driven cases west of UTC in calendar.dateValueZone-10293.test.tsx. type-check for @object-ui/types and @object-ui/components: exit 0, after building the closure of @object-ui/components and its dependencies; tsc --listFilesOnly confirms the 3 calendar test files are in the test programs. eslint . in both packages: exit 0. Ablations (each committed first, restored with git checkout HEAD --; each restore proven by an empty git diff HEAD and a HEAD blob hash match; mutation proven on disk by grep -c of the marker, 1 before and 0 after): (1) renderer set back to its 8c10f4f bytes: calendar.selectionModes-10304 1 failed / 5 passed, the multiple-plus-one-day crash case. The range cases stay green in UTC because DayPicker's own parse lands on the named day there, hence (2). (2) per-day coercion replaced by new Date(value), run on the forked pool with OBJECTUI_DATE_ZONE_CHILD=1: 3 failed / 10 passed (single-day west, multiple west, range west). (3) mirror refinement forced to always pass: 7 failed / 10 passed across calendar-selection-mode-10304 and calendar-date-value-boundary-10293. Gates exit 0: check:spec-symbols, designer-field-key-parity, component-surface-parity, new-line-citations, control-bytes, doc-types, doc-fences, doc-example-ids, prompt-keys, registry-bare-names, changeset-claims, pending-changeset-literals, test-path-roots, handler-key-reads, check-changeset-presence, check-changeset-no-major, spec-main-shape-gate --self-test 26/26. NOT MEASURED: spec-main-shape-gate inject. The documented usage requires --tarball, --sha and --upstream-checkout, i.e. a spec packed from objectstack main plus a repo-wide typecheck; the bare 'inject' as dispatched exits 2 with a usage message. That is the CI job, and the diff touches no @objectstack/spec import. NOT MEASURED: check:doc-examples, doc-snippets, skill-examples, readme-exports; PREREQUISITE NOT MET, they need a whole-tree build, and they read ts/tsx fences and READMEs only, none of which were edited. Substitute: the 3 JSON examples added to calendar.mdx were parsed against UiCalendarSchema and all 3 are accepted. Local narrowing declared: repo-wide pnpm lint is left to CI.",
"mcp_calls": "0",
"api_writes": "2 fleet-write relay strokes, each a POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create, which became POST /repos/objectstack-ai/objectui/pulls (draft #10376); (2) comment, which became POST /repos//issues/10304/comments (this report). No label write: objectui path labels come from labeler.yml, and the dispatch named no label. Plus git pushes (not REST).",
"open_questions": [
{
"question": "The dispatch asked for value/defaultValue to be mode-dependent on BOTH faces. The TS declaration is still an interface whose keys are the union of all three shapes, so TypeScript accepts {mode:'multiple', value:'2026-09-15'} and only the mirror refuses it. Should the TS face become a real discriminated union on mode?",
"options": [
"A: keep it as is. The interface carries the key-level union with the pairing documented, and the mirror enforces it. Cost: TS authors get no compile-time pairing check. Every gate that looks declarations up as InterfaceDeclaration keeps working.",
"B: turn CalendarSchema into a type alias: a base interface intersected with a union of Single, Multiple and Range members. Cost: it changes a published declaration kind, and name-keyed InterfaceDeclaration lookups (check-component-surface-parity, check-readme-exports, check-designer-field-key-parity, the zod-mirror-parity ledger lookups) must learn type aliases. UiCalendarSchema's Omit already collapses to the index signature, so it gains nothing."
],
"recommendation": "A. The mirror is the authoring gate that JSON and AI authors hit, and B widens the diff into several gates for a TS-only benefit. If B is wanted, it should be its own card."
}
],
"out_of_scope_findings": [
"class: c · evidence: the ui:calendar registration inputs (the designer, packages/components/src/renderers/form/calendar.tsx) offer mode enum ['default','single','multiple','range'], and UiCalendarSchema.safeParse({type:'ui:calendar', mode:'default'}) fails with invalid_value at path mode. The designer offers a value the mirror refuses. Runtime effect of 'default' on react-day-picker 10 not measured. Seam: spec:form.zod.ts#CalendarSchema.mode -> renderer:packages/components ui:calendar registration inputs · dedupe words: ui:calendar mode default; calendar inputs enum default; calendar mode invalid_value designer · noted in PR Acceptance notes, not filed"
]
}
fix(types,components): ui:calendar selection shape follows its mode (…