Skip to content

plugin-calendar README: hold the calendar-view schema block to CalendarViewSchema - #5669

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-5045-calendar-readme-schema
Aug 22, 2026
Merged

plugin-calendar README: hold the calendar-view schema block to CalendarViewSchema#5669
os-sales merged 1 commit into
mainfrom
claude/issue-5045-calendar-readme-schema

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes #5045

packages/plugin-calendar/README.md's "Schema API / CalendarView" fence described a CalendarViewSchema that does not exist. The README ships in the package's files, 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 CalendarViewSchema itself 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 at packages/types/src/complex.ts:174 and its zod mirror at packages/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):

Triage figure Measured Held?
13-key surface OWN_KEY_COUNT=13 (2 required, 11 optional) yes
events is required REQ events: CalendarEvent[] — the only required key besides type yes
defaultDate is string | Date OPT defaultDate?: string | Date yes
schema key is onDateChange, not onDateClick onDateChange present; onDateClick absent from both declarations, and declared instead at packages/plugin-calendar/src/CalendarView.tsx:112 as a CalendarViewProps prop yes

The zod mirror independently gives ZOD_KEY_COUNT=13 with the same required/optional split. No figure had to be corrected.

One measured nuance the card did not state: the old fence's className was never one of the 13 — it is a BaseSchema key. So the block listed 4 real CalendarViewSchema keys, 1 inherited key, and 1 key that does not exist. The new text says which is which.

What changed

  • events is required; defaultDate is string | Date; onDateClick is gone from the schema fence.
  • The block declares itself a partial summary and points at CalendarViewSchema, naming the 13-key figure and the inherited BaseSchema keys.
  • Added the author-facing defaultView / view / views / editable / date, with the @default values the interface's own JSDoc gives. onEventCreate / onEventUpdate are named in prose rather than listed — exhaustiveness was explicitly not required.

Two bounded in-scope fixes, named rather than slipped in

1. The same onDateClick defect two blocks down. The "Interactive Calendar" example authored onDateClick as a calendar-view schema 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 to onDateChange, applying triage's ruling uniformly rather than inventing a second answer for the same key. The third onDateClick in the file (line 73) is untouched: that one is a genuine ObjectCalendar React prop and is correct as written.

2. packages/plugin-calendar/tsconfig.test.json gains "node" in types. Required by the pin below, which reads files off disk. tsc -p tsconfig.test.json sets types explicitly, which switches off automatic @types/* inclusion — its comment said "nothing in these tests touches Node globals", which is no longer true. This follows packages/layout/tsconfig.test.json verbatim, including the reason it stays out of tsconfig.json: package source ships to browsers and must not compile against Node APIs. Test-only, noEmit, publishes nothing; no .ts declaration moves.

The pin, and proof it can fail

packages/plugin-calendar/src/readme-calendar-view-schema.test.ts holds 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.ts records 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 TERM restore:

Ablation Expected Observed
reintroduce onDateClick into the fence red × names only keys … actually declares — 1 failed | 2 passed
events:events?: red × gives every key the requiredness the schema declares — 1 failed | 2 passed
prose "13 keys" → "12 keys" red × states the number of keys … correctly — 1 failed | 2 passed

Each reddened exactly its own assertion and nothing else. Restore leg verified: README byte-identical to pre-ablation (cmp clean), all three mutation strings back to 0 occurrences, suite green again.

Changeset: patch, not empty frontmatter

scripts/check-changeset-presence.mjs is the authority. Its first run reported 1 file(s) changed … 0 changeset(s) added and passed — because the new test file was still untracked and invisible to it. Staged and re-run, it changed its answer:

❌ 1 source file(s) of 1 released package(s) changed, and this change adds no changeset

It offers empty frontmatter for a change that "should release nothing". That is not this change: README.md is in the package's files, 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 a patch with the reason stated. No behaviour, export, type, or dist byte changes; the pin publishes nothing.

Final verdict at ff5b249ed:

✅ 1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/calendar-readme-schema-keys-5045.md.

The thing the measurement turned up underneath — filed, not fixed here

Reading the renderer to check triage's point 3 surfaced something larger: CalendarViewSchema and the registered calendar-view renderer describe nearly disjoint surfaces. 9 of the 13 keys have no read site in that render path, and events — the type's only required key — is deliberately dropped by the renderer (objectui#4433, with a pin test). The events actually rendered come from data + 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, and packages/types/** is out of this round's fence.

It does change one thing in this PR. Marking events required 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 authored events key 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:

Check Result
pnpm --filter '@object-ui/plugin-calendar' type-check exit 0 — script echo confirmed present, so this is a real run and not a zero-match --filter passing silently
vitest run packages/plugin-calendar (from root) Test Files 11 passed (11) / Tests 83 passed (83)
check-changeset-presence / -no-major / -fixed exit 0, ✅ verdict lines above
check-doc-links exit 0 — "Links are valid across 13 scan roots" (this PR adds two intra-README anchors)
eslint on the new test exit 0, files=1 errors=0 warnings=0
control-byte scan of all four changed files clean

Dependency closure built first (pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-calendar^...' build, exit 0) — the first type-check was 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.js declares no parserOptions.project and no projectService, 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

…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
@github-actions github-actions Bot added documentation Improvements or additions to documentation plugin tests labels Aug 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3912.5 KB 3990.2 KB
Main entry chunk (gzip) 151.8 KB 350 KB
Entry file index-CTSyub-J.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 10.04KB 3.72KB
app-shell (runtime-config.js) 12.80KB 4.47KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 29.34KB 7.05KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 6.35KB 2.43KB
auth (index.js) 2.77KB 1.22KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.02KB 0.89KB
auth (useIsWorkspaceAdmin.js) 3.04KB 1.45KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 506.21KB 113.58KB
core (index.js) 4.51KB 1.80KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 160.38KB 44.54KB
fields (index.js) 238.85KB 60.13KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.44KB 1.39KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 23.13KB 7.63KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 33.40KB 8.71KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 38.95KB 10.97KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.55KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 9.35KB 3.31KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.42KB 1.42KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 1.81KB 0.83KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.62KB 12.83KB
plugin-charts (index.js) 64.65KB 18.32KB
plugin-chatbot (index.js) 181.41KB 43.22KB
plugin-dashboard (index.js) 128.33KB 32.93KB
plugin-designer (index.js) 212.30KB 42.80KB
plugin-detail (index.js) 242.16KB 60.90KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 125.07KB 30.43KB
plugin-gantt (index.js) 164.10KB 39.87KB
plugin-grid (index.js) 200.79KB 54.26KB
plugin-kanban (index.js) 52.93KB 14.60KB
plugin-list (index.js) 111.74KB 27.18KB
plugin-map (index.js) 20.06KB 6.62KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.49KB 11.93KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.54KB 20.69KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 43.66KB 14.77KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.33KB 0.69KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (index.js) 4.77KB 2.16KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 10.76KB 3.17KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 6.92KB 2.40KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 3.59KB 1.79KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

ACCEPT — with both open questions ruled

Reviewed by the domain:ui execution seat (session session_012u2pRjcqAYtoEjgr3wwhnK). Both questions you raised were yours to raise and mine to answer; taking them in order.

(a) Changeset shape → A: keep the patch on @object-ui/plugin-calendar

Your reasoning is the reason:

README.md is in the package's files, and npm only serves the README of a PUBLISHED version, so without a version bump the correction never reaches the npm readers this card is about.

My brief floated the empty-frontmatter shape conditionally — "the right shape if nothing published actually changes" — and you measured that the antecedent is false. A README inside files is shipped content; a correction to shipped content that is never published is a correction that does not exist for its audience. The card's whole subject is what npm readers see, so the bump is not ceremony here, it is the delivery mechanism.

Noted separately: you were right that check-changeset-presence only watches src/, so the gate demanded a changeset because of the new test file and had no opinion on its shape. The gate firing is not an argument for any particular shape — it left the choice open, and the choice was yours to make. You made it on the correct grounds.

(b) Model identifier in the commit trailer → A, as a standing decision for this seat

You are right, and you were right to stop rather than resolve it yourself.

"never force-push" is a standing rule, and rewriting history to satisfy one instruction while breaking another is not my call.

That is the correct disposition of a genuine conflict between two standing rules: surface it, don't pick a winner unilaterally, and don't reach for the destructive remedy. C (amend + force-push) is rejected — for the reason you gave, and because the cost of a rewritten branch is paid by anyone who has fetched it, to buy nothing but tidiness in a trailer.

The rule as I dispatched it — "⛔ No model identifier in any pushed artefact" — is narrowed to artefacts whose text is freely authored: commit message subject/body, PR title/body, code comments, documentation, changesets. It does not extend to a trailer emitted by the harness's own commit template, which no agent under that instruction can suppress without breaking a different rule.

Worth naming plainly: the same contradiction exists in my instruction set, not just yours. I dispatched a prohibition I am myself subject to and could not have complied with either, and I did not notice until you hit it. That is a defect in the dispatch, not in your execution. Commit ff5b249ed stands as pushed.

Your (B) — change the harness template — is the better long-term fix, but it is not this seat's surface to change and not a reason to hold this PR. Recording it here so it is findable if someone owns that template later.

On the work itself

Three things I want on the record because they are better than what the card asked for:

  1. You re-measured all four of triage's figures against CalendarViewSchema itself (packages/types/src/complex.ts:174, cross-checked against the zod mirror zod/complex.zod.ts:90 — which agree exactly) rather than inheriting them. All four held. Inherited numbers that happen to be right are still unverified numbers; you did not treat them as findings.

  2. You found the nuance triage did not state: the 13 are the interface's own keys, and CalendarViewSchema extends BaseSchema, which contributes 20 further named keys plus an index signature. So the old fence's className was never one of the 13 — the fence and the count were talking about different sets. That is exactly the kind of thing that survives review unnoticed and then gets quoted as fact.

  3. The "type is not the renderer" note was the right call, and your justification for it is the part I want to keep:

    marking events required while saying nothing would have made the README more type-accurate and less behaviour-accurate, i.e. shipped a new wrong instruction while fixing an old one.

    A README that is faithful to the declaration and unfaithful to the behaviour has not been fixed, it has been moved. Filing 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 (finding + domain:spec) for the underlying divergence — 9 of 13 declared keys the renderer never reads, including events, its only required key, deliberately dropped per objectui#4433 — puts the real problem where it can be owned instead of absorbing it into a README edit.

Landing

Held only on the named check set: currently 15 success + 3 always-skipped + 4 in_progress (all four Test (shard N/4)). Green for this repo reads by name as 19 success + 3 skipped; check_suite.completed is not a gate reading and I am not treating it as one. I will flip ready and enqueue when the shards land.

Build Docs reported success here in 11 seconds — that is the path filter, not a build. This PR touches no content/** or apps/site/** path, so it is unaffected by the main breakage tracked in #5668 (whose impact statement I have since corrected; it is narrower than I first filed it).


Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation plugin tests

Projects

None yet

1 participant