Skip to content

spec: ComponentPropsMap['object-calendar'] prescribes calendar: { ..., allDayField } — a shape its own CalendarConfigSchema refuses by name #17054

Description

@claude

Found while implementing objectui#8026 (making object-calendar honour allDayField); out of scope for that card, which is objectui-side only. Measured on objectstack main @ 881ce2b and on the published @objectstack/spec@17.3.0 that objectui resolves — both agree.

Measured

ComponentPropsMap['object-calendar'] refuses a flat allDayField and prescribes a remedy, verbatim from its own diagnostic:

Unrecognized key(s) on this object-calendar: allDayField.
• Write this as a key of the calendar config object instead — calendar: { startDateField, endDateField, titleField, colorField, allDayField }. The flat spelling is the runtime handoff ObjectView/ListView emit and a stored-document fallback the renderer keeps reading; it is not a second authorable spelling (one key per concept, Prime Directive 12).

The prescribed shape is refused by the spec's own CalendarConfigSchema (packages/spec/src/ui/view.zod.ts), a strictObject of exactly four keys — startDateField, endDateField, titleField, colorField:

CalendarConfigSchema.safeParse({ startDateField: 'start_date', endDateField: 'end_date',
                                 titleField: 'subject', colorField: 'status',
                                 allDayField: 'is_all_day' })
  -> REFUSED  unrecognized_keys: ["allDayField"]
     "Unrecognized key(s) on this calendar configuration: `allDayField`. Until these shapes
      were closed an unknown key was dropped silently ..."

Controls on the same call, same session:

input verdict
the four declared keys only ACCEPTED
four keys plus bogusKeyXy REFUSED, unrecognized_keys
four keys plus allDayField REFUSED, unrecognized_keys

And through the real door an author's stored view metadata goes down, ListViewSchema (calendar lives on ListViewShapeSchema), the refusal is reported at path: ["calendar"], in the same shape as the bogus-key control:

ListViewSchema.safeParse({ type: 'calendar', name: 'my_cal', calendar: { ...four, allDayField: 'is_all_day' } })
  -> unrecognized_keys keys=["allDayField"] path=["calendar"]

Why it matters — class (c), a trap

An author (or an AI author, which is who reads these .describe() strings) hits the flat-spelling refusal, follows the remedy it prints exactly, and is refused a second time by a different schema with a different message. The remedy names a key its own contract does not admit. Nothing in either message says the key is not a member of the calendar config at all, so the natural next step is to assume a typo and try more spellings.

The prescription is not the only face saying the key is a member. objectui's ObjectView prints a dev-mode warning listing allDayField among the flat keys that should move into the nested config — the same instruction, the same dead end — and objectui's own designer.ts publishes a calendar options object of { startDateField, endDateField, titleField, colorField, allDayField, defaultView }, so the metadata designer offers the author an input for it.

Two directions, and this report does not pick one — that judgement is triage's:

  1. allDayField is a real member and CalendarConfigSchema is missing it. The whole ecosystem behaves as though it is: the prescription names it, the designer offers it, objectui's renderer now honours it (objectui PR fix(plugin-calendar): object-calendar reads the allDayField it resolves objectui#8765). Adding it is a one-line, purely additive change to a strict schema.
  2. allDayField is not a member and the prescription is wrong. Then the flat-spelling remedy text should stop naming it, and objectui's local extension is what carries the key.

Note the neighbouring precedent, which is why direction 1 does not widen anything in practice: defaultView is refused by CalendarConfigSchema in exactly this shape and is nonetheless a released, honoured objectui key, declared as a sanctioned local extension in @object-ui/types. allDayField currently rides the same repo's .passthrough() anonymously — undeclared in the mirror's shape and absent from its sanctioned-local list. So a later hardening of that .passthrough(), the class recorded for gantt in #15469, would silently drop a shipped capability. That downstream tidy-up is deliberately not a separate card: it dissolves entirely if direction 1 lands here, and is only worth doing on its own if direction 2 is chosen.

Refs: objectstack-ai/objectui#8026, objectstack-ai/objectui#8765. Filed unassigned, no labels — triage's call.

Reported by the ObjectStack dev seat in session session_01611D6ZaRaMmwTNQmSbk8MH.


Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions