Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .changeset/9256-public-blocks-content-channels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
'@object-ui/types': minor
---

**BREAKING (shipped as `minor` — see below):** nineteen ADR-0080 public-block
arms and the dashboard widget slot's `metric-card` node now refuse an authored
`children` by name. None of these renderers reads the node's child list, so the
key rendered nothing, with no render-time error or warning and no element
(objectui#9256).

- `page:header`, `page:tabs`, `page:accordion`, `record:details`,
`record:highlights`, `record:related_list`, `record:path`, `record:activity`,
`record:discussion`, `record:history`, `record:quick_actions`,
`record:reference_rail`, `element:text`, `element:number`, `element:button`,
`element:divider`, `object-metric` and `object-master-detail-form`: `children`
and `body` are declared as by-name refusals on the zod arm, each kept a member.
This package has no TypeScript declaration of these nodes, so the zod face is
the only one that changes.
- `record:alert`: `children` only. Its `body` is the alert's message text, not a
content channel, and is left as it was.
- `page:tabs` and `page:accordion`: the node's own `children` only. Each item's
`children` in `items` stays live — that is what these blocks render.
- `metric-card` in a dashboard's `widgets`: `children?: never` and `body?: never`
on the TypeScript face (`DashboardWidgetSlotComponentSchema`), and both keys
refused by name on its zod twin. That twin is the first arm of the widget
slot's union, so the refusal reaches the author inside the union's
`invalid_union` issue at the widget's path, beside the strict widget schema's
`unrecognized_keys`; `objectui validate` prints it as one arm of two.

What moves for an author:

- `children` on any of these nodes parsed green (and, on `metric-card`, also
type-checked). It is now refused at `safeParse` time at its own path — on
`metric-card`, under the widget's `invalid_union` — and on
`DashboardWidgetSlotComponentSchema` at authoring time.
- `body` was already refused on all of them, by `BaseSchema`. Everywhere except
`record:alert`, the zod refusal message now names what the node renders
instead, where it used to point at `children`, which these nodes do not read
either.

No render behaviour changes: nothing read these keys, which is the whole reason
they could be refused.

Migration: each of these nodes renders from its own keys, so there is no channel
to move the content to. Put it in the key the node does render
(`element:text`'s `properties.content`, the item-level `children` of a
`page:tabs` or `page:accordion` item, `record:alert`'s `properties.body`), place
it beside the node in a container that reads `children` (`page:section`,
`page:card`), or drop it.

Also, text only: `CodeEditorSchema.children`'s docblock now names `onChange`
among the keys `CodeEditorRenderer` forwards to Monaco.

`minor` rather than `major` because this repo's version policy forbids `major`
in any changeset — one `fixed` group — and records `minor` plus an explicit
breaking note as the spelling for a breaking change here.
39 changes: 27 additions & 12 deletions packages/types/src/__tests__/content-channel-family-d-9256.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@
* RE-POINTED at a twin rather than inverted; what this card asserts — that it
* narrowed `children` on the chatbot faces and left their `body` alone — is
* unmoved.
* ⚠️ AMENDED AGAIN (objectui#9256, public-block slice): the hold-out on the
* two TWIN faces has ended too, and not by a decision on this card —
* objectui#6771 retired `body` on `BaseSchema`, so each twin now declares
* the same neither-channel tombstone on `body` that its `children` carries,
* pointing at `requestBody`. `body` is REFUSED on all three chatbot faces
* today; the two controls below assert that refusal (the mirror control)
* and its compile-time twin, and neither proves `body` parses anywhere.
*
* ## ⚠️ Half of this file is a COMPILE-TIME assertion and vitest CANNOT read it
*
Expand Down Expand Up @@ -301,17 +308,22 @@ const ROWS: ReadonlyArray<readonly [
['calendar-view', CalendarViewMirror as unknown as Mirror, ['body', 'children'], {}],
// ⚠️ ONE-SIDED ROW, and ⛔ not a two-sided reading (objectui#9659, carrying a
// contract-review residual on objectui#9639). The three rows below list `children`
// only, and on the two TWIN faces that still means what it always meant: `children`
// dead, `body` held out and LIVE, with the same-face LIVE CONTROL below proving it.
// On the PLAIN `chatbot` face it no longer does. Ruling A on objectui#8572 retired
// `ChatbotSchema.body` as an ADR-0049 tombstone for a DIFFERENT reason than this card's
// — a naming collision, not a dead content channel — and objectui#9639 landed it, so
// the plain face refuses BOTH channels today. Measured: `body` is ACCEPTED on
// `chatbot-enhanced` and `chatbot-floating`, REFUSED on `chatbot`.
// ⇒ the absence of `body` from the plain row means "not this card's to assert", ⛔ not
// "still live here", and objectui#9639 had to re-point both controls below at a twin
// precisely because no same-face control is available any more. The `body` half of the
// plain face is pinned by `node-recursion-point-8344.test.ts`, which owns objectui#8572.
// only. That no longer means `body` is live on any of them: `body` is REFUSED on all
// three faces today, for two different reasons.
// - PLAIN `chatbot`: ruling A on objectui#8572 retired `ChatbotSchema.body` as an
// ADR-0049 tombstone for a DIFFERENT reason than this card's — a naming collision,
// not a dead content channel — and objectui#9639 landed it.
// - The TWINS `chatbot-enhanced` / `chatbot-floating`: objectui#6771 retired `body`
// on `BaseSchema`, so each twin declares the same neither-channel tombstone on
// `body` that its `children` carries, pointing at `requestBody`. The LIVE CONTROL
// below asserts that refusal on `chatbot-enhanced`.
// Re-measured in the objectui#9256 public-block slice: `body` is REFUSED on
// `chatbot`, `chatbot-enhanced` and `chatbot-floating` alike. (This comment used to
// say `body` was ACCEPTED on the two twins; that stopped being true when the twins
// gained their `body` tombstone, and the control below already asserted the refusal.)
// ⇒ the absence of `body` from these rows means "not this card's row to assert", ⛔ not
// "still live here". The `body` half of the plain face is pinned by
// `node-recursion-point-8344.test.ts`, which owns objectui#8572.
// ⛔ Do not add `'body'` to the plain row to "fix" this: the message that row's
// assertions read is objectui#9256's, and the plain face's tombstone carries
// objectui#8572's instead — the row would go red on a true statement.
Expand Down Expand Up @@ -405,7 +417,10 @@ describe('objectui#9256 — CONTROLS: the node itself, and the held-out channel,
expect(Object.keys(mirror.shape)).toContain(key);
});

it('LIVE CONTROL — the chatbot family still accepts `body`, the channel held out of this card', () => {
// Named for what it asserts since the objectui#9256 public-block slice: the
// name used to say the family "still accepts `body`", which the body below has
// not asserted since the twins gained their `body` tombstone.
it('LIVE CONTROL — the chatbot twins REFUSE `body` too, pointing the author at `requestBody`', () => {
// The held-out channel, still held out — RE-POINTED, not inverted, by
// objectui#8572. This control was aimed at `ChatbotSchema`, whose own `body`
// the parity ledger recorded as "two different meanings of one key — a naming
Expand Down
Loading
Loading