Skip to content

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

Closed

Description

@os-sales

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. events has no ?, so const schema: CalendarViewSchema = { type: 'calendar-view' } does not compile — an author must write events — 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 / onDateChange and 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 ENFORCED allowCreate, objectui#4493 REMOVED colorMapping. Both were registry inputs; nobody has yet asked the same question of the @object-ui/types layer 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:1077 publishes onDateChange as a working callback. The README's "Basic Calendar" example still authors events: [...], 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 CalendarViewSchema and 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, and events becoming 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 CalendarViewSchema is 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

Activity

  1. added
    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
    on Aug 22, 2026
  2. huangyiirene commented on Aug 22, 2026

    @huangyiirene
    Collaborator

    Concentration-round first-touch (triage seat): finding → decision inbox (needs-user-decision; domain:spec seat 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 是第三起)。
    • 项目长远合理性:A(渲染器为权威、类型收敛到真实读集)让 declared=enforced 重新成立——正是终结此类反复的性质;B 为无人要的九键实现渲染,反向违反 implementation-first。
    • 防 AI 写代码犯错:published type 是 AI 的第一参考;今天它教的是一门渲染器不讲的语言。A 最对症;C 把陷阱留给下一个作者。
    • 创业阶段不扩散需求:A 按测量收窄(只退无人授权的键),零新能力;B 最贵。

    一句话问题: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

  3. huangyiirene commented on Aug 22, 2026

    @huangyiirene
    Collaborator

    Maintainer ruling recorded — A: the renderer is authoritative; CalendarViewSchema converges on the measured read set

    Provenance: 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 what calendar-view actually reads (data, the five field-name inputs, view, currentDate, allowCreate, className), retiring the nine inert keys including the required-but-dropped events; zod mirror moves in the same change; the documentation instances (schema-reference.md:1077's onDateChange, 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 on packages/types.


    Generated by Claude Code

  4. added a commit that references this issue on Aug 23, 2026
    065bba7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions