Skip to content

docs(plugin-calendar): rewrite the two object-calendar examples onto declared keys, and annotate them - #8470

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-7925-calendar-readme-keys
Sep 8, 2026
Merged

docs(plugin-calendar): rewrite the two object-calendar examples onto declared keys, and annotate them#8470
baozhoutao merged 1 commit into
mainfrom
claude/issue-7925-calendar-readme-keys

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #7925

Both halves of the triage ruling ship here: the six wrong keys are replaced with the keys ObjectCalendarSchema declares, and both blocks are annotated so check:doc-snippets judges them instead of compiling bare literals against nothing.

⚠️ Re-derivation first — four coordinates on the card had drifted

The card was measured at db2c20d08; this branch is based on 868e82501. Every coordinate was re-derived before a byte was touched. The defect is entirely real and unrepaired, but four numbers were stale, and one figure the card and its own triage comment disagreed about is now settled:

the card said measured at 868e82501
packages/types/src/objectql.ts:2672 :2698
getCalendarConfig at ObjectCalendar.tsx:119, gate at :131 function at :155, gate at :162
README blocks at lines 262 / 314 261 / 312
schema.objectName read at 26 sites 19 occurrences — the triage comment's count was the correct one

Also corrected: the brief predicted UNGATED_DOCS had reached {}. It holds 4 entries. packages/plugin-calendar/README.md is not among them — it is covered, so no entry was added and none was needed.

The measurement, with its non-vacuity control

A zero read-site count means nothing without a term known to be present. Both halves come from the same run over packages/plugin-calendar/src/ObjectCalendar.tsx:

CONTROL   schema.objectName      19 occurrences   (present, as expected)
          schema.object           0              (excluding objectName)
          schema.startField       0
          schema.fields           0
          schema.dataSource       0

The control is non-zero in the same run, so the four zeros are readings rather than a broken matcher.

The part of the card worth preserving: one wrong key silently disabled three correct ones

This is not six independent typos. getCalendarConfig gates the entire flat-property branch on the start-date spelling:

if ((schema as any).startDateField || (schema as any).dateField) {

Block 1 supplied startField. The gate was therefore false, getCalendarConfig returned null, and the block's correctly spelled titleField was never read either — the node rendered the "Calendar configuration required. Please specify startDateField and titleField." refusal screen (ObjectCalendar.tsx:833, pinned by ObjectCalendar.unconfiguredRefusal-7029.test.tsx). The prose now states that consequence, because it is the reason the key list is not a matter of taste.

⚠️ What the annotation buys — measured, and written into the file rather than implied

The card's own explanation of why three gates stayed green predicted that annotating would not have caught the key names either, because ObjectCalendarSchema extends BaseSchema and BaseSchema ends with an [key: string]: any index signature (packages/types/src/base.ts:467). That prediction was tested rather than assumed — a throwaway probe compiled against the built dist/*.d.ts:

probe case result
annotated literal carrying the undeclared object and startField 0 diagnostics — excess-property checking does not fire
annotated literal with defaultView: 'agenda' (a retired value) TS2322
annotated literal with titleField: 42 TS2322

So the annotation is live (two positive controls prove it is not vacuously any) but its reach is values, not key names. A reader who sees an annotation and assumes the key set is now guarded would be worse off than one who sees a bare literal, so the block says so in a comment rather than leaving the impression uncorrected.

Ablation — proving the gate now actually judges these blocks

check:doc-snippets was green before and after, with identical block counts (632 compiled, 0 failed). That pair of greens on its own proves nothing about this change, since both blocks already compiled as bare literals. So the annotation was ablated from the committed state:

  • mutation: block 1's defaultView: 'month' changed to 'agenda'
  • proven on disk before reading any result — blob hash fcc26a3d… moved to dc3a8961…, and the marker count for defaultView: 'month' fell from 2 to 1
  • result: the gate went red, exit 1, 1 failed:
[semantic]  packages/plugin-calendar/README.md:281:3  TS2322:
Type '"agenda"' is not assignable to type '"month" | "week" | "day" | undefined'.

The gate named this README, this line, and this annotation. Restored under an EXIT INT TERM trap with absolute paths, and the restore proven by state rather than by exit code: git diff HEAD empty, blob hash back to fcc26a3d…, git status clean.

Verification

check result
check:doc-snippets before exit 0 — 632 of 632 block(s) judged, 0 failed
check:doc-snippets after exit 0 — same, and now judging these two blocks (see ablation)
check:doc-snippets ablated exit 1 — 1 failed, naming this file
check-doc-links exit 0 — Links are valid across 17 scan roots
check-doc-component-types exit 0 — does not read package READMEs, run anyway
check:readme-exports exit 0 — 43 READMEs, 0 fabricated, 0 wrong-path
check:doc-fences exit 0
check:control-bytes exit 0 — 6704 tracked text files
check:doc-example-readers exit 0
check-changeset-presence exit 0 — 0 of them published source of a package the release covers, so none owed; run, not guessed
check-governed-queue-guard --test NOT GOVERNED — 1 path, none of the 5 governed surfaces matched

Exit codes were captured before any pipe, and each verdict line above is the gate's own printed text.

四轴分析

  • 实际业务需求 — 实测,不是"读起来像有用"。这两个块是 object-calendar 在本包 README 里仅有的两个 ObjectQL 示例,而它们教的六个键渲染器一个都不读(上面的对照读数)。照抄 block 1 的读者拿到的不是"部分生效",是一整块拒绝渲染的屏幕。需求是真实的:文档是这个键集唯一的对外教学面。
  • 项目长远合理性 — contract-first,no workarounds。修的是文档(生产者侧),不是渲染器。⛔ 没有在消费端加任何宽容:startField / object / fields 一律直接改成出厂拼写,不留双拼写、不加别名说明。别名去留是 objectui#8355 的题,triage 明确裁定两卡不得重叠,本 PR 不碰 ObjectCalendar.tsx,也不把别名问题写进 README 当搭车。
  • 防 AI 写代码犯错(尤其元数据 app) — 这是本卡的重心,也是标注为什么是交付物的一半。裸字面量对着任何东西都不编译,AI 批量生成的错拼永远不会被发现。加上标注,check:doc-snippets 每次提交重判一次值,消融已证明它能红。⚠️ 但收紧只到值这一层:索引签名让键名仍然不受检。所以这里同时做了第二件事 —— 把这个边界写进文件本身。声明即强制的反面不只是"声明了不兑现",还包括"看起来被守住其实没有";留一个让读者误判覆盖面的标注,比留一个裸字面量更糟。键名那一层要真正关上,需要的是 objectui#8466 那类声明面收敛,不是这里再加一层看着像的东西。
  • 创业阶段不扩散需求 — 从紧。本 PR 只动一个文件的两个块,不新增任何能力面、不新增门禁、不扩 UNGATED_DOCS。顺路量到的两个问题都按无 assignee 立单交 triage,⛔ 没有搭车修:objectui#8466(colorField / allDayField 被读却未声明)、objectui#8468(另外三个 README 仍把 adapter 写成 schema 键,与已关闭的 objectui#5098 同一类)。colorField 因此从 block 1 的示例里去掉而不是保留 —— 保留它等于在一个刚刚说明"键名不受检"的块里,示范一个未声明的键。

Scope

⛔ Untouched, by triage's explicit fence: packages/plugin-calendar/src/ObjectCalendar.tsx and its alias ladder. That question is objectui#8355 and is not addressed here.

Related findings filed while working this card, both unassigned and bare: objectui#8466, objectui#8468.

Drafted by an automated development seat; session reference https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr. Left as a draft for the PM seat to flip.


Generated by Claude Code

…declared keys

Both `object-calendar` blocks in this README taught keys that
`ObjectCalendarSchema` does not declare and the renderer does not read:
`object`, `startField`, `endField`, `dataSource` and a `fields` container.
Measured on the renderer, with `schema.objectName` (19 occurrences) as the
non-vacuity control in the same run: `schema.object`, `schema.startField`,
`schema.fields` and `schema.dataSource` each have 0 read sites.

The wrong start-date key was not a partial failure. `getCalendarConfig` gates
the entire flat-property branch on the start-date spelling, so the first
block's correctly spelled `titleField` was never read either and the node
rendered the "Calendar configuration required" refusal screen.

Both blocks are now annotated `ObjectCalendarSchema`, which puts them inside
`check:doc-snippets` instead of leaving them as bare literals compiled against
nothing. The annotation's real reach is stated in the file rather than implied:
it type-checks the VALUES of declared keys, and it does NOT check key names,
because this interface extends `BaseSchema` and its `[key: string]: any`
admits any spelling.

Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
@github-actions github-actions Bot added documentation Improvements or additions to documentation plugin labels Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3473.5 KB 3512.7 KB
Main entry chunk (gzip) 143.9 KB 350 KB
Entry file index-CjjYG-tk.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 (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
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) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
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) 498.87KB 114.10KB
core (index.js) 7.48KB 2.96KB
create-plugin (index.js) 10.12KB 3.28KB
data-objectstack (index.js) 191.36KB 53.16KB
fields (index.js) 243.15KB 61.40KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
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.99KB 0.87KB
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 (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
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) 4.83KB 2.27KB
plugin-ai (index.js) 15.16KB 3.68KB
plugin-calendar (index.js) 49.00KB 13.91KB
plugin-charts (index.js) 71.39KB 19.92KB
plugin-chatbot (index.js) 194.52KB 46.34KB
plugin-dashboard (index.js) 131.48KB 34.45KB
plugin-designer (index.js) 213.21KB 43.63KB
plugin-detail (index.js) 248.78KB 63.98KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 131.01KB 32.32KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 208.58KB 56.63KB
plugin-kanban (index.js) 55.40KB 15.71KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.33KB 3.25KB
plugin-view (index.js) 84.54KB 20.84KB
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) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.55KB 2.45KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 13.64KB 4.59KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
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 (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
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 (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
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

@baozhoutao
baozhoutao marked this pull request as ready for review September 8, 2026 01:29
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit 189328d Sep 8, 2026
31 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-7925-calendar-readme-keys branch September 8, 2026 01:44
@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

LANDEDdomain:devx @ objectui execution seat, PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46. PR #8470 merged 2026-09-08T01:44:44Z as 189328d3d, in the same queue group as #8462; probe run on a clean extract of re-fetched origin/main, ⛔ never on the shared working tree.

leg reading on origin/main@189328d3d
probe — both blocks annotated const schema: ObjectCalendarSchema × 2
probe — declared keys in place objectName 2 · startDateField 3 · endDateField 3 · defaultView 2
probestartField gone 0
probe — node-level dataSource, gone 0
control — the renderer gate unchanged ObjectCalendar.tsx:162 if ((schema as any).startDateField || (schema as any).dateField) {
control — the grep fires object-calendar × 5

⇒ The corrected keys satisfy the gate that was silently refusing the old ones, and the gate itself was not touched — the alias question stays with #8355.

⚠️ One leg returned 3 where this seat predicted 0, and again the instrument was too coarse. colorField still matches — at :168, :219 and :254, all three inside a different schema: type: 'calendar-view' (CalendarViewSchema), plus the prose about that node's keys. None is in an object-calendar block. ⇒ A file-wide grep asked about a key the card scopes to two specific blocks. Second time in one round, and the same fix applies: scope the probe to the region the card is about.

⭐ What the PR leaves behind is the part worth keeping: the README now states in the file that the annotation type-checks values and not key names, because ObjectCalendarSchema extends BaseSchema whose [key: string]: any admits any spelling — measured against the built dist, with defaultView: 'agenda' and titleField: 42 as the positive controls. A reader who saw an annotation and inferred a guarded key set would have been worse off than one who saw a bare literal.

pm:dispatched stripped in the same pass; domain:devx, bug, documentation, finding, priority:p2 stay. The card closed via Fixes.


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

Projects

None yet

2 participants