From b581817aaeab730f404b13db283453d98d4ad757 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Aug 2026 12:36:59 +0000 Subject: [PATCH 1/3] docs(spec): re-anchor the `chartConfig` liveness evidence to what was actually measured (#7017) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `widgets.children.chartConfig`'s evidence claimed "chart-config bag forwarded to the chart renderer". Nothing ever measured that: the cited lines took the bag out of the widget and the line after them read exactly one key (`showLegend`, #3135), leaving the rest of `ChartConfigSchema` declared and inert. #5175 measured the real state — 1 of 14 keys reaching the renderer — and recorded that this sentence had already mis-steered the #5022 measurement for half its length. Re-anchored to today's objectui main rather than to either older state, because #7016 landed the enforce half in between. At objectui `@230ffd875`, `chartConfigPresentation` lowers NINE keys, one `if` per key, and the caller spreads the result onto the chart schema handed to the renderer; a DOM test pins each forwarded key. The remaining FIVE are named as unforwarded with the reason: `xAxis`/`yAxis`/`series` are derived from the dataset selection, `type` is answered by the widget's own `type` through `CHART_TYPE_MAP`, and `aria` has no reader on that path. The verdict is unchanged (`live`) — #5175's measurements never demanded a different one; they demanded evidence that supports it. No `children` are opened and no key is classified: the narrowing half for the five unforwarded keys is still an open maintainer decision on #5175, and the note says so. `verifiedAt` is stamped 2026-08-09 and `evidenceScope: "cross-repo"` added — the objectui realm was walked, at a pinned commit, which is what those two fields (#7024) exist to record. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01PoaTe9AqwDvcGiE92dUp5Z --- packages/spec/liveness/dashboard.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/spec/liveness/dashboard.json b/packages/spec/liveness/dashboard.json index 5cabfada16..b346342b37 100644 --- a/packages/spec/liveness/dashboard.json +++ b/packages/spec/liveness/dashboard.json @@ -47,9 +47,10 @@ }, "chartConfig": { "status": "live", - "verifiedAt": "2026-08-03", - "evidence": "objectui @91757a7: packages/plugin-dashboard/src/DatasetWidget.tsx:602-603 (chart-config bag forwarded to the chart renderer)", - "note": "chart presentation config; read on the dataset-bound chart path." + "verifiedAt": "2026-08-09", + "evidenceScope": "cross-repo", + "evidence": "objectui @230ffd875: packages/plugin-dashboard/src/DatasetWidget.tsx:380-429 — chartConfigPresentation lowers NINE of the declared keys, one `if` per key (:390 showLegend, :391 showDataLabels, :393 title, :395 subtitle, :397 description, :401 height, :403 annotations, :405 interaction, :419 and :425 colors, split into the positional palette and categoryColors); objectui: packages/plugin-dashboard/src/DatasetWidget.tsx:1091 calls it and :1118 spreads the result onto the chart schema handed to the renderer; objectui: packages/plugin-dashboard/src/__tests__/DatasetWidget.chartConfig.dom.test.tsx pins the DOM effect of each forwarded key", + "note": "chart presentation config on the dataset-bound chart path — read PER KEY, not forwarded as a bag. Of the 14 keys ChartConfigSchema declares, 9 reach the renderer (above) and 5 do not: xAxis/yAxis/series are derived from the dataset selection, so an authored value would shadow the derived binding; type is answered by the widget's own `type` through CHART_TYPE_MAP; aria has no reader on this path (AdvancedChartImpl has no `aria` prop and the ARIA injection reads the flat ariaLabel/ariaDescribedBy/role). This entry previously read 'chart-config bag forwarded to the chart renderer', which was never measured: at that time exactly one key (showLegend, #3135) was lowered, and the overstatement mis-steered the #5022 measurement for half its length. #5175 measured the real 1-of-14 state, #7016 forwarded the keys the chart block actually delivers, and the narrowing half for the 5 unforwarded keys is still open on #5175 — do not read this row as classifying them." }, "colorVariant": { "status": "dead", From 455931bb85fe949ac53bfdf45637fa2571db69b7 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Aug 2026 12:37:16 +0000 Subject: [PATCH 2/3] docs(spec): `allowAddTab` describes the session-scoped semantics #5236 A1 shipped (#6961) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #5073 promoted `allowAddTab` into `UserFiltersSchema` with a `.describe()` narrowed on purpose to "renders an add-tab affordance": the button objectui shipped carried no click handler, and promising "end users can add presets" would have advertised a capability the runtime did not deliver (PD#10). The maintainer ruled A1 — implement, session-scoped (#5236, 2026-08-06) — and objectui#3926 delivered it (merged, objectui main `cf5be4ec2`). The describe now states what is shipped: the affordance asks for a name and snapshots the filters currently applied as a new tab; the tab is SESSION-SCOPED — it lives only for the current mount and is never written back as metadata (ADR-0047) — and it carries a remove control the authored presets do not. #5073's applicability sentence is kept verbatim (page lists only; object views use `listViews` for named presets). The `⚠️ Scope of the promotion` TSDoc above the schema recorded the dead-button state as current, so it was realigned too: it now records how the narrowing was resolved instead of asserting a state that has not held since objectui#3926. Documentation semantics only — the ruling confirmed renderers do not read this text. `content/docs/references/ui/view.mdx` is the regenerated projection of the new describe (`gen:schema` + `gen:docs`); no schema key, enum member or strictness posture moves, so the acceptance face is byte-identical. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01PoaTe9AqwDvcGiE92dUp5Z --- content/docs/references/ui/view.mdx | 2 +- packages/spec/src/ui/view.zod.ts | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/content/docs/references/ui/view.mdx b/content/docs/references/ui/view.mdx index 8a6feaf536..1736db54e4 100644 --- a/content/docs/references/ui/view.mdx +++ b/content/docs/references/ui/view.mdx @@ -734,7 +734,7 @@ End-user quick-filter configuration (Airtable "User filters" parity) | **fields** | `{ field: string; label?: string \| Record; type?: Enum<'select' \| 'multi-select' \| 'boolean' \| 'date-range' \| 'text'>; options?: object[]; … }[]` | optional | Fields exposed as quick filters (dropdown/toggle elements) | | **tabs** | `{ name: string; label?: string \| Record; icon?: string; view?: string; … }[]` | optional | Named filter presets rendered as tabs (tabs element). Reuses ViewTabSchema | | **showAllRecords** | `boolean` | optional | Show an "All records" tab before the presets (tabs element) | -| **allowAddTab** | `boolean` | optional | Render an "add tab" affordance after the presets (tabs element). Page lists only — object views use `listViews` for named presets | +| **allowAddTab** | `boolean` | optional | Let end users add their own tab after the presets (tabs element): the affordance asks for a name and snapshots the filters currently applied as a new tab. SESSION-SCOPED — an added tab lives only for the current mount, is never written back as metadata (ADR-0047), and carries a remove control the authored presets do not. Page lists only — object views use `listViews` for named presets | --- diff --git a/packages/spec/src/ui/view.zod.ts b/packages/spec/src/ui/view.zod.ts index 714c9fbf35..09a94751ef 100644 --- a/packages/spec/src/ui/view.zod.ts +++ b/packages/spec/src/ui/view.zod.ts @@ -775,12 +775,19 @@ export const UserFilterFieldSchema = lazySchema(() => strictObject({ * `packages/spec` and objectui two sources of truth for one contract — the * fork #2231's derive-by-reference unification exists to prevent (PD#12). * - * ⚠️ Scope of the promotion: `allowAddTab` declares that the tab bar RENDERS an - * add-tab affordance. The button objectui renders today carries no click - * handler, so it is presentational — filed against the renderer as #5236, and - * deliberately NOT written into the `.describe()`, because a contract that - * promises "end users can add presets" would be advertising a capability the - * runtime does not deliver (PD#10). + * ⚠️ Scope of the promotion, and its RESOLUTION (#5236 → #6961). At promotion + * time `allowAddTab` was described as declaring only that the tab bar RENDERS + * an add-tab affordance: the button objectui rendered carried no click handler, + * so it was presentational, and the narrower wording was deliberate — a + * contract promising "end users can add presets" would have advertised a + * capability the runtime did not deliver (PD#10). The maintainer then ruled + * **A1 — implement, session-scoped** (#5236, 2026-08-06), objectui#3926 + * delivered it (`packages/plugin-list/src/UserFilters.tsx` — naming popover, + * snapshot of the applied filters, component state only, remove affordance on + * the added tab), and the `.describe()` below was upgraded back to the real + * semantics. The narrowing was the interim state the ruling closed, not the + * contract: the key now promises the behaviour, and PD#10 is satisfied by the + * delivery rather than by the hedge. * * ## What closing flips, and why that flip is wanted (批 6e's question) * @@ -814,7 +821,7 @@ export const UserFiltersSchema = lazySchema(() => strictObject({ showAllRecords: z.boolean().optional() .describe('Show an "All records" tab before the presets (tabs element)'), allowAddTab: z.boolean().optional() - .describe('Render an "add tab" affordance after the presets (tabs element). Page lists only — object views use `listViews` for named presets'), + .describe('Let end users add their own tab after the presets (tabs element): the affordance asks for a name and snapshots the filters currently applied as a new tab. SESSION-SCOPED — an added tab lives only for the current mount, is never written back as metadata (ADR-0047), and carries a remove control the authored presets do not. Page lists only — object views use `listViews` for named presets'), }).describe('End-user quick-filter configuration (Airtable "User filters" parity)')); /** From 32d19e7c26c8ae5f3718eb9bc6cdabd4e6dc1892 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 9 Aug 2026 12:37:17 +0000 Subject: [PATCH 3/3] chore(changeset): two self-descriptions re-anchored to measured reality (#7056) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One patch changeset for the sweep — both members land in `@objectstack/spec` and neither changes behaviour. Matches the predecessor of this criterion family (#7024 / PR #7032), whose prose-level spec entries were also patch. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01PoaTe9AqwDvcGiE92dUp5Z --- ...f-descriptions-answerable-to-reality-ii.md | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .changeset/self-descriptions-answerable-to-reality-ii.md diff --git a/.changeset/self-descriptions-answerable-to-reality-ii.md b/.changeset/self-descriptions-answerable-to-reality-ii.md new file mode 100644 index 0000000000..d1c60f3c08 --- /dev/null +++ b/.changeset/self-descriptions-answerable-to-reality-ii.md @@ -0,0 +1,55 @@ +--- +"@objectstack/spec": patch +--- + +docs(spec): two self-descriptions re-anchored to measured reality — the `chartConfig` liveness evidence and `allowAddTab`'s `.describe()` (#7017, #6961) + +Sweep card #7056. Both members are prose that stopped being answerable to the +code it describes: one **overstated** what a reader delivers, one **understated** +what a renderer now delivers. Zero acceptance-face change — no schema key, no +enum member, no strictness posture moves, and the only generated artifact that +shifts is the `.describe()` row in `content/docs/references/ui/view.mdx`. + +**#7017 — `widgets.children.chartConfig`'s evidence said "chart-config bag +forwarded", which was never measured.** The cited lines took the bag out of the +widget and the line after them read exactly one key (`showLegend`, #3135); the +rest of `ChartConfigSchema` stayed unforwarded. #5175 measured that (1 of 14 +keys reaching the renderer) and recorded that the wording had already mis-steered +the #5022 measurement for half its length. Since then #7016 landed the enforce +half in objectui, so the row is re-anchored to today's reality rather than to +either older state: at objectui `@230ffd875`, `chartConfigPresentation` lowers +**nine** keys one `if` per key — `showLegend`, `showDataLabels`, `title`, +`subtitle`, `description`, `height`, `annotations`, `interaction` and `colors` +(split into the positional palette and `categoryColors`) — and the caller spreads +the result onto the chart schema handed to the renderer, with a DOM test pinning +each forwarded key. The remaining **five** are named as unforwarded and why: +`xAxis` / `yAxis` / `series` are derived from the dataset selection, `type` is +answered by the widget's own `type` through `CHART_TYPE_MAP`, and `aria` has no +reader on that path. The entry also gains `verifiedAt: 2026-08-09` and +`evidenceScope: "cross-repo"` (the objectui realm was walked, at a pinned +commit), the two fields #7024 added for exactly this. + +The verdict is unchanged — `live`, as it was, now for a reason the evidence +actually supports. No `children` are opened and no key is classified: the +narrowing half for the five unforwarded keys is still an open maintainer decision +on #5175, and the note says so, so this row cannot be read as pre-empting it. + +**#6961 — `UserFiltersSchema.allowAddTab` still described the interim, +deliberately-narrowed contract.** #5073 promoted the key with a `.describe()` +that promised only that the tab bar *renders* an add-tab affordance, because the +button objectui shipped had no click handler — narrowing on purpose rather than +advertising a capability the runtime did not deliver (PD#10). The maintainer then +ruled **A1 — implement, session-scoped** (#5236, 2026-08-06) and objectui#3926 +delivered it. The describe now states the semantics that are actually shipped: +the affordance asks for a name and snapshots the filters currently applied as a +new tab; the tab is **session-scoped** — it lives only for the current mount and +is never written back as metadata (ADR-0047, which scopes an end user's filter +choices to the session) — and it carries a remove control the authored presets do +not. The applicability sentence #5073 wrote is kept verbatim: page lists only, +object views use `listViews` for named presets. The `⚠️ Scope of the promotion` +TSDoc above the schema, which recorded the dead-button state as current, now +records how it was resolved instead. + +The ruling confirmed renderers do not read this text, so this is documentation +semantics only: no runtime change, and the JSON-Schema baselines and the docs +site are regenerated with the describe.