Skip to content
This repository was archived by the owner on Oct 26, 2026. It is now read-only.

Commit deed787

Browse files
committed
feat(types): make ObjectKanbanSchema.objectName a presence rule on both faces
`ObjectKanban` resolves rows through the pre-fetched `data` prop, then `bind`, then the inline row array on `schema.data`, and only then a fetch keyed by `schema.objectName` — every `objectName` read guarded. Both published faces nevertheless REQUIRED `objectName`, so a `bind`-only or `data`-only board that renders correctly was refused by the shipped validator and could not be annotated with its own type. `objectName` is now optional on the interface and on the mirror, with the requirement the renderer really has expressed as a refinement: `requireKanbanRecordSource` — at least one of `bind`, `data`, `objectName`. Deliberately NOT the map / gantt / calendar predicate: that ladder is `data` (a ViewData provider block) -> `staticData` -> `objectName` through the shared `resolveRecordSourceConfig`. This board has no `staticData` rung, reads `data` as a raw row array, has a `bind` rung the other three never walk, and calls `getDataConfig` zero times. objectui#7651 (ruled B, closed not_planned) refuses building the shared ladder here; nothing here adds a rung, and `data` / `bind` stay inherited from `BaseSchema` rather than re-declared. `groupBy` stays REQUIRED (objectui#7322, PR #7774): a record source and a lane key are different questions, and the two readings that PR excluded from counting as a lane-less mode are pinned still-refused. Refs #7780 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QtGhnU3WnnWyiWeYQhw2aX
1 parent fff250f commit deed787

5 files changed

Lines changed: 511 additions & 17 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
'@object-ui/types': minor
3+
---
4+
5+
`ObjectKanbanSchema.objectName` becomes a PRESENCE RULE on both faces — at least one of
6+
`bind`, `data`, `objectName` (objectui#7780).
7+
8+
`packages/plugin-kanban/src/ObjectKanban.tsx` resolves a board's rows in four steps: the
9+
pre-fetched `data` PROP a parent passes (`hasExternalData`), then `bind` through
10+
`useDataScope(schema.bind)`, then the inline ROW ARRAY on `schema.data`, and only then a
11+
fetch keyed by `schema.objectName` — `rawData = (hasExternalData ? externalData :
12+
undefined) || boundData || schema.data || fetchedData`, with the fetch itself gated on
13+
`schema.objectName && !boundData && !schema.data`. Every `objectName` read is guarded.
14+
Both published faces nevertheless REQUIRED `objectName`, so a `bind`-only or `data`-only
15+
board — one that renders correctly today — was refused by the shipped validator and could
16+
not be annotated with its own type.
17+
18+
**The accept set moves in one direction only.** Measured from source on `origin/main`
19+
`fff250ff` and on this branch, both entry paths (`ObjectKanbanSchema.safeParse` and the
20+
published `safeValidateSchema`), against a rebuilt `dist`:
21+
22+
| document (all carry `groupBy`) | before | after |
23+
| --- | --- | --- |
24+
| `bind` only | refused at `objectName` (`invalid_type`) | **accepted** |
25+
| `data` only (raw rows) | refused at `objectName` (`invalid_type`) | **accepted** |
26+
| `objectName` only | accepted | accepted |
27+
| `objectName: ''` | accepted | accepted |
28+
| none of the three | refused at `objectName` | refused **on the refinement** (`RECORD_SOURCE_REQUIRED`, root path) |
29+
30+
Nothing went from accepted to refused. Presence is `!== undefined`, matching the sibling
31+
predicate's wording rather than the renderer's truthiness, so an empty `objectName: ''`
32+
keeps validating.
33+
34+
**⚠️ Not the map / gantt / calendar ladder, and deliberately not built on it.** Those
35+
three (objectui#6939, objectui#7313) resolve `data` as a `ViewData` PROVIDER BLOCK →
36+
`staticData` → `objectName` through the shared `resolveRecordSourceConfig`, refined by
37+
`requireRecordSource`. This board has **no** `staticData` rung, reads `data` as a **raw row
38+
array** directly, **has** a `bind` rung the other three never walk, and calls
39+
`getDataConfig` / `resolveRecordSourceConfig` **zero** times. The two key sets are neither
40+
equal nor nested, so the predicate is a new one — `requireKanbanRecordSource` — and the
41+
shared one is untouched.
42+
43+
**objectui#7651 is a prior ruling and it holds.** It was ruled B and closed `not_planned`
44+
on 2026-09-05, refusing a sixth `getDataConfig` producer, a `ViewData` retype of the
45+
board's `data`, and a `staticData` rung; its epitaph is in the tree on
46+
`KanbanSchema.data`. Nothing here adds a rung. `data` and `bind` stay INHERITED from
47+
`BaseSchema` rather than re-declared on this member, and the new pin asserts that by
48+
IDENTITY (`shape.data === BaseSchema.shape.data`) rather than by membership, because
49+
membership cannot tell inherited from re-declared.
50+
51+
**`groupBy` is untouched and stays REQUIRED** (objectui#7322, PR #7774). A record source
52+
and a lane key are different questions: every lane-less document above is still refused at
53+
`groupBy`, and the two readings PR #7774 excluded from counting as a lane-less mode — the
54+
`dataSource` json fragment in `content/docs/utilities/data-objectstack.mdx` and
55+
`ListView.tsx`'s runtime-generated node — are asserted still-refused in the new pin. The
56+
retired `groupField` tombstone is likewise still refused by name.
57+
58+
Landed on both faces per the zod-mirror-parity pairing: `objectName?: string` on the
59+
`ObjectKanbanSchema` interface in `@object-ui/types`, `z.string().optional()` plus
60+
`.superRefine(requireKanbanRecordSource)` on its mirror in `@object-ui/types/zod`. The key
61+
sets are unchanged on both sides, so no parity ledger row and none of the objectui#7279
62+
header figures move. `object-calendar-record-source-7313.test.ts` loses the
63+
`@ts-expect-error` it carried to record where this defect class ended — leaving it would
64+
have reddened `TS2578` under `tsconfig.test.json` — and the literal it guarded now
65+
compiles, which is the assertion.
66+
67+
Marked `minor` per this repo's version-alignment rule: a published accept set widens, and
68+
no declared shape narrows.

‎packages/types/src/__tests__/object-calendar-record-source-7313.test.ts‎

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -123,21 +123,32 @@ export const DATA_DOCUMENT: TsObjectCalendarSchema = {
123123
};
124124

125125
/**
126-
* CLASS BOUNDARY, recorded without touching it (triage boundary 3 on
127-
* objectui#7313): `ObjectKanbanSchema` still REQUIRES `objectName`. Its
128-
* renderer reads `schema.data` ahead of the fetch and guards every
129-
* `objectName` read, so it carries the same defect class — but that block is
130-
* objectui#7322's, and this control only says where the class currently ends.
131-
* When a card moves the kanban's `objectName` to optional, this directive is
132-
* the line that card deletes.
126+
* CLASS BOUNDARY — and it MOVED. This literal used to carry a
127+
* `@ts-expect-error` reading "objectName is still required on
128+
* ObjectKanbanSchema", recorded here by objectui#7313 as the one deliberate
129+
* error in this file, with the note "when a card moves the kanban's
130+
* `objectName` to optional, this directive is the line that card deletes".
131+
* objectui#7780 is that card, and this is that deletion: the directive is gone
132+
* and the literal now compiles, which is the assertion — an unused
133+
* `@ts-expect-error` is `TS2578`, so leaving it would have reddened
134+
* `tsconfig.test.json` rather than quietly meaning nothing.
133135
*
134-
* `groupBy` is supplied because objectui#7322 made it the required lane key
135-
* (and retired `groupField`), so `objectName` is the ONE member this literal
136-
* is missing — the directive would otherwise be satisfied by an unrelated
137-
* omission and stop saying anything about `objectName`.
136+
* ⚠️ It is NOT the same fix as this file's own subject. `object-calendar`
137+
* joined the `object-map` / `object-gantt` ladder — `data` (a `ViewData`
138+
* provider block) → `staticData` → `objectName`, `requireRecordSource`. The
139+
* kanban board walks its own: pre-fetched `data` prop → `bind` → an inline ROW
140+
* ARRAY on `data` → `objectName`, with no `staticData` rung, and objectui#7651
141+
* (ruled B, closed `not_planned`) refuses giving it the shared one. Its
142+
* refinement is `requireKanbanRecordSource`, written for that ladder, and the
143+
* verdict table lives in `object-kanban-record-source-7780.test.ts`.
144+
*
145+
* `groupBy` is still supplied, and now for a second reason as well as the
146+
* first: objectui#7322 made it the required lane key, so it was the only way
147+
* `objectName` could be the ONE member this literal was missing — and
148+
* objectui#7780 deliberately did NOT touch it, so this literal also witnesses
149+
* that a record source and a lane key stayed different questions.
138150
*/
139-
// @ts-expect-error — objectName is still required on ObjectKanbanSchema (the one deliberate error here)
140-
export const KANBAN_STILL_REQUIRES_OBJECT_NAME: TsObjectKanbanSchema = {
151+
export const KANBAN_NO_LONGER_REQUIRES_OBJECT_NAME: TsObjectKanbanSchema = {
141152
type: 'object-kanban',
142153
groupBy: 'status',
143154
};

0 commit comments

Comments
 (0)