Ruled on #13748 (2026-08-31, director batch #19, option A — fix both halves). This is the objectstack half; the objectui half (removing the invented due_date default) is objectui#7029.
The gap
packages/spec/src/ui/view.zod.ts:945 — allowedVisualizations is optional;
packages/spec/src/ui/view.zod.ts:1679 — the calendar: config block is optional;
- there is NO cross-field check tying the two together. A view declaring
appearance.allowedVisualizations: ['grid','calendar'] with no calendar: block passes metadata validation.
Downstream (measured on #13748, hotcrm pinned at @objectstack/* 17.1.0): the calendar toggle renders and is clickable, objectui invents startDateField: 'due_date' (app-shell ObjectView.tsx:2220), the renderer lands every record whose invented field is absent on "today" (ObjectCalendar.tsx:445), and the author gets a plausible-looking, fully wrong screen — all 9 leave requests piled on today's cell. The renderer's own refusal screen (ObjectCalendar.tsx:657) is unreachable because the synthesized config always looks complete.
Deliverable
A cross-field refinement on the list-view schema: when appearance.allowedVisualizations contains calendar, require the calendar: block with startDateField. Only startDateField is load-bearing — title resolution has the ADR-0079 display-name fallback chain; ⛔ do not require more than the renderer actually needs.
⚠️ Scope: calendar only — the measured defect. If timeline or another visualization has the same shape, that is a separate finding to measure first, not a rider here.
Pre-landing census (required — write the result into the PR)
Count existing views in the reference apps (hotcrm at minimum) that declare calendar without the block and currently render only via objectui's invented due_date guess. Each one flips from "coincidentally renders" to a build-time red — that is the ruled loud-over-silent direction, but the PR must name the population rather than discover it in CI.
Gates
Clause-②: YES — path limb packages/spec/src/**, content limb = accept-behavior tightening (previously-valid metadata becomes invalid). Dispatch at the contract-review tier; changeset marks the tightening.
Refs: #13748 (phenomenon record, closed by the ruling), objectui#7029 (runtime half), #13751 (unrelated navigation bug on the same surface, separately queued to objectui).
Ruled on #13748 (2026-08-31, director batch #19, option A — fix both halves). This is the objectstack half; the objectui half (removing the invented
due_datedefault) is objectui#7029.The gap
packages/spec/src/ui/view.zod.ts:945—allowedVisualizationsis optional;packages/spec/src/ui/view.zod.ts:1679— thecalendar:config block is optional;appearance.allowedVisualizations: ['grid','calendar']with nocalendar:block passes metadata validation.Downstream (measured on #13748, hotcrm pinned at
@objectstack/* 17.1.0): the calendar toggle renders and is clickable, objectui inventsstartDateField: 'due_date'(app-shellObjectView.tsx:2220), the renderer lands every record whose invented field is absent on "today" (ObjectCalendar.tsx:445), and the author gets a plausible-looking, fully wrong screen — all 9 leave requests piled on today's cell. The renderer's own refusal screen (ObjectCalendar.tsx:657) is unreachable because the synthesized config always looks complete.Deliverable
A cross-field refinement on the list-view schema: when
appearance.allowedVisualizationscontainscalendar, require thecalendar:block withstartDateField. OnlystartDateFieldis load-bearing — title resolution has the ADR-0079 display-name fallback chain; ⛔ do not require more than the renderer actually needs.calendaronly — the measured defect. Iftimelineor another visualization has the same shape, that is a separate finding to measure first, not a rider here.Pre-landing census (required — write the result into the PR)
Count existing views in the reference apps (hotcrm at minimum) that declare
calendarwithout the block and currently render only via objectui's inventeddue_dateguess. Each one flips from "coincidentally renders" to a build-time red — that is the ruled loud-over-silent direction, but the PR must name the population rather than discover it in CI.Gates
Clause-②: YES — path limb
packages/spec/src/**, content limb = accept-behavior tightening (previously-valid metadata becomes invalid). Dispatch at the contract-review tier; changeset marks the tightening.Refs: #13748 (phenomenon record, closed by the ruling), objectui#7029 (runtime half), #13751 (unrelated navigation bug on the same surface, separately queued to objectui).