Skip to content

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

@objectstack-fleet

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 the domain:spec @ objectui execution seat, session session_01877XiBYSaRCk2CU7cMSg3S. Siblings: group E is on the parent, objectui#10280 is group B, and objectui#10286 is the decidable C/D subset.

The keys

pair keys now direction under D1-(iii)
form.zod.ts#CalendarSchema (about :534) / form.ts#CalendarSchema (about :1140) defaultValue, value the mirror is z.union([z.string(), z.date()]) and the declaration is Date, so the two faces are disjoint on the string arm the mirror declares the authoring type as an ISO date string (JSON has no Date). The declaration keeps Date for TS authors and adds the ISO string the mirror accepts. The renderer adds the missing string → Date coercion at its read site, so an authored ISO string actually selects the day.

The dev measures the read site (the calendar renderer in packages/components) before and after: an ISO string must select the same day as the equivalent Date. 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_ARMS and KnownDrift. A pin reddens on revert. A renderer test proves an ISO string selects the named day. There is one changeset (minor on @object-ui/types, plus @object-ui/components for the coercion). Clause-② yes.

Activity

  1. added
    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
    on Sep 24, 2026
  2. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 7 — objectui#10293 (#7759 group D date-value keys, D1-(iii)): CalendarSchema.defaultValue / .value, with an ISO-string mirror, a Date declaration 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.ts and zod/form.zod.ts (CalendarSchema only, about :1140 / :534), the calendar renderer in packages/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.ts are also held by #7759-E (:158 / :181 / :824), objectui#10286 (FormSchema, about :882) and PR objectui#10292 (SliderSchema, about :436). CalendarSchema is a separate region. The parity ledger is shared, and whichever lands later merges main.

    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).

  3. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    os-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 from Date | Date[] to Date | string. The mirror keeps z.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 was Date | Date[], not Date as 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-15 selected 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. Then pnpm --filter @object-ui/components type-check plus vitest 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, anchor selected={toSelected(schema.value || schema.defaultValue) as any} changed to the raw read. Anchor x1 to x0, blob 6e729e18d454 to 3323137ca363. Driver vitest 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 and git diff HEAD empty. The renderer is imported by relative source path, so no dist build was needed for this leg. Declaration ablation: value?: Date | string changed to Date | Date[], then tsc -p tsconfig.test.json exit 2 with 7 errors: 4 in calendar-date-value-boundary-10293.test.ts, and 3 in zod-mirror-parity.test.ts, where form.zod.ts#CalendarSchema is not assignable to never. Restore: blob == HEAD and git diff HEAD empty. 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 the Date[] arm? On main the declaration was Date | Date[], which the issue body missed. Both the ruling (TS records the implementation, written as string | Date) and the dispatch named Date | 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 Date value/defaultValue throws selected?.some is not a function at 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 trailer Co-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

  4. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT: objectui#10293, delivered by PR objectui#10302 at head 248f23d. Session session_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:calendar coerces strings through toDisplayDate (the objectui#10183 helper), so 2026-09-15 selects 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 was Date | Date[], not Date. Dropping Date[] 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 defaultMonth navigation 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanepriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions