…o CalendarViewSchema (#5045)
The "Schema API / CalendarView" fence described a `CalendarViewSchema` that
does not exist. Measured against the interface (`packages/types/src/complex.ts`)
and its zod mirror (`packages/types/src/zod/complex.zod.ts`):
- `events` is REQUIRED — the schema's only required key besides `type` — and
was published as `events?`;
- `defaultDate` is `string | Date`, not `string`;
- `onDateClick` is not on the schema at all. It is a `CalendarViewProps`
component prop; the schema's key is `onDateChange`;
- 6 of 13 keys were listed with nothing saying the block was a summary.
The block now carries the schema's requiredness, names itself a partial summary,
and adds the author-facing `defaultView` / `view` / `views` / `editable` / `date`.
It also states what the registered renderer actually reads, so the corrected
requiredness does not become a new wrong instruction on its own.
A pin holds the fence to the interface from now on: hand-correction is the
failure mode this repo already named in `readme-registration-keys`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012u2pRjcqAYtoEjgr3wwhnK
Fixes #5045
packages/plugin-calendar/README.md's "Schema API / CalendarView" fence described aCalendarViewSchemathat does not exist. The README ships in the package'sfiles, so it is the npm landing page.Every figure re-measured against the schema, not the card
Triage supplied four figures. All four were verified against
CalendarViewSchemaitself before anything was written — the point of this card is that a published README restated a schema wrongly, so inheriting a figure would have reproduced the defect in a new place. Both declarations were read: the interface atpackages/types/src/complex.ts:174and its zod mirror atpackages/types/src/zod/complex.zod.ts:90. They agree exactly.Measured by brace-matching the interface body and extracting depth-1 property declarations (not by eye, and not from the README):
OWN_KEY_COUNT=13(2 required, 11 optional)eventsis requiredREQ events: CalendarEvent[]— the only required key besidestypedefaultDateisstring | DateOPT defaultDate?: string | DateonDateChange, notonDateClickonDateChangepresent;onDateClickabsent from both declarations, and declared instead atpackages/plugin-calendar/src/CalendarView.tsx:112as aCalendarViewPropspropThe zod mirror independently gives
ZOD_KEY_COUNT=13with the same required/optional split. No figure had to be corrected.One measured nuance the card did not state: the old fence's
classNamewas never one of the 13 — it is aBaseSchemakey. So the block listed 4 realCalendarViewSchemakeys, 1 inherited key, and 1 key that does not exist. The new text says which is which.What changed
eventsis required;defaultDateisstring | Date;onDateClickis gone from the schema fence.CalendarViewSchema, naming the 13-key figure and the inheritedBaseSchemakeys.defaultView/view/views/editable/date, with the@defaultvalues the interface's own JSDoc gives.onEventCreate/onEventUpdateare named in prose rather than listed — exhaustiveness was explicitly not required.Two bounded in-scope fixes, named rather than slipped in
1. The same
onDateClickdefect two blocks down. The "Interactive Calendar" example authoredonDateClickas acalendar-viewschema key — triage's point 3, in a different fence. Fixing one and leaving the other would have left the exact false statement this card exists to remove. Renamed toonDateChange, applying triage's ruling uniformly rather than inventing a second answer for the same key. The thirdonDateClickin the file (line 73) is untouched: that one is a genuineObjectCalendarReact prop and is correct as written.2.
packages/plugin-calendar/tsconfig.test.jsongains"node"intypes. Required by the pin below, which reads files off disk.tsc -p tsconfig.test.jsonsetstypesexplicitly, which switches off automatic@types/*inclusion — its comment said "nothing in these tests touches Node globals", which is no longer true. This followspackages/layout/tsconfig.test.jsonverbatim, including the reason it stays out oftsconfig.json: package source ships to browsers and must not compile against Node APIs. Test-only,noEmit, publishes nothing; no.tsdeclaration moves.The pin, and proof it can fail
packages/plugin-calendar/src/readme-calendar-view-schema.test.tsholds the fence to the interface. This repo already named hand-correction as the failure mode for exactly this class —packages/layout/src/__tests__/readme-registration-keys.test.tsrecords a README corrected by hand and observes that "someone read it" is not a mechanism. This card's fix was another hand-correction; the pin is what stops the next one being needed.Both sides are parsed from source on every run, never restated in the test, and a moved or renamed interface throws rather than passing a vacuous empty key set. Exhaustiveness is deliberately not asserted — the block is a declared partial summary, and a pin demanding all thirteen would convert an editorial choice into a gate.
A doc-shaped pin over a schema that already matches passes whether or not it reads anything, so each of the three assertions was ablated independently. Every mutation was confirmed on disk in both directions (deleted text → 0, injected text → 1) before the run, and the script carried a
trap … EXIT INT TERMrestore:onDateClickinto the fence× names only keys … actually declares— 1 failed | 2 passedevents:→events?:× gives every key the requiredness the schema declares— 1 failed | 2 passed× states the number of keys … correctly— 1 failed | 2 passedEach reddened exactly its own assertion and nothing else. Restore leg verified: README byte-identical to pre-ablation (
cmpclean), all three mutation strings back to 0 occurrences, suite green again.Changeset:
patch, not empty frontmatterscripts/check-changeset-presence.mjsis the authority. Its first run reported1 file(s) changed … 0 changeset(s) addedand passed — because the new test file was still untracked and invisible to it. Staged and re-run, it changed its answer:It offers empty frontmatter for a change that "should release nothing". That is not this change:
README.mdis in the package'sfiles, and npm only serves the README of a published version — without a bump the correction never reaches the readers the card is about. So it carries apatchwith the reason stated. No behaviour, export, type, ordistbyte changes; the pin publishes nothing.Final verdict at
ff5b249ed:The thing the measurement turned up underneath — filed, not fixed here
Reading the renderer to check triage's point 3 surfaced something larger:
CalendarViewSchemaand the registeredcalendar-viewrenderer describe nearly disjoint surfaces. 9 of the 13 keys have no read site in that render path, andevents— the type's only required key — is deliberately dropped by the renderer (objectui#4433, with a pin test). The events actually rendered come fromdata+titleField/startDateField/endDateField/colorField/allDayField, none of which the type declares.Filed as #5667 (unassigned,
finding) with the full cross-reference table and three options for the maintainer. It is not fixed here: it needs a ruling on which side is authoritative, andpackages/types/**is out of this round's fence.It does change one thing in this PR. Marking
eventsrequired and saying nothing would have made the README more type-accurate and less behaviour-accurate — shipping a new wrong instruction while fixing an old one. So the block carries a short "The type is not the renderer" note stating what the renderer reads and that an authoredeventskey is dropped. That note is a signpost to landed, pinned behaviour, not a resolution of #5667.Verification
Union re-run after the final commit, on a clean tree at
ff5b249ed:pnpm --filter '@object-ui/plugin-calendar' type-check--filterpassing silentlyvitest run packages/plugin-calendar(from root)Test Files 11 passed (11)/Tests 83 passed (83)check-changeset-presence/-no-major/-fixedcheck-doc-linkseslinton the new testfiles=1 errors=0 warnings=0Dependency closure built first (
pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-calendar^...' build, exit 0) — the firsttype-checkwas a genuine failure, not the unbuilt closure, and every exit code above was captured before any pipe.Lint narrowing, declared. Repo-wide
eslint .was not run; linting was scoped to the changed files, and the narrowing is lossless rather than a skip: (1) the population comes from eslint's own config, which lints**/*.{ts,tsx}and no markdown — so the README and the changeset are outside it by configuration, not by choice; (2) the count comes from--format json: 1 file, 0 errors, 0 warnings, i.e. the whole lintable diff; (3)eslint.config.jsdeclares noparserOptions.projectand noprojectService, so type-aware linting is off and no untouched file's verdict can move because of this diff. CI runs the full farm regardless.Generated by Claude Code