Skip to content

feat(types)!: the object-calendar element's calendar container follows the @objectstack/spec 17.7.0 slot by reference (objectui#6152, round 9) - #11980

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-6152-r9-calendar-container
Oct 8, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-6152-r9-calendar-container

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #6152 (round 9). Round 10 (ObjectGridSchema.defaultFilters, and the gantt / map flat filter) is still to come, so #6152 remains open.

Clause-②: no

What changed

ObjectCalendarBlockConfigSchema in packages/types/src/zod/objectql.zod.ts is the object-calendar element's own calendar container. It is now the object-calendar row's own member, taken by reference: ObjectCalendarPropsSchema.shape.calendar.unwrap(), extended only by objectui#8355's two by-name alias refusals (dateField, endField). The following are gone:

  • .partial() and .passthrough();
  • the allDayField restatement. The slot's own member has the same accept set.

The TypeScript face derives from this mirror through ObjectCalendarBlockConfig, so it is now closed in the same way. It has no index signature, and startDateField is required.

Narrowed (a published door). The changeset ships @object-ui/types as minor and states the break:

  • A key outside the five inside the block is now refused with the spec's own unrecognized_keys. That covers calendar.defaultView and misspellings.
  • A block without startDateField is now refused at calendar.startDateField.

The installed slot refuses both, and nothing read either one. getCalendarConfig returns the block whole, and the events pass reads only the five members. So an extra key was dropped unread, and a block with no start binding mounted a calendar that placed no events.

Docblocks rewritten:

  • the ObjectCalendarBlockConfigSchema docblock. The paragraph that said "⛔ .passthrough() is kept" is gone;
  • the ObjectCalendarSchema.calendar note in packages/types/src/objectql.ts. It no longer says "the container stays .passthrough()";
  • the member's .describe().

Mechanism assumptions, measured

Measured on the installed @objectstack/spec 17.7.0. The pin file re-reads all of this on every run: packages/types/src/__tests__/object-calendar-container-by-reference-6152.test.ts.

  • H1 holds: the slot's shape.
    • The wrapper chain is ["optional","object"], and the catchall is never, so the object is strict.
    • Keys: startDateField (required), plus endDateField, titleField, colorField and allDayField.
    • It is not the CalendarConfigSchema object. Each member, though, is the same object as CalendarConfigSchema's, and the error map is the same. So the two give identical issues on every input probed.
    • Decision: the mirror takes the row's own member, not CalendarConfigSchema. Today the verdict is the same either way. The row is the contract this element is judged by, so if the spec ever gives the element its own block, this follows it without an edit.
    • A verdict-equality pin covers 14 inputs plus the two aliases. On every non-alias input, it holds the mirror's container to the slot: the same success, and the same issues (code, path, keys and message).
    • On the aliases, both sides refuse. The spec answers unrecognized_keys. objectui answers invalid_type at the alias's own path, with the named remedy.
  • H2: the slot does not declare defaultView. The row declares it flat, which is where ObjectCalendar reads it. So the narrowing refuses calendar.defaultView. No authored container writes it (census below), so this is the first branch of H2. Nothing was declared that the renderer would not honour.
  • H3 holds: the corpus is clean. No authored container carries a refused key or lacks startDateField. No document needed a correction, and no pair is runtime-only.

Corpus census

The instrument. A TypeScript-AST census was run once, at this head, over every tracked file that carries the literal object-calendar: 121 files at the base, plus this PR's test file and changeset. As a control, the same text search finds object-grid in 397 files. The census finds each element whose type is object-calendar and reads its own calendar container, or properties.calendar. It resolves a bare identifier to a const in the same source, then parses the container against the installed slot.

The same instrument is kept as a pin over the authored trees:

  • examples/, which holds the schema catalog;
  • content/, skills/, docs/ and apps/;
  • every package README, and the root README.

It re-derives the census on every run. It fails loudly on any container it cannot judge, and it has a firing control.

key in an element's own calendar authored documents carrying it read site
any key outside the five none no reader
defaultView none no reader in the block (ObjectCalendar reads the flat defaultView)
dateField / endField none no reader since objectui#8355
a block without startDateField none getCalendarConfig returns the block whole, so nothing is placed
CONTROL: the five declared members content/docs/plugins/plugin-calendar.mdx (12 containers, one of them through a const) and packages/plugin-calendar/README.md (2) the ObjectCalendar events pass

Zero elements found, each with a control:

  • examples/ and the schema catalog: zero object-calendar elements. The same search finds object-grid in 5 files there.
  • skills/: zero. The same search finds object-grid in 3 files.
  • apps/console non-test source: zero element containers. register-plugins.ts only registers the type.
  • docs/: zero.

objectstack corpus (read-only, origin/main): object-calendar appears only in the 17.6 and 17.7 release notes.

Test fixtures (not corpus, classified separately):

  • There are 37 element containers across 24 test files. 33 parse. The other 4 carry only declared keys, with the start binding present and non-literal values.
  • No pre-existing fixture flipped. The dispatched union is green with no fixture edits.
  • The refused keys still in test fixtures (dateField, endField, defaultView, zzqxNoSuchField) fall into two groups. Some sit on the list VIEW's calendar block, a different contract that this round does not touch. The rest are this round's own refusal pins.

Tests (heads 15019c511 and aaba24a2c; the second only adds the ledger entry in scripts/markdown-test-inputs.mjs)

Union, under os-verify-lock, on head 15019c511:

  • pnpm exec vitest run --maxWorkers=2 packages/types/ packages/plugin-calendar/ packages/plugin-list/ packages/plugin-view/ apps/console/src/__tests__/registry-inputs-spec-parity.test.ts: Test Files 607 passed (607), Tests 12311 passed | 95 skipped (12406).
  • scripts/__tests__/: one red on that head. markdown-test-inputs.test.ts flagged the new census as an unadjudicated markdown reader. aaba24a2c registers it.
  • On aaba24a2c, scripts/__tests__/ plus the new pin: Test Files 180 passed | 2 skipped (182), Tests 5508 passed | 2 skipped (5510).

The new pin alone: Tests 56 passed (56).

type-check, run against the rebuilt @object-ui/types dist after turbo run build --filter='@object-ui/console^...' (34/34 tasks):

  • packages: types, core, plugin-calendar, plugin-dashboard, plugin-timeline, plugin-tree, plugin-list, plugin-view, app-shell and console;
  • every row printed its script name and Done;
  • this covers every package whose source names ObjectCalendarSchema, plus the view producers and the app shells;
  • types runs tsconfig.test.json, so the pin's @ts-expect-error directives are live.

Gates, all exit 0:

  • pnpm check:doc-snippets (784 of 784 blocks judged, 0 failed)
  • check:doc-examples, check:skill-examples, check:readme-exports
  • check:doc-types, check:doc-fences, check:doc-example-ids, check:doc-example-readers, check:docs-route-closure
  • check:spec-symbols
  • check:component-surface-parity (report-only, no object-calendar row)
  • check:changeset-claims, check:pending-changeset-literals
  • check:new-line-citations (0 new)
  • check:control-bytes
  • node scripts/check-changeset-presence.mjs, node scripts/check-changeset-no-major.mjs
  • node scripts/markdown-test-inputs.mjs --audit

The doc gates ran after their own --build-filter closure (35/35 turbo tasks).

ESLint over the four changed code files: 0 errors. All 32 warnings are pre-existing no-explicit-any in objectql.ts / objectql.zod.ts.

Ablations

Each ablation is one-shot and runs through ablation-replace.mjs. In each one, the mutation is proven on disk, then restored to the HEAD blob with an empty git diff HEAD.

  • A. The container set back to .partial().passthrough(). The new pin goes 19 red / 37 green. The red rows are:

    • member identity and the catchall;
    • the verdict-equality rows: empty block, missing start, defaultView, misspelling, unknown key, and alias without start;
    • the unknown-key / defaultView / missing-start rows on the element mirror, the tolerant face and safeValidateSchema;
    • the missing-start row on the strict face;
    • the census's extractor control.

    calendar-date-alias-refusal-8355 and plugin-calendar's calendarUnionReads-8651 stay green, because they pin the aliases and the node level. The direction was red, as predicted.

  • B. A consumer literal the closed type refuses, injected into plugin-calendar's ObjectCalendar.tsx: calendar: { startDateField, defaultView }. tsc --noEmit reports error TS2353: Object literal may only specify known properties, and 'defaultView' does not exist, against the rebuilt dist's closed type. That proves consumers read the narrowed .d.ts. The control leg is the same injection with only startDateField, and it compiles (exit 0).

    • ⚠️ The first attempt at B was a no-op. Its replacement contained the anchor, and ablation-replace refused it (anchor count moved 1 -> 1) before running anything. It was re-run with a replacement the anchor no longer matches.

Scope and deviations

Files beyond the claim's literal list, each one forced by this change:

  • content/docs/plugins/plugin-calendar.mdx. Its CalendarConfig section said two things that are now false: that the container "keeps .passthrough() ... any other key you write inside it still parses", and that the spec does not judge the slot. The paragraph is rewritten, and the section's calendar-doc-key-set-8830 pins are still green.
  • packages/plugin-calendar/README.md, which is outside src/**. A fence comment said the calendar block "is open ... a misspelt key there still compiles clean". It is rewritten.
  • scripts/markdown-test-inputs.mjs. The new census reads markdown, and markdown-test-inputs.test.ts was red until its ledger entry was added.

startDateField becomes required inside the block. That is the slot's own verdict, followed by reference. The dispatch named keys only. The census reads zero authored blocks without it.

Left alone:

  • packages/plugin-calendar/src/**. H2 needed no reader there.
  • ListView.tsx.
  • the list VIEW's calendar block.

Acceptance notes (observations, not filed)

  • The list VIEW's per-view-type blocks are still open. KanbanConfig, CalendarConfig, GalleryConfig and TimelineConfig stay .partial() + .passthrough(). The installed spec's ListViewSchema blocks are all strict: catchall never, measured on 17.7.0 for kanban, calendar, gallery, timeline, gantt, map, chart and tree. This is a different contract, and out of this round as dispatched. No carrier.
  • The derived strict face already refused an unknown key here. StrictAnyComponentSchema refused an unknown key inside the element's container before this round: ablation A left its unknown-key and defaultView rows green. The tolerant face and safeValidateSchema did not.
  • A stale docblock in ObjectCalendar.tsx. The ObjectCalendarConfig docblock still says two things: that allDayField is "NOT a spec key", and that the view-level mirror "keeps .passthrough() explicitly for this". It has been stale since 17.5.0. This round did not make it false, and plugin-calendar/src/** is outside the round. No carrier.

Size

7 files, +610 / −103 against d7e9e9ab. Not governed: node scripts/check-governed-queue-guard.mjs --test over the 7 paths prints "NOT GOVERNED".

Implemented in session https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw (os-dev, domain:spec @ objectui seat 1).


Generated by Claude Code

claude added 2 commits October 8, 2026 13:55
…tstack/spec 17.7.0 slot by reference

ObjectCalendarBlockConfigSchema is now the object-calendar row's own
calendar member (ComponentPropsMap['object-calendar'].calendar, unwrapped),
extended only by the two by-name alias refusals. The .partial() and
.passthrough() go: a key outside the five (calendar.defaultView, a
misspelling) and a block without startDateField are refused, by the
slot's own verdict. The derived TypeScript type closes the same way.

Pins: the slot measured on the installed spec, member identity, verdict
equality over a set of inputs, both directions through every door, the
TS face, and a corpus census that every authored container parses.
Docs and the plugin-calendar README stop calling the container open.

Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
Co-authored-by: Claude <noreply@anthropic.com>
…inputs

The new object-calendar container census walks the authored trees and
reads markdown from them, so the ledger records it as a markdown-tree
reader; an edit to those documents now triggers the test.

Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation package: types plugin tests labels Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 337 chunks) 3311.1 KB 3312.0 KB
Main entry chunk (gzip) 71.4 KB 350 KB
Entry file index-CsVXPy82.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) 18.33KB 6.73KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
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) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 587.49KB 141.58KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 268.74KB 68.04KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
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) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
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) 16.04KB 3.92KB
plugin-calendar (index.js) 53.43KB 15.54KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 199.63KB 47.46KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 248.57KB 65.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.11KB 45.88KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 248.22KB 68.84KB
plugin-kanban (index.js) 52.22KB 16.38KB
plugin-list (index.js) 117.68KB 29.40KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.10KB 11.81KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 91.09KB 22.94KB
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.81KB 3.58KB
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) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
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) 1.13KB 0.65KB
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) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: aaba24a2c144033c305835da3bf4f2551c2ab8a9
Local-runs: none

PR objectui#11980, round 9 of card objectui#6152 (draft, not ready, no auto-merge, first line Part of #6152 (round 9), no closing keyword). Inputs, and nothing else: the card body and all 63 comments (the pointer 6028991886, the triage restart 6029312212, the round-8 ACCEPT 6060539258 and landing 6061003736, the claim 6061068534, the dev report 6062742416, and the carried items of the earlier at-tier records 5921606917 / 5924301833), the PR body, its 7-file list, the net diff against main at the merge base d7e9e9ab6 (7 files, +610 / −103, the same 7 paths as the file list; main has since moved three app-shell commits, none touching these paths), and the 43 check-runs on this head: 40 success, 3 skipped (dependabot, coverage, coverage shard), 0 failure. Gate verdicts are read off those check-runs; nothing was built, run or re-run here. The dev's censuses, probes and ablations are read as declared, not as verdicts; where a reading mattered it was re-derived from the diff and the tree at this head.

① Derived judgments

Zod accept sets (@object-ui/types/zod: the object-calendar mirror, the tolerant AnyComponentSchema, the strict StrictAnyComponentSchema, and safeValidateSchema, which the CLI validate / check commands call):

  1. ObjectCalendarBlockConfigSchema becomes stripImportedDefaults(SpecObjectCalendarPropsSchema).shape.calendar.unwrap().extend({ dateField, endField }); .partial(), .passthrough() and the allDayField restatement are gone — RIGHT. This is the direction the pointer and the triage booked (the spec slot by reference; objectui#8327 forbids accepting a key the platform refuses). Taking the ROW's member rather than CalendarConfigSchema is right: the row is the contract the element is judged by, and the two give the same verdict today, which the pin asserts (member identity against the installed slot, same error map). The pin also asserts the catchall stays never through .extend(), so the extension adds no opening. The dropped allDayField restatement had the same accept set as the slot's own member, so removing it moves no verdict; the carrier that was named for that removal (objectui#8831) is closed, so this is the right PR.
  2. The two by-name alias arms (dateField, endField) kept — RIGHT. Both sides refuse an alias; the slot answers a bare unrecognized_keys, objectui answers invalid_type at the alias's own path with the named remedy, which is the whole reason the arms stay. Verdict equality is pinned on 14 non-alias inputs (success and issues, code / path / keys / message), and the alias-plus-missing-start row shows the slot's own missing-start issue still surfaces beside the alias's.
  3. What NARROWS on ObjectCalendarSchema.calendar: (a) any key outside the five, calendar.defaultView and misspellings included, is refused unrecognized_keys at calendar; (b) a block without startDateField is refused invalid_type at calendar.startDateField — RIGHT. Both are the installed slot's own verdicts. The renderer reads neither: getCalendarConfig returns the block whole and the events pass reads the five members, so an extra key was dropped unread and a start-less block mounted a calendar that placed nothing; defaultView is read from the node's flat member, and the pin's flat-defaultView control still parses. By the dev's ablation A (read as declared) the strict face already refused the unknown key before this round, so its narrowing is the missing-start row only; the tolerant face and safeValidateSchema narrow on both. The calendar key itself stays optional on both faces — unchanged. Value checks and the alias refusals are unchanged.
  4. Reach — RIGHT, a narrowing with zero measured reach. Authored: the census in the new pin walks examples (the schema catalog), content, skills, docs, apps, the root and every package README, test trees excluded, resolves const references, and fails loudly on any container it cannot judge; it finds the 14 authored containers (12 in content/docs/plugins/plugin-calendar.mdx, 2 in packages/plugin-calendar/README.md), all parsing on the mirror and on the slot, with an extractor control. Produced: re-read on the tree at this head, ListView's calendar branch and ObjectView's generateViewSchema build the object-calendar node FLAT (...restCalendar spread onto the node, defaultView lifted), never under a calendar: container, so no producer-built node meets the narrowed door; no other non-test source names the object-calendar type as a producer; and the only runtime zod callers outside tests are the CLI validate / check commands, which now agree with os validate on the row where they disagreed before.
  5. The list VIEW's calendar block (CalendarConfig: .partial(), defaultView, the two alias arms, .passthrough()) and the other per-view-type blocks are untouched — RIGHT; the claim fenced them as a different contract.

Published TypeScript face (@object-ui/types):

  1. ObjectCalendarBlockConfig (exported from zod/objectql.zod.ts, re-exported by objectql.ts) and, through it, ObjectCalendarSchema.calendar: no index signature, startDateField: string required, the four other members with the slot's own member types, and dateField / endField as undefined-only arms (the z.never().optional() refusals) — RIGHT. Pinned by the Equal assertions and four live @ts-expect-error directives with a lit control; the directives are compiled, since tsconfig.test.json includes src/**/*.test.ts and the package's type-check script runs it. The Type Check check-run is green on this head, and the dev's ablation B (a consumer literal carrying defaultView failing against the rebuilt dist, control compiling) is read as declared.
  2. No other exported declaration moved: the objectql.ts diff is the member's docblock only (+23 / −22), and the .describe() on the zod member now states the required key and the refusal, which is what the member does.

Test and tooling (no published face):

  1. packages/types/src/__tests__/object-calendar-container-by-reference-6152.test.ts — RIGHT. Its first block is a precondition pin on the installed slot (wrapper chain, catchall, members, required start, no defaultView, a .passthrough() control on the view block), so a spec bump that moves the slot reds here by name; the census writes no count down and re-derives on every run. Its reads escape the package (examples, content, skills, docs, apps, READMEs), seeded from import.meta.url with a pnpm-workspace.yaml walk — a spelled form — and scripts/markdown-test-inputs.mjs gains the ADJUDICATED entry naming the reads and the walker, which markdown-test-inputs.test.ts demanded (red on 15019c511, green on this head). The Test shards and Test (dist pins) are green.

Prose faces (the seat's to judge, per the contract-review reference; read here only for consistency with the diff): the plugin-calendar.mdx CalendarConfig paragraph and the README fence comment now say the container is the spec's strict slot, startDateField required, a sixth key refused with unrecognized_keys, defaultView written flat, the aliases refused by name, and the list-view block a different contract still .passthrough() — each sentence is true on this head. Build Docs, Doc Snippet Type Check, Doc Fence Language Check, Doc Component Type Check and Internal Docs Link Check are green.

File surface against the claim 6061068534: objectql.zod.ts, objectql.ts, the new pin and the changeset are the claimed surface. Three files beyond its literal list — content/docs/plugins/plugin-calendar.mdx and packages/plugin-calendar/README.md (prose this head makes false) and scripts/markdown-test-inputs.mjs (the ledger entry the scripts suite requires) — are each forced by the change. Every excluded path holds: the list VIEW block is untouched, packages/plugin-calendar/src/** carries no diff, ListView.tsx is untouched. 713 changed lines; none of the 7 paths is a governed surface, and Governed Surface Queue Guard is success.

② Semver level

  • What the diff publishes: @object-ui/types only — a zod narrowing on the object-calendar arm (the tolerant face and safeValidateSchema on two inputs, the strict face on the missing start) and a TypeScript narrowing of ObjectCalendarBlockConfig / ObjectCalendarSchema.calendar. No widening on any face. @object-ui/plugin-calendar ships no source change (its README is outside the presence rule), and content/docs and scripts/ publish nothing.
  • .changeset/6152-object-calendar-container-strict.md: @object-ui/types minor, headed "Narrowed (breaking)", naming both refusals with the remedy for each (defaultView written on the node; startDateField required inside the block), the TypeScript face change (no index signature, required start), the kept by-name aliases, the census pin, and "what did not move" (the list view's block, the renderers' reads) — MATCHES what the diff publishes. The fixed release group prices a stated break as minor (objectui AGENTS.md: never major; the round-1 and round-3-to-8 precedent, each PASSed at tier). Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report and Changeset Claim Re-read are all success.
  • The Clause-②: line reads no on the claim, the PR body and the changeset — RIGHT, not a false declaration: the value answers whether the card widens an accept set or the public surface, and nothing here widens. Noted: the sharper spelling for a pure narrowing is no (narrowing); an absent arm declares no direction, which is the spelling rounds 7 and 8 PASSed at tier, and the break is carried by the ! title and the changeset body. Cosmetic.

③ Boundary flags

Dev-declared deviations (report 6062742416), each answered:

  • Files beyond the claim's literal list (the mdx paragraph, the README fence comment, the markdown-test-inputs.mjs entry) — accepted: each is forced by the change, the two prose edits replace sentences this head makes false, and the ledger entry is the scripts suite's own requirement. The seat may widen the claim's surface to the three paths.
  • startDateField becomes required inside the block — accepted, and required: following the slot by reference includes its required member, and leaving it optional would have kept the mirror accepting a block the slot refuses, the direction the round exists to close. The census reads zero authored blocks without it, and no producer writes the container.
  • The allDayField restatement dropped — accepted: same accept set as the slot's member (① 1), and its named carrier is closed.
  • The packages union not re-run on aaba24a2c — accepted: the second commit touches only scripts/markdown-test-inputs.mjs, whose suite was re-run there, and the check-runs on this head are the verdicts.
  • Model-free commit trailers and the session-URL PR footer — accepted: AGENTS.md's requirement, not a contract matter.
  • plugin-calendar/src/** not edited; ablation B injected transiently — accepted: the diff carries no such path.

open_questions: none declared, and none found.

Out-of-scope findings, judged:

  • The list VIEW's per-view-type blocks (KanbanConfig, CalendarConfig, GalleryConfig, TimelineConfig) stay .partial() plus .passthrough() while the installed spec's ListViewSchema blocks are strict — a real door of the same class (objectui accepting what the platform refuses), outside this round by the claim. It needs its own corpus census first, since view blocks are produced by app-shell's view helpers as well as authored. Carrier: this card's family, for the seat to book as a round or to file. Not an escalation for this head.
  • The ObjectCalendarConfig docblock in plugin-calendar/src/ObjectCalendar.tsx still says allDayField is "NOT a spec key" and that the view-level mirror "keeps .passthrough() explicitly for this" — stale since 17.5.0, not made false by this round, excluded path. Noted beside it: the plugin-calendar registration's calendar input row describes four keys and omits allDayField, also pre-existing. Carrier: the next PR that touches plugin-calendar/src.
  • The strict face already refused an unknown key inside the container — information, consistent with ① 3.
  • The verify-lock hold time — a box fact, not a defect.

Noted, not flagged:

  • The console parity pins sentence for object-calendar.calendar already calls the spec side "a strict object of exactly the five documented keys", so it holds on this head.
  • Round 10 (ObjectGridSchema.defaultFilters, the gantt / map flat filter) stays on the card, as the PR body and the round-8 landing state; the PR carries no closing keyword.

Escalations: none. No maintainer decision is needed for this head to land.

Implemented-by: claude/issue-6152-r9-calendar-container
Reviewed-by: session_01DBZ9bntPZ7VKyQNtJeNsgw

VERDICT: PASS


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 package: types plugin tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants