From b6aba1c85176beec5885fe9c92bd971bb359fbff Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 22 Sep 2026 01:42:48 +0000 Subject: [PATCH 1/3] fix(spec): calendar binding warning describes the refusal, not the deleted literal fallback `VIEW_BINDING_BLOCKS`' calendar row and the `view/layout-without-binding` body both asserted a renderer fallback to literal `start_date` / `end_date`. Re-measured on objectui `main` at `0cf2d6644`: `ListView.tsx`'s calendar branch restates only declared bindings, and `ObjectCalendar`'s `getCalendarConfig` resolves `null` into the "Calendar configuration required" refusal screen. The warning body is now per type, keeps its prescription, and names `calendar.startDateField`; no severity moves. Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx Co-authored-by: Claude --- ...iew-binding-calendar-fallback-corrected.md | 18 +++ .../kernel/functional-completeness.test.ts | 55 +++++++- .../src/kernel/functional-completeness.ts | 119 ++++++++++++++++-- 3 files changed, 175 insertions(+), 17 deletions(-) create mode 100644 .changeset/17445-view-binding-calendar-fallback-corrected.md diff --git a/.changeset/17445-view-binding-calendar-fallback-corrected.md b/.changeset/17445-view-binding-calendar-fallback-corrected.md new file mode 100644 index 00000000000..7926b222e77 --- /dev/null +++ b/.changeset/17445-view-binding-calendar-fallback-corrected.md @@ -0,0 +1,18 @@ +--- +"@objectstack/spec": patch +--- + +`view/layout-without-binding` — the `calendar` warning no longer tells an author the renderer falls back to `start_date` / `end_date`, because objectui deleted those floors; it names the refusal screen the renderer shows instead, and the key that clears it (#17445). + +Two carriers asserted the same deleted behaviour: `VIEW_BINDING_BLOCKS`' calendar row in `src/kernel/functional-completeness.ts` (stated as *measured*, against "the built console 17.2.0") and the body of the warning `checkViewCompleteness` emits on that route. Re-measured on objectui `main` at `0cf2d6644` (2026-09-21), both halves of the path a `type: 'calendar'` list view takes: + +- `packages/plugin-list/src/ListView.tsx`, `case 'calendar'` — the two literal floors are gone (objectui#7029); the branch restates only bindings the view declared. +- `packages/plugin-calendar/src/ObjectCalendar.tsx` — `getCalendarConfig` resolves `null` with neither a `calendar` block nor a flat `startDateField`, and the component renders its "Calendar configuration required" refusal screen, which names `startDateField` (objectui#8170 corrected that screen: `titleField` is not required). + +So the old body was wrong twice — there is no fallback to literal field names, and the failure is not silent. What it was right about is the remedy, and that is the half the new body keeps: it names `calendar.startDateField`, `CalendarConfigSchema`'s one required key, and records that the event title resolves through the ADR-0079 display-name chain when `titleField` is omitted. The `fix` hint is unchanged. + +- **The message is now per type.** `VIEW_BINDING_MESSAGE` carries an entry for a type whose measured outcome is not the generic literal-fallback sentence; the other five types receive the generic body unchanged. A type that stops flooring gets an entry, never a reworded universal — the two carriers drifted apart once, and the map is what makes correcting both one edit. +- **No severity moves.** Whether a renderer that refuses BY NAME still deserves WARNING under ADR-0078 §1 is #16577's question; correcting false prose does not answer it, and nothing here pre-empts it. +- ⚠️ **The same reading found three sibling rows stale, and they are recorded rather than corrected** — out of this card's scope, and each changes what its row's severity rests on: `gantt`'s four floors are gone (objectui#7070, objectui#7499) and `ObjectGantt` refuses; `timeline`'s `created_at` floor is gone (objectui#7070) while its `titleField || 'name'` stands; `map`'s `locationField || 'location'` is gone on both faces (objectui#8169). The table now carries that re-measurement note so the three are not reused as current fact, and `kanban` / `tree` were re-read at the same ref and still say what they say. + +No schema moved, no export moved and no accept set moved: this corrects prose and one warning string. `Clause-②: no` diff --git a/packages/spec/src/kernel/functional-completeness.test.ts b/packages/spec/src/kernel/functional-completeness.test.ts index 125e926c577..81432157dc7 100644 --- a/packages/spec/src/kernel/functional-completeness.test.ts +++ b/packages/spec/src/kernel/functional-completeness.test.ts @@ -118,10 +118,17 @@ describe('checkFieldCompleteness — the verified inert shapes go red', () => { }); describe('checkViewCompleteness — layout bindings', () => { - // Six of the view `type` members carry a binding block. The renderer's - // fallback for every one is a literal field name (measured in objectui's - // ListView adapter — see the table's docblock), so the missing block is the - // same defect on all six, not a lesser one on the last three. + // Six of the view `type` members carry a binding block, and the missing + // block is the same defect on all six: the surface the author asked for is + // not the one that renders, while authoring reports success. + // + // ⛔ What it is NOT is one shared mechanism. This comment used to say the + // renderer's fallback for every one is a literal field name; objectui has + // been deleting those floors one view type at a time, and for `calendar` + // the renderer now REFUSES by name instead (#17445). The rule fires on all + // six either way — that is what this pin holds — and the per-type + // mechanism belongs to the table's docblock, which states each row's own + // measurement and which of them have gone stale. it.each(['kanban', 'calendar', 'gantt', 'timeline', 'map', 'tree'])('flags a %s view missing its block as a WARNING', (type) => { const f = only(checkViewCompleteness({ type }) as never); expect(f.rule).toBe(VIEW_LAYOUT_WITHOUT_BINDING); @@ -151,6 +158,46 @@ describe('checkViewCompleteness — layout bindings', () => { expect(checkViewCompleteness({ type: 'tree', tree: { parentField: 'parent' } })).toEqual([]); }); + it('the calendar body describes the REFUSAL, not a deleted literal fallback (#17445)', () => { + // Re-measured on objectui `main` at `0cf2d6644` (2026-09-21), both halves + // of the path: `ListView.tsx`'s `case 'calendar'` restates only declared + // bindings — objectui#7029 deleted the `startDateField || 'start_date'` / + // `endDateField || 'end_date'` floors — and `ObjectCalendar`'s + // `getCalendarConfig` resolves `null`, so the component renders its + // "Calendar configuration required" refusal screen. The body asserted + // those floors as the reason for the warning, which is what this pin + // stops from coming back. + const f = only(checkViewCompleteness({ type: 'calendar' }) as never); + expect(f.message).not.toContain('falls back to literal default field names'); + expect(f.message).toContain('Calendar configuration required'); + expect(f.message).toContain('ObjectCalendar.tsx'); + // ⛔ The half the correction had to PRESERVE: a warning an author meets at + // authoring time earns its place by naming the key to declare, not by + // reporting that something is missing. + expect(f.message).toContain('calendar.startDateField'); + expect(f.fix).toContain('startDateField'); + // Severity is deliberately untouched here. Whether a renderer that + // refuses BY NAME still deserves WARNING under ADR-0078 §1 is #16577's + // question; correcting false prose does not answer it. + expect(f.severity).toBe('warning'); + }); + + it('the five types with no per-type body keep the generic one — an override, not a rewrite', () => { + // ⚠️ An honest pin: it records WHICH body each type receives, NOT that + // the body is true of each. The table's re-measurement note says `gantt`, + // `timeline` and `map` inherit this sentence pending corrections of their + // own; `kanban` and `tree` were re-read at the same ref and still floor a + // literal. Correcting one of the three means adding an entry beside + // `calendar`'s and moving that type out of this list — a deliberate edit, + // which is the point. + for (const type of ['kanban', 'gantt', 'timeline', 'map', 'tree']) { + const f = only(checkViewCompleteness({ type }) as never); + expect(f.rule).toBe(VIEW_LAYOUT_WITHOUT_BINDING); + expect(f.message).toContain('falls back to literal default field names'); + expect(f.message).toContain(`A \`${type}\` view with no \`${type}\` block`); + } + }); + it('names the schema-required keys in the timeline prescription', () => { // `TimelineConfigSchema` requires exactly these two; the hint must not // send an author to declare a block the parser then refuses. diff --git a/packages/spec/src/kernel/functional-completeness.ts b/packages/spec/src/kernel/functional-completeness.ts index 5b89b1e1bd6..04ac4b5a4f7 100644 --- a/packages/spec/src/kernel/functional-completeness.ts +++ b/packages/spec/src/kernel/functional-completeness.ts @@ -216,13 +216,23 @@ export function checkFieldCompleteness(def: unknown): CompletenessFinding[] { } /** - * The layout-specific config block each view type is inert without. The - * renderer falls back to LITERAL field names, so a view without its block - * renders — but empty — on any object that does not happen to declare those - * exact fields. Degrades rather than fully dies: WARNING, not error - * (ADR-0078 §1). - * - * Every entry names its measured renderer fallback. The verify-then-enforce + * The layout-specific config block each view type is inert without. What a + * view WITHOUT its block actually does is per type — a fallback to LITERAL + * field names that renders empty on any object not happening to declare + * them, a binding inferred from the object, or a refusal screen — so every + * row below states its own measured outcome and names the reading it came + * from. ⛔ Do not generalise one row to the next, and ⛔ do not restate a row + * as fact without re-reading the renderer: objectui is deleting these floors + * one view type at a time, so a row is only as true as its last measurement. + * + * Either way the author asked for a surface that does not render, while + * authoring reports success: WARNING, not error (ADR-0078 §1). ⚠️ The + * rubric's other half — refuse what renders NOTHING — is what [#16577] is + * asking of this route now that some of these renderers answer with a + * refusal. This table records what was measured and deliberately does not + * pre-empt that ruling: nothing here moves a severity. + * + * Every entry names its measured renderer binding. The verify-then-enforce * gate this table sits behind (the audit's Tier-A had named only the first * three) was discharged for `timeline` / `map` / `tree` by two measurements * on record: the per-type props builder of objectui's ListView adapter @@ -230,10 +240,27 @@ export function checkFieldCompleteness(def: unknown): CompletenessFinding[] { * console 17.2.0), and a both-direction ablation on `os validate` — deleting * a `timeline` block left `warnings: []` with `valid: true`, deleting the * sibling `gantt` block warned as designed. The gate was working; the table - * was short. + * was short. ⚠️ That reading is dated — console 17.2.0 — and is no longer + * current for every row; see the re-measurement note under the table. * * - `kanban` → `groupBy = groupByField || groupField || ` - * - `calendar` → `startDateField || 'start_date'`, `endDateField || 'end_date'` + * - `calendar` → NO fallback, and no silence: the view is bound to nothing + * and the renderer says so on screen. Measured on objectui `main` at + * `0cf2d6644` (2026-09-21), both halves of the path a `type: 'calendar'` + * list view takes. ① `ListView.tsx`'s `case 'calendar'` restates only the + * bindings the view DECLARED — the `startDateField || 'start_date'` and + * `endDateField || 'end_date'` floors this row used to name were deleted + * by objectui#7029, whose own comment records that they were "field names + * no view had written and most objects do not carry". ② `ObjectCalendar`'s + * `getCalendarConfig` returns `null` with neither a `calendar` block nor a + * flat `startDateField`, and the `if (!calendarConfig)` arm renders the + * "Calendar configuration required" refusal screen, which names + * `startDateField` as the key to declare (objectui#8170 corrected that + * screen's second clause: `titleField` is NOT required). So the loss is + * total rather than partial — every record, on every object — and the + * author is told at render time as well as here. This repo already records + * the same deletion one door over: the #13817 check in `../ui/view.zod.ts` + * names objectui#7029 as its runtime half. * - `gantt` → `startDateField || 'start_date'`, `endDateField || 'end_date'`, * `progressField || 'progress'`, `dependenciesField || 'dependencies'` — * fails CLOSED (`null` unless both dates resolve): a blank chart @@ -255,6 +282,30 @@ export function checkFieldCompleteness(def: unknown): CompletenessFinding[] { * still renders flat — the parent pointer has its own rule, * {@link VIEW_TREE_WITHOUT_PARENT_FIELD}, below. * + * ⚠️ RE-MEASUREMENT [#17445], objectui `main` at `0cf2d6644` (2026-09-21). + * The `calendar` row above is the one that card corrected. The same reading + * found three siblings still naming floors objectui has since deleted, and + * they are RECORDED here rather than corrected, because that correction is a + * separate finding to rule on and not a rider (the card scoped itself to + * `calendar`, and each of the three changes what its row's severity rests + * on): + * + * - `gantt` — all four floors gone (objectui#7070 for the dates, #7499 for + * `progressField` / `dependenciesField`); `ObjectGantt` REFUSES an absent + * date binding rather than drawing the blank chart this row describes. + * - `timeline` — `startDateField || 'created_at'` gone (objectui#7070 step + * ③, on the ruling 日期轴永不虚构), with a refusal screen in its place; + * the `titleField || 'name'` half of the row still stands. + * - `map` — `locationField || 'location'` gone on BOTH faces (objectui#8169): + * `ObjectMap` renders "Map configuration required" instead of an empty map. + * ⛔ That literal is also quoted in this rule's `map`-specific message + * below and pinned by `functional-completeness.test.ts`, so correcting the + * row means correcting the message and the pin together. + * + * `kanban` and `tree` were re-read at the same ref and still say what they + * say. ⛔ Until the three above are corrected, do not cite them — or the + * warning text they feed — as a current measurement. + * * `gallery` is measured too (`titleField || 'name'`) and is deliberately NOT * here: `GalleryConfigSchema` requires no key, so "has a `gallery` block" * would assert nothing an author could act on, and the fallback mis-titles @@ -296,6 +347,51 @@ const VIEW_BINDING_FIX: Readonly> = { tree: "tree: { parentField: '', labelField: '' }", }; +/** + * The body of the `view/layout-without-binding` warning for a type whose + * measured outcome is NOT the generic literal-fallback sentence + * ({@link unboundBlockMessage}). A type with no entry here gets the generic + * body, so this map is the exception list, not a second copy of the table. + * + * ⛔ An entry is written from a reading of the renderer, never from the row + * above it — the two carriers went out of sync once already, which is what + * this map exists to make cheap to fix: {@link VIEW_BINDING_BLOCKS}'s + * `calendar` row and this rule's message BOTH asserted a + * `startDateField || 'start_date'` fallback that objectui#7029 had deleted, + * and correcting one without the other would have left the author reading the + * stale half. + * + * ⚠️ What the correction must PRESERVE is the prescription. The old sentence + * was wrong about the mechanism and still right about the remedy, and the + * remedy is the whole value of a warning an author meets at authoring time: + * it says which key to declare, not merely that something is missing. + */ +const VIEW_BINDING_MESSAGE: Readonly> = { + calendar: + 'A `calendar` view with no `calendar` block declares no date axis, and the renderer does ' + + 'not invent one: objectui\'s `ListView.tsx` calendar branch forwards only the bindings the ' + + 'view DECLARED, so `getCalendarConfig` (objectui `ObjectCalendar.tsx`) resolves `null` and ' + + 'the view renders its "Calendar configuration required" refusal screen instead of records ' + + '— it parses and publishes clean, then shows no event on any object, not just on one that ' + + 'happens to lack a field. Declare `calendar.startDateField`, the block\'s one required key; ' + + 'the event title resolves through the ADR-0079 record display-name chain when `titleField` ' + + 'is omitted.', +}; + +/** + * The generic body — a view type whose renderer still floors the binding at a + * literal field name. It is what the five types with no + * {@link VIEW_BINDING_MESSAGE} entry receive, and the re-measurement note on + * {@link VIEW_BINDING_BLOCKS} says which of those five it is still true of + * (`kanban`, `tree`) and which three inherit it pending their own correction. + * ⛔ So it is the DEFAULT, never a universal: a type that stops flooring gets + * an entry above, not a reworded sentence here. + */ +const unboundBlockMessage = (type: string, block: string): string => + `A \`${type}\` view with no \`${block}\` block is bound to nothing: the renderer falls ` + + 'back to literal default field names, which works only if the object happens to declare ' + + 'them — on any other object the view renders empty while authoring reports success.'; + const isNonEmptyString = (v: unknown): v is string => typeof v === 'string' && v.length > 0; /** A `map` block's coordinate binding — either of the two forms its schema documents. */ @@ -514,10 +610,7 @@ export function checkViewCompleteness(view: unknown, boundObject?: unknown): Com rule: VIEW_LAYOUT_WITHOUT_BINDING, severity: 'warning', path: block, - message: - `A \`${type}\` view with no \`${block}\` block is bound to nothing: the renderer falls ` - + 'back to literal default field names, which works only if the object happens to declare ' - + 'them — on any other object the view renders empty while authoring reports success.', + message: VIEW_BINDING_MESSAGE[type] ?? unboundBlockMessage(type, block), fix: VIEW_BINDING_FIX[type], }); } else if (type === 'map' && isRec(view.map) && !hasMapCoordinateBinding(view.map)) { From 6f1b457d4836dbea0a7cdbad320a984f1487505a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 22 Sep 2026 01:57:44 +0000 Subject: [PATCH 2/3] docs(spec): record that the calendar re-measurement holds at the objectui pin too The two reads behind the corrected row are byte-identical at `.objectui-sha` = `87af769e9`, so the row describes the console this repo ships and not only objectui's head. The asserting spelling wires the dependency edge `check:objectui-pin-citations` enforces: the next pin bump fails loudly on this record instead of letting it rot. Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx Co-authored-by: Claude --- packages/spec/src/kernel/functional-completeness.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/spec/src/kernel/functional-completeness.ts b/packages/spec/src/kernel/functional-completeness.ts index 04ac4b5a4f7..6c45d2ad76e 100644 --- a/packages/spec/src/kernel/functional-completeness.ts +++ b/packages/spec/src/kernel/functional-completeness.ts @@ -258,9 +258,11 @@ export function checkFieldCompleteness(def: unknown): CompletenessFinding[] { * `startDateField` as the key to declare (objectui#8170 corrected that * screen's second clause: `titleField` is NOT required). So the loss is * total rather than partial — every record, on every object — and the - * author is told at render time as well as here. This repo already records - * the same deletion one door over: the #13817 check in `../ui/view.zod.ts` - * names objectui#7029 as its runtime half. + * author is told at render time as well as here. Both reads are identical + * at the pin this repo builds against (`.objectui-sha` = `87af769e9`), so + * this describes the console this repo SHIPS and not only objectui's head. + * This repo already records the same deletion one door over: the #13817 + * check in `../ui/view.zod.ts` names objectui#7029 as its runtime half. * - `gantt` → `startDateField || 'start_date'`, `endDateField || 'end_date'`, * `progressField || 'progress'`, `dependenciesField || 'dependencies'` — * fails CLOSED (`null` unless both dates resolve): a blank chart From 4530d21191dc283a5badc9012cba3951aeff1cc5 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 22 Sep 2026 02:38:16 +0000 Subject: [PATCH 3/3] docs(spec): record #16577 as ruled, not pending, in the calendar carriers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The three in-tree carriers this PR added deferred the calendar route's severity to #16577 as an open question. It is not open: #16577 ruled B on 2026-09-11 (comment 5634033966, card closed completed) and fixed the route at warning-class under ADR-0078 §1 -- reasoning from exactly the premise this PR re-measures, that both doors are loud (the os validate warning, and ObjectCalendar's reachable refusal screen after objectui deleted the start_date / end_date floors). So the corrected row is not a change whose severity consequence is deferred; it is the evidence the standing ruling already rests on. The changeset ships into packages/spec/CHANGELOG.md, which is where the previous stale claim survived, so a second stale claim there would reproduce the defect this PR exists to correct. Also adds one clause to the calendar row: the quoted refusal literal is the text rendered AT THE PIN. objectui#10101 landed after the pin and moved it into a tt('calendar.configRequired', ...) default, so a non-English locale on objectui's head renders other words. Harmless for the console this repo ships, and the asserting pin citation below it is what reds at the next bump. Prose only: no schema, no severity, no behaviour, no new assertion. Claude-Session: https://claude.ai/code/session_01UDXER3sdqfeVYpEWZs5mZx Co-authored-by: Claude --- ...iew-binding-calendar-fallback-corrected.md | 2 +- .../kernel/functional-completeness.test.ts | 10 +++++--- .../src/kernel/functional-completeness.ts | 23 +++++++++++++++---- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/.changeset/17445-view-binding-calendar-fallback-corrected.md b/.changeset/17445-view-binding-calendar-fallback-corrected.md index 7926b222e77..614617e9cb6 100644 --- a/.changeset/17445-view-binding-calendar-fallback-corrected.md +++ b/.changeset/17445-view-binding-calendar-fallback-corrected.md @@ -12,7 +12,7 @@ Two carriers asserted the same deleted behaviour: `VIEW_BINDING_BLOCKS`' calenda So the old body was wrong twice — there is no fallback to literal field names, and the failure is not silent. What it was right about is the remedy, and that is the half the new body keeps: it names `calendar.startDateField`, `CalendarConfigSchema`'s one required key, and records that the event title resolves through the ADR-0079 display-name chain when `titleField` is omitted. The `fix` hint is unchanged. - **The message is now per type.** `VIEW_BINDING_MESSAGE` carries an entry for a type whose measured outcome is not the generic literal-fallback sentence; the other five types receive the generic body unchanged. A type that stops flooring gets an entry, never a reworded universal — the two carriers drifted apart once, and the map is what makes correcting both one edit. -- **No severity moves.** Whether a renderer that refuses BY NAME still deserves WARNING under ADR-0078 §1 is #16577's question; correcting false prose does not answer it, and nothing here pre-empts it. +- **No severity moves — the severity is already ruled.** #16577 ruled **B** on 2026-09-11 (comment `5634033966`, card closed `completed`): the `type: 'calendar'` route stays warning-class under ADR-0078 §1, and it stays there *because* both doors are loud — loud at `os validate` (this warning) and loud at render (objectui#7029 deleted the `start_date` / `end_date` floors; `getCalendarConfig` returns `null` and the named refusal screen is reachable). That is exactly the premise re-measured here, so the corrected row is the evidence the standing ruling rests on, not a change whose severity consequence is pending. - ⚠️ **The same reading found three sibling rows stale, and they are recorded rather than corrected** — out of this card's scope, and each changes what its row's severity rests on: `gantt`'s four floors are gone (objectui#7070, objectui#7499) and `ObjectGantt` refuses; `timeline`'s `created_at` floor is gone (objectui#7070) while its `titleField || 'name'` stands; `map`'s `locationField || 'location'` is gone on both faces (objectui#8169). The table now carries that re-measurement note so the three are not reused as current fact, and `kanban` / `tree` were re-read at the same ref and still say what they say. No schema moved, no export moved and no accept set moved: this corrects prose and one warning string. `Clause-②: no` diff --git a/packages/spec/src/kernel/functional-completeness.test.ts b/packages/spec/src/kernel/functional-completeness.test.ts index 81432157dc7..5a8a246d85c 100644 --- a/packages/spec/src/kernel/functional-completeness.test.ts +++ b/packages/spec/src/kernel/functional-completeness.test.ts @@ -176,9 +176,13 @@ describe('checkViewCompleteness — layout bindings', () => { // reporting that something is missing. expect(f.message).toContain('calendar.startDateField'); expect(f.fix).toContain('startDateField'); - // Severity is deliberately untouched here. Whether a renderer that - // refuses BY NAME still deserves WARNING under ADR-0078 §1 is #16577's - // question; correcting false prose does not answer it. + // Severity is untouched because it is already RULED, not because the + // question is open: #16577 ruled B (comment `5634033966`, card closed + // `completed` 2026-09-11) — a `calendar` route that refuses BY NAME + // stays warning-class under ADR-0078 §1, on the reasoning that BOTH + // doors are loud, `os validate` and render. That is the reading the + // assertions above re-measure, so this pin carries the ruling's + // evidence rather than standing in for a pending one. expect(f.severity).toBe('warning'); }); diff --git a/packages/spec/src/kernel/functional-completeness.ts b/packages/spec/src/kernel/functional-completeness.ts index 6c45d2ad76e..2c06ab40226 100644 --- a/packages/spec/src/kernel/functional-completeness.ts +++ b/packages/spec/src/kernel/functional-completeness.ts @@ -227,10 +227,18 @@ export function checkFieldCompleteness(def: unknown): CompletenessFinding[] { * * Either way the author asked for a surface that does not render, while * authoring reports success: WARNING, not error (ADR-0078 §1). ⚠️ The - * rubric's other half — refuse what renders NOTHING — is what [#16577] is - * asking of this route now that some of these renderers answer with a - * refusal. This table records what was measured and deliberately does not - * pre-empt that ruling: nothing here moves a severity. + * rubric's other half — refuse what renders NOTHING — was put to [#16577] + * for the `type: 'calendar'` route and is SETTLED, not pending: ruled B + * (director seat, comment `5634033966`; the card closed `completed` on + * 2026-09-11), the route STAYS warning-class and is carried at `warning` by + * the table below. The ruling turns on BOTH doors being loud — loud at + * `os validate` (this warning) and loud at render (objectui#7029 deleted the + * `'start_date'` / `'end_date'` floors; `ObjectCalendar.getCalendarConfig` + * returns `null` and the named refusal screen is reachable) — which is + * exactly what the `calendar` row below now measures. So this table moves no + * severity because the severity is already RULED, and the corrected row is + * the evidence that ruling rests on. ⛔ Reopening it takes a new ruling, + * not a re-read; a row going stale is a reason to re-measure the ROW. * * Every entry names its measured renderer binding. The verify-then-enforce * gate this table sits behind (the audit's Tier-A had named only the first @@ -256,7 +264,12 @@ export function checkFieldCompleteness(def: unknown): CompletenessFinding[] { * flat `startDateField`, and the `if (!calendarConfig)` arm renders the * "Calendar configuration required" refusal screen, which names * `startDateField` as the key to declare (objectui#8170 corrected that - * screen's second clause: `titleField` is NOT required). So the loss is + * screen's second clause: `titleField` is NOT required). ⚠️ That literal + * is the text RENDERED at the pin: objectui#10101 landed AFTER the pin and + * moved it into a `tt('calendar.configRequired', …)` default, so on + * objectui's head a non-English locale renders other words for the same + * screen. Harmless for the console this repo ships — and the pin citation + * below is what reds when the pin moves past it. So the loss is * total rather than partial — every record, on every object — and the * author is told at render time as well as here. Both reads are identical * at the pin this repo builds against (`.objectui-sha` = `87af769e9`), so