Repository navigation
finding(types/plugin-calendar): CalendarViewSchema declares 9 of its 13 keys that the calendar-view renderer never reads — including events, its only required key, which the renderer deliberately drops #5667
Description
Found while implementing #5045 (correcting packages/plugin-calendar/README.md's "Schema API / CalendarView" block against CalendarViewSchema). Filed unassigned, not claiming.
#5045 was scoped to prose and is fixed as scoped. This card is the thing the measurement turned up underneath it: the published authored type and the registered renderer describe two nearly-disjoint surfaces.
Measured
CalendarViewSchema (packages/types/src/complex.ts:174, mirrored in packages/types/src/zod/complex.zod.ts:90) declares 13 own keys:
type (required) events (required) defaultView? view? defaultDate? date?
views? editable? onEventClick? onEventCreate?
onEventUpdate? onDateChange? onViewChange?
The renderer registered for calendar-view (packages/plugin-calendar/src/calendar-view-renderer.tsx) declares 10 registry inputs, and they are almost a different set:
data titleField startDateField endDateField allDayField colorField
view currentDate allowCreate className
Cross-referencing the two (grep for each key's read sites in packages/plugin-calendar/src/):
CalendarViewSchema key |
read by the calendar-view path? |
|---|---|
type |
yes — registry dispatch |
view |
yes — resolveAuthoredView |
onEventClick, onViewChange |
only as host props — they are in HOST_CALLBACKS, forwarded when the value is a function, which authored JSON can never produce |
events |
explicitly dropped — destructured out as events: _authoredEvents and never used (objectui#4433) |
defaultView, defaultDate, date, views, editable, onEventCreate, onEventUpdate, onDateChange |
no read site anywhere — 0 occurrences outside packages/types |
So 9 of the 13 keys (events plus those eight) do nothing when authored on a calendar-view node, and the events the component actually renders are computed from the node's data array plus titleField / startDateField / endDateField / colorField / allDayField — none of which CalendarViewSchema declares. data and className reach it only through BaseSchema.
Why this is worth a card rather than a note
The two halves are individually defensible and jointly misleading:
- The type requires a key the renderer refuses.
eventshas no?, soconst schema: CalendarViewSchema = { type: 'calendar-view' }does not compile — an author must writeevents— and objectui#4433 then drops it by design, with a pin test. The one key TypeScript forces you to supply is the one key guaranteed to have no effect. - It is the AI-authoring failure shape. A metadata author (human or model) reading the published type writes
defaultView/views/editable/onDateChangeand gets silence — no error, no console warning, no red panel. Declared-but-inert is exactly the state ADR-0049's enforce-or-remove framing says must not persist, and this repo has already answered that question twice on this very renderer, in opposite directions: objectui#4454 ENFORCEDallowCreate, objectui#4493 REMOVEDcolorMapping. Both were registry inputs; nobody has yet asked the same question of the@object-ui/typeslayer above them. - It keeps regenerating documentation defects. finding(plugin-calendar): README 的 "Schema API / CalendarView" 块把必填的
events标成选填,且只列了 CalendarViewSchema 13 个键里的 6 个 #5045 is one.content/docs/api/schema-reference.md:1077publishesonDateChangeas a working callback. The README's "Basic Calendar" example still authorsevents: [...], which renders nothing. Each gets corrected in isolation because the underlying contradiction is not written down anywhere.
What this needs from a maintainer
Which side is authoritative? The answers differ a lot in cost and are not mine to pick:
- A — the renderer is authoritative: retire the inert keys from
CalendarViewSchemaand give it the keys the renderer actually reads (data, the field-name inputs,currentDate,allowCreate). Honest, and makes the type usable as authoring guidance. Breaking to any consumer type-annotating with it, andeventsbecoming optional/absent is a published type change. - B — the type is authoritative: implement the nine keys in the renderer. Large, and buys authoring surface nobody has been measured asking for — which the startup-scope principle argues against.
- C — declare the split explicitly: keep both, document that
CalendarViewSchemais a component-shaped type and the authored node surface is the registry inputs. Cheapest, changes no bytes, but leaves the trap armed for the next author.
My read is A, scoped by measurement — retire only what a liveness check shows no app authors, rather than a blanket rewrite — because it is the option that makes the declared surface and the enforced surface the same thing, which is the property that stops this recurring. But packages/types/** is a published surface and #4972 / #4984 are already in flight there this round, so this wants sequencing rather than a fast follow.
Notes
- Not a duplicate of objectui#4454 or objectui#4493 (both closed): those are the registry inputs layer on the same renderer. This is the
@object-ui/typeslayer above them, which neither touched. - Point 3 of finding(plugin-calendar): README 的 "Schema API / CalendarView" 块把必填的
events标成选填,且只列了 CalendarViewSchema 13 个键里的 6 个 #5045 overlaps objectui#5044's "authorable vs runtime same-name" question in prose only; this card is about read sites, not naming. - finding(plugin-calendar): README 的 "Schema API / CalendarView" 块把必填的
events标成选填,且只列了 CalendarViewSchema 13 个键里的 6 个 #5045's fix does not paper over this — its PR adds a "The type is not the renderer" note to the README naming theeventsdrop, precisely so that correctingeventsto required did not ship as a new wrong instruction. That note is a signpost, not a resolution.
Activity
huangyiirene commentedon Aug 22, 2026
Concentration-round first-touch (triage seat):
finding→ decision inbox (needs-user-decision;domain:specseat context stands) — retiring keys from a published type is the manual floor, and this card's own A/B/C is well-formed.[facets-block]
events)恰是被设计丢弃的键;9/13 键静默无效——每个读类型写元数据的作者(人或 AI)都会踩,且已在持续产生文档缺陷(finding(plugin-calendar): README 的 "Schema API / CalendarView" 块把必填的events标成选填,且只列了 CalendarViewSchema 13 个键里的 6 个 #5045 是第三起)。一句话问题:
CalendarViewSchema以渲染器为权威收敛(退役九个无效键、纳入真实输入),还是实现九键,还是只写文档声明分裂?推荐 A(按测量收窄),与 filer 建议一致,置信度中高。本分析看不见什么:仓外消费者是否用该 published type 做类型标注(决定 breaking 的真实半径);
packages/types在飞的 #4972/#4984 落地后形状(排序约束,非分歧)。裁决格式:回「A」/「B」/「C」。裁后执行:A ⇒ 晋级派 ui/spec@objectui 席(liveness 测量先行 → 类型收敛 + zod 镜像同步 +schema-reference.md:1077等文档同批修),排在 #4972/#4984 之后。Generated by Claude Code
huangyiirene commentedon Aug 22, 2026
Maintainer ruling recorded — A: the renderer is authoritative;
CalendarViewSchemaconverges on the measured read setProvenance: maintainer, 2026-08-22, live session with the triage seat (「同意所有」, item 12 = A, scoped by measurement).
State:
needs-user-decision→pm:queue(spec@objectui seat). Scope: liveness measurement first (retire only what no app authors — the card's own qualifier), then converge the type on whatcalendar-viewactually reads (data, the five field-name inputs,view,currentDate,allowCreate,className), retiring the nine inert keys including the required-but-droppedevents; zod mirror moves in the same change; the documentation instances (schema-reference.md:1077'sonDateChange, the README example) are repaired in the same PR so the contradiction has no surviving copy. Published-type change ⇒ standard breaking/changeset discipline. Sequencing: after in-flight #4972/#4984 land onpackages/types.Generated by Claude Code
feat(types,plugin-calendar): converge CalendarViewSchema on the calen…