From 5165a77370213f57be2b9ab644eb7ba168783140 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 28 Sep 2026 23:00:18 +0000 Subject: [PATCH 1/3] spec(object-timeline): items' describe stops saying "the author owns the item shape" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ObjectTimelinePropsSchema.items stays z.array(z.unknown()) — no schema-shape change. Its describe now names the actual owner: each element is objectui's declared timeline element, @object-ui/types's TimelineFeedItem (feed, variant absent / vertical / horizontal) or TimelineGanttItem (a gantt row, variant: 'gantt'), selected by the node's own variant, whose strict authoring face refuses a mismatched arm and any undeclared key. Regenerated content/docs/references/ui/component.mdx via gen:schema && gen:docs. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx --- .../20283-object-timeline-items-element-owner-describe.md | 7 +++++++ content/docs/references/ui/component.mdx | 2 +- packages/spec/src/ui/component.zod.ts | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 .changeset/20283-object-timeline-items-element-owner-describe.md diff --git a/.changeset/20283-object-timeline-items-element-owner-describe.md b/.changeset/20283-object-timeline-items-element-owner-describe.md new file mode 100644 index 00000000000..ee7f38a01f5 --- /dev/null +++ b/.changeset/20283-object-timeline-items-element-owner-describe.md @@ -0,0 +1,7 @@ +--- +"@objectstack/spec": patch +--- + +`ObjectTimelinePropsSchema.items`' describe no longer says "the author owns the item shape". `items` stays `z.array(z.unknown())` — no schema-shape change — but the sentence now names the actual owner: each element is objectui's declared timeline element, `@object-ui/types`'s `TimelineFeedItem` (feed, `variant` absent / `vertical` / `horizontal`) or `TimelineGanttItem` (a gantt row, `variant: 'gantt'`), selected by the node's own `variant`, whose strict authoring face refuses a mismatched arm and any undeclared key. + +The element union and its zod mirror are declared and enforced entirely inside objectui's `packages/types` — nothing in this package imports or re-declares it, so this is a documentation-only correction, not a value-tightening. Value tightening (declaring the arms in this schema instead of `z.unknown()`) stays a later ratchet with its own inventory. diff --git a/content/docs/references/ui/component.mdx b/content/docs/references/ui/component.mdx index fa285bc05d5..ed1d623f3e7 100644 --- a/content/docs/references/ui/component.mdx +++ b/content/docs/references/ui/component.mdx @@ -991,7 +991,7 @@ View filter rule | **sort** | `{ field: string; order: Enum<'asc' \| 'desc'> }[]` | optional | Row order for the fetched entries — the SortItem array form `[{ field, order }, ...]`, the one sort orthography every declared `sort` door on this platform shares; lowered to the wire `$orderby`. The legacy string clause (`name desc`) is refused — see migration `object-block-sort-item-array` | | **limit** | `integer` | optional | Maximum number of records loaded onto the rail (row cap); lowered to the query's top-level `$top` (renderer default 100). A timeline renders one rail with no pagination control, so this is the author's window rather than a page size | | **data** | `any[]` | optional | Pre-fetched records — read FIRST as the rail's row source, ahead of the data-scope binding and the fetch, and composed into entries through the same `timeline` field bindings a fetched row takes; authoring it suppresses the object query entirely. Distinct from `items`, which is the already-composed entry shape and wins over this key when both are written | -| **items** | `any[]` | optional | Static inline entries — read ahead of every record source, `data` above included, and bypasses the object query entirely (the renderer becomes a pass-through and the author owns the item shape) | +| **items** | `any[]` | optional | Static inline entries — read ahead of every record source, `data` above included, and bypasses the object query entirely (the renderer becomes a pass-through). Each element is objectui's declared timeline element, `@object-ui/types`'s `TimelineFeedItem` (feed, `variant` absent / `vertical` / `horizontal`) or `TimelineGanttItem` (a gantt row, `variant: 'gantt'`), selected by this node's `variant`; objectui's strict authoring face refuses a mismatched arm and undeclared keys | | **variant** | `Enum<'vertical' \| 'horizontal' \| 'gantt'>` | optional | Rail layout (renderer default `vertical`). ⚠️ `gantt` needs authored `items`: the object-bound path composes flat feed entries, which the gantt branch cannot draw, and refuses that combination with a named diagnostic instead of drawing an empty chart | | **dateFormat** | `Enum<'short' \| 'long' \| 'iso'>` | optional | How each entry's date is rendered (renderer default `short`): `short` / `long` are locale-formatted, `iso` is the locale-free machine form | | **rowLabel** | `string` | optional | Header label for the gantt row column — read by the gantt branch only, which on this block needs authored `items` | diff --git a/packages/spec/src/ui/component.zod.ts b/packages/spec/src/ui/component.zod.ts index d133710476d..8ba622f0312 100644 --- a/packages/spec/src/ui/component.zod.ts +++ b/packages/spec/src/ui/component.zod.ts @@ -5207,7 +5207,7 @@ export const ObjectTimelinePropsSchema = lazySchema(() => strictObject({ data: z.array(z.unknown()).optional() .describe("Pre-fetched records — read FIRST as the rail's row source, ahead of the data-scope binding and the fetch, and composed into entries through the same `timeline` field bindings a fetched row takes; authoring it suppresses the object query entirely. Distinct from `items`, which is the already-composed entry shape and wins over this key when both are written"), items: z.array(z.unknown()).optional() - .describe('Static inline entries — read ahead of every record source, `data` above included, and bypasses the object query entirely (the renderer becomes a pass-through and the author owns the item shape)'), + .describe("Static inline entries — read ahead of every record source, `data` above included, and bypasses the object query entirely (the renderer becomes a pass-through). Each element is objectui's declared timeline element, `@object-ui/types`'s `TimelineFeedItem` (feed, `variant` absent / `vertical` / `horizontal`) or `TimelineGanttItem` (a gantt row, `variant: 'gantt'`), selected by this node's `variant`; objectui's strict authoring face refuses a mismatched arm and undeclared keys"), variant: z.enum(['vertical', 'horizontal', 'gantt']).optional() .describe("Rail layout (renderer default `vertical`). ⚠️ `gantt` needs authored `items`: the object-bound path composes flat feed entries, which the gantt branch cannot draw, and refuses that combination with a named diagnostic instead of drawing an empty chart"), dateFormat: z.enum(['short', 'long', 'iso']).optional() From 019752324722f845bcf3b2758f9d8862de4e1491 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 00:41:37 +0000 Subject: [PATCH 2/3] fix(spec): object-timeline items describe drops the false refusal claim Round-2 correction: the previous describe claimed objectui's strict authoring face refuses a mismatched arm and undeclared keys for object-timeline. Measured false at the objectui pin dd3f7e1be3561d63267d7162f3fc0ac52e72834d: objectui's zod authoring face (AnyComponentSchema / StrictAnyComponentSchema, a z.discriminatedUnion on `type`) has no object-timeline arm at all. A live StrictAnyComponentSchema.safeParse({ type: 'object-timeline', items: [...] }) against objectui's built types at this pin returns invalid_union / "No matching discriminator" on both faces -- not an arm/key judgment. The arm-selection and unrecognized_keys mechanism lives on the sibling `timeline` zod node (TimelineSchema's superRefine), which object-timeline types against only in TypeScript (ObjectTimeline.tsx's schema: TimelineSchema & { ... }), never at runtime. items stays z.array(z.unknown()) -- no schema-shape change. The describe's second sentence now says only the measured-true part: each element is objectui's declared timeline element, TimelineFeedItem or TimelineGanttItem, the arm the node's variant selects. Regenerated content/docs/references/ui/component.mdx via gen:schema && gen:docs. Corrected the changeset prose the same way and recorded the correction in it. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx --- .../20283-object-timeline-items-element-owner-describe.md | 6 ++++-- content/docs/references/ui/component.mdx | 2 +- packages/spec/src/ui/component.zod.ts | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.changeset/20283-object-timeline-items-element-owner-describe.md b/.changeset/20283-object-timeline-items-element-owner-describe.md index ee7f38a01f5..dbe704f0ced 100644 --- a/.changeset/20283-object-timeline-items-element-owner-describe.md +++ b/.changeset/20283-object-timeline-items-element-owner-describe.md @@ -2,6 +2,8 @@ "@objectstack/spec": patch --- -`ObjectTimelinePropsSchema.items`' describe no longer says "the author owns the item shape". `items` stays `z.array(z.unknown())` — no schema-shape change — but the sentence now names the actual owner: each element is objectui's declared timeline element, `@object-ui/types`'s `TimelineFeedItem` (feed, `variant` absent / `vertical` / `horizontal`) or `TimelineGanttItem` (a gantt row, `variant: 'gantt'`), selected by the node's own `variant`, whose strict authoring face refuses a mismatched arm and any undeclared key. +`ObjectTimelinePropsSchema.items`' describe no longer says "the author owns the item shape". `items` stays `z.array(z.unknown())` — no schema-shape change — but the sentence now names the actual owner: each element is objectui's declared timeline element, `@object-ui/types`'s `TimelineFeedItem` (`variant` absent / `vertical` / `horizontal`) or `TimelineGanttItem` (`variant: 'gantt'`), the arm this node's `variant` selects. -The element union and its zod mirror are declared and enforced entirely inside objectui's `packages/types` — nothing in this package imports or re-declares it, so this is a documentation-only correction, not a value-tightening. Value tightening (declaring the arms in this schema instead of `z.unknown()`) stays a later ratchet with its own inventory. +The element union is declared entirely inside objectui's `packages/types` (`TimelineFeedItem` / `TimelineGanttItem`, plain TypeScript interfaces) — nothing in this package imports or re-declares it, so this is a documentation-only correction, not a value-tightening. Value tightening (declaring the arms in this schema instead of `z.unknown()`) stays a later ratchet with its own inventory. + +Correction (round 2): the first cut of this describe also claimed "objectui's strict authoring face refuses a mismatched arm and undeclared keys" for this block. Measured false at the pin — objectui's zod authoring face has no `object-timeline` arm at all (`AnyComponentSchema` / `StrictAnyComponentSchema` in `packages/types/src/zod/index.zod.ts` are a `z.discriminatedUnion('type', …)` whose member literals include `object-grid` / `object-form` / `object-view` / `object-map` / `object-tree` / `object-gantt` / `object-calendar` / `object-kanban` / `object-chart` / `object-gallery` / `object-data-table` and, separately, `timeline` — never `object-timeline`); a live `StrictAnyComponentSchema.safeParse({ type: 'object-timeline', items: […] })` against objectui's built types at this pin returns `invalid_union` / "No matching discriminator" on both faces, not an arm/key judgment. The arm-selection and `unrecognized_keys` mechanism this describe now names lives on the sibling `timeline` zod node (`TimelineSchema`'s `.superRefine`), which `object-timeline` types against only in TypeScript (`ObjectTimeline.tsx`'s `schema: TimelineSchema & { … }`), not at runtime. The refusal clause is dropped; nothing else about the route changed. diff --git a/content/docs/references/ui/component.mdx b/content/docs/references/ui/component.mdx index ed1d623f3e7..825a9343ef4 100644 --- a/content/docs/references/ui/component.mdx +++ b/content/docs/references/ui/component.mdx @@ -991,7 +991,7 @@ View filter rule | **sort** | `{ field: string; order: Enum<'asc' \| 'desc'> }[]` | optional | Row order for the fetched entries — the SortItem array form `[{ field, order }, ...]`, the one sort orthography every declared `sort` door on this platform shares; lowered to the wire `$orderby`. The legacy string clause (`name desc`) is refused — see migration `object-block-sort-item-array` | | **limit** | `integer` | optional | Maximum number of records loaded onto the rail (row cap); lowered to the query's top-level `$top` (renderer default 100). A timeline renders one rail with no pagination control, so this is the author's window rather than a page size | | **data** | `any[]` | optional | Pre-fetched records — read FIRST as the rail's row source, ahead of the data-scope binding and the fetch, and composed into entries through the same `timeline` field bindings a fetched row takes; authoring it suppresses the object query entirely. Distinct from `items`, which is the already-composed entry shape and wins over this key when both are written | -| **items** | `any[]` | optional | Static inline entries — read ahead of every record source, `data` above included, and bypasses the object query entirely (the renderer becomes a pass-through). Each element is objectui's declared timeline element, `@object-ui/types`'s `TimelineFeedItem` (feed, `variant` absent / `vertical` / `horizontal`) or `TimelineGanttItem` (a gantt row, `variant: 'gantt'`), selected by this node's `variant`; objectui's strict authoring face refuses a mismatched arm and undeclared keys | +| **items** | `any[]` | optional | Static inline entries — read ahead of every record source, `data` above included, and bypasses the object query entirely (the renderer becomes a pass-through). Each element is objectui's declared timeline element, `@object-ui/types`'s `TimelineFeedItem` (`variant` absent / `vertical` / `horizontal`) or `TimelineGanttItem` (`variant: 'gantt'`), the arm this node's `variant` selects | | **variant** | `Enum<'vertical' \| 'horizontal' \| 'gantt'>` | optional | Rail layout (renderer default `vertical`). ⚠️ `gantt` needs authored `items`: the object-bound path composes flat feed entries, which the gantt branch cannot draw, and refuses that combination with a named diagnostic instead of drawing an empty chart | | **dateFormat** | `Enum<'short' \| 'long' \| 'iso'>` | optional | How each entry's date is rendered (renderer default `short`): `short` / `long` are locale-formatted, `iso` is the locale-free machine form | | **rowLabel** | `string` | optional | Header label for the gantt row column — read by the gantt branch only, which on this block needs authored `items` | diff --git a/packages/spec/src/ui/component.zod.ts b/packages/spec/src/ui/component.zod.ts index 8ba622f0312..1fb93dca91b 100644 --- a/packages/spec/src/ui/component.zod.ts +++ b/packages/spec/src/ui/component.zod.ts @@ -5207,7 +5207,7 @@ export const ObjectTimelinePropsSchema = lazySchema(() => strictObject({ data: z.array(z.unknown()).optional() .describe("Pre-fetched records — read FIRST as the rail's row source, ahead of the data-scope binding and the fetch, and composed into entries through the same `timeline` field bindings a fetched row takes; authoring it suppresses the object query entirely. Distinct from `items`, which is the already-composed entry shape and wins over this key when both are written"), items: z.array(z.unknown()).optional() - .describe("Static inline entries — read ahead of every record source, `data` above included, and bypasses the object query entirely (the renderer becomes a pass-through). Each element is objectui's declared timeline element, `@object-ui/types`'s `TimelineFeedItem` (feed, `variant` absent / `vertical` / `horizontal`) or `TimelineGanttItem` (a gantt row, `variant: 'gantt'`), selected by this node's `variant`; objectui's strict authoring face refuses a mismatched arm and undeclared keys"), + .describe("Static inline entries — read ahead of every record source, `data` above included, and bypasses the object query entirely (the renderer becomes a pass-through). Each element is objectui's declared timeline element, `@object-ui/types`'s `TimelineFeedItem` (`variant` absent / `vertical` / `horizontal`) or `TimelineGanttItem` (`variant: 'gantt'`), the arm this node's `variant` selects"), variant: z.enum(['vertical', 'horizontal', 'gantt']).optional() .describe("Rail layout (renderer default `vertical`). ⚠️ `gantt` needs authored `items`: the object-bound path composes flat feed entries, which the gantt branch cannot draw, and refuses that combination with a named diagnostic instead of drawing an empty chart"), dateFormat: z.enum(['short', 'long', 'iso']).optional() From 891830acdf29765700ac43c36a38aad9348543fd Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 01:04:31 +0000 Subject: [PATCH 3/3] docs(changeset): drop the round-2 correction paragraph from CHANGELOG prose The "Correction (round 2): ..." paragraph referenced round 1's wording, which never shipped and never left this branch -- the published CHANGELOG has no reader who saw it, so a paragraph explaining "the first cut of this describe" tells them about something that never happened to them. That record already lives in the PR body's verification section. The changeset keeps only its first two paragraphs (the actual change, and why it is documentation-only). Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx --- .../20283-object-timeline-items-element-owner-describe.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/.changeset/20283-object-timeline-items-element-owner-describe.md b/.changeset/20283-object-timeline-items-element-owner-describe.md index dbe704f0ced..1fe51f3ad3d 100644 --- a/.changeset/20283-object-timeline-items-element-owner-describe.md +++ b/.changeset/20283-object-timeline-items-element-owner-describe.md @@ -5,5 +5,3 @@ `ObjectTimelinePropsSchema.items`' describe no longer says "the author owns the item shape". `items` stays `z.array(z.unknown())` — no schema-shape change — but the sentence now names the actual owner: each element is objectui's declared timeline element, `@object-ui/types`'s `TimelineFeedItem` (`variant` absent / `vertical` / `horizontal`) or `TimelineGanttItem` (`variant: 'gantt'`), the arm this node's `variant` selects. The element union is declared entirely inside objectui's `packages/types` (`TimelineFeedItem` / `TimelineGanttItem`, plain TypeScript interfaces) — nothing in this package imports or re-declares it, so this is a documentation-only correction, not a value-tightening. Value tightening (declaring the arms in this schema instead of `z.unknown()`) stays a later ratchet with its own inventory. - -Correction (round 2): the first cut of this describe also claimed "objectui's strict authoring face refuses a mismatched arm and undeclared keys" for this block. Measured false at the pin — objectui's zod authoring face has no `object-timeline` arm at all (`AnyComponentSchema` / `StrictAnyComponentSchema` in `packages/types/src/zod/index.zod.ts` are a `z.discriminatedUnion('type', …)` whose member literals include `object-grid` / `object-form` / `object-view` / `object-map` / `object-tree` / `object-gantt` / `object-calendar` / `object-kanban` / `object-chart` / `object-gallery` / `object-data-table` and, separately, `timeline` — never `object-timeline`); a live `StrictAnyComponentSchema.safeParse({ type: 'object-timeline', items: […] })` against objectui's built types at this pin returns `invalid_union` / "No matching discriminator" on both faces, not an arm/key judgment. The arm-selection and `unrecognized_keys` mechanism this describe now names lives on the sibling `timeline` zod node (`TimelineSchema`'s `.superRefine`), which `object-timeline` types against only in TypeScript (`ObjectTimeline.tsx`'s `schema: TimelineSchema & { … }`), not at runtime. The refusal clause is dropped; nothing else about the route changed.