Skip to content

Commit 52aad5c

Browse files
feat(types,plugin-kanban)!: retire ObjectKanbanSchema.onQuickAdd; the object-kanban path renders an internal board that takes the quick-add pair as props (objectui#11234) (#11269)
Fixes #11234 Clause-②: no (narrowing) Retires `ObjectKanbanSchema.onQuickAdd` on `object-kanban` on both faces, completing ruling B of the director seat's decision batch #91 (`5583979207`, recorded on objectui#8285): the object-bound board does not grow an inline record-creation write path, and the Quick Add pair stays with a React host. Direction is option A of the PR objectui#11231 dev, as adopted in the card; no inline creation path is added, and `KanbanRenderer`'s host pair is unchanged. ## What changed - **`@object-ui/types`**: `ObjectKanbanSchema.onQuickAdd` is `?: never` on the TypeScript face (`objectql.ts`), and `handlerKeyRefusal('onQuickAdd', 'retired', 'Quick Add handler')` on the zod mirror (`zod/objectql.zod.ts`). The JSON face still refuses the key by name with code `custom`; the guidance now says RETIRED instead of RUNTIME SLOT. - **`@object-ui/plugin-kanban`**: - A new internal module `src/KanbanBoardCore.tsx` holds the board body that used to live in `KanbanRenderer`: the bucketing memo, the lazy `KanbanImpl`, and the Suspense fallback. It takes the Quick Add pair ONLY as explicit props (`quickAdd`, `onQuickAdd`), and its `schema` type is `KanbanRendererProps['schema']` with those two keys omitted, so a `schema.onQuickAdd` read written into it fails type-check. It is not re-exported from the `index.tsx` barrel. - `KanbanRenderer` (public) now renders `KanbanBoardCore` and passes `schema.quickAdd` / `schema.onQuickAdd`. Its props interface is byte-identical, and so is what a host gets. - `ObjectKanban` renders `KanbanBoardCore` and passes neither half. The old `quickAdd: undefined` line on `effectiveSchema` is removed, because nothing on this path reads `schema.quickAdd` any more; the comment in its place says where the cut now is. - Why the module move was needed: `check:handler-key-reads` refuses a `retired` disposition while a renderer reachable from the registration reads the key off the document. The `object-kanban` path reached `KanbanRenderer`'s `schema.onQuickAdd` read. After this change, the gate's census for `object-kanban` holds one row, `onCardClick [runtime-slot]`, read in `KanbanBoardCore.tsx`. - **Docs**: `content/docs/plugins/plugin-kanban.mdx` (the handler-key paragraph and the "Quick Add is a React-host capability" section) and `content/docs/api/schema-reference.md` (the `ObjectKanbanSchema` handler-keys callout, and one clause of the `quickAdd` callout). - **Changesets**: `.changeset/11234-object-kanban-onquickadd-retired.md`, with `minor` on `@object-ui/types` (a narrowing, breaking semantics stated in the body) and `patch` on `@object-ui/plugin-kanban` (no public props moved). Three pending entries that this release would otherwise ship with a false sentence are amended, one sentence each, with every other byte and the frontmatter unchanged: `8285-object-kanban-quick-add-retired.md` (its closing note said `onQuickAdd` stays a runtime slot), `7804-object-kanban-handler-keys-judged.md` (it said `onCardClick` and `onQuickAdd` are both runtime slots) and `8285-kanban-quickadd-html-diagnostic.md` (it said `onQuickAdd` is a runtime slot). ## Pins - **JSON face, by name, and TS face `never`**: new `packages/types/src/__tests__/object-kanban-on-quick-add-retired-11234.test.ts`. - Runtime legs: the key is refused as `custom` at path `onQuickAdd`, for an action object and for a live function. The `.describe()` text is the parse message. A near-miss key is kept. The CLI union refuses a whole node carrying the key. - The retired-disposition leg reads RETIRED for `onQuickAdd` against `onCardClick` on the same arm and helper, which still reads RUNTIME SLOT. That `onCardClick` reading is the lit control. - Type-level pins: `Equal` to `undefined`, not `any`, not callable, and an `@ts-expect-error` on a literal that writes the key. The sibling slot `onCardClick` is still callable. - **The host pair still draws and calls (control)**: `quickAddRetiredNotForwarded-8285.test.tsx` row 2, unchanged. `KanbanRenderer` with `quickAdd: true` plus a function draws one control per lane, and a submitted title reaches the host. - **An `object-kanban` node draws no Quick Add**: the same file's row 1, unchanged, through the real registry and the real board. It was already green before this change, because `quickAdd` was cut. The readings that discriminate on `onQuickAdd` itself flip in two files, each with `onCardClick` as the lit control on the same render: - `handlerKeyDispositionsMeasured-7804.test.tsx` suite 2: the board receives `onQuickAdd: undefined` and `quickAdd: undefined`, while the authored `onCardClick` runs once. - `kanban-handler-slots-7664.test.tsx` suite 1. - **Ablation** (below): re-declaring the key as a runtime slot turns the retired-disposition pin red on each face. Ledger rows moved with it: - the objectui#6124 ledger: the row moves from `RUNTIME_SLOT` to `RETIRED`, 61 + 21 becomes 60 + 22 with the total unchanged at 82, and the type blocks move the key from `KeepsFunction` to `RetiredIsNever`; - `zod-mirror-parity`: `onQuickAdd` leaves the `objectql.zod.ts#ObjectKanbanSchema` drift entry, and the header key total goes 87 to 86 with its history sentence; - the objectui#7340 docs census: 29 retired members, `objectql.ts` 2, and `onQuickAdd` joins the unambiguously retired names; - the objectui#7804 and objectui#7664 plugin-kanban readings: suite 3 of 7664 now derives the read sites from both bodies, and adds a barrel pin that `KanbanBoardCore` is not published; - `check-handler-key-read-sites.test.ts`: `onQuickAdd` leaves the `object-kanban` census, the arm reads `retired`, and the `onCardClick` row's file is `KanbanBoardCore.tsx`. Three source-reading pins had to be re-pointed at the moved text: - the objectui#9357 arity pin's `index.tsx` hop is now the hand-off to `KanbanBoardCore`; - the objectui#8932 firing control finds the live board's lazy import in `KanbanBoardCore.tsx`; - the objectui#8801 schema-channel end is now `KanbanBoardCore`'s signature. ## Measured producers of `onQuickAdd` on `object-kanban`: 0 in both repos - **objectui** (this branch): tracked files naming `onQuickAdd`, excluding tests and markdown, number 7 at the final head. They are `KanbanImpl.tsx` (the board's props), `index.tsx` (the host face), the two declarations, one comment in the gate script, `KanbanBoardCore.tsx` (its internal prop pair), and one comment in `ObjectKanban.tsx`. None writes the key onto an `object-kanban` node. Lit control on the same instrument: 7 non-test files write a `type: 'object-kanban'` node, and 2 JSON files in `examples/schema-catalog` carry `object-kanban`. JSON/YAML files naming `onQuickAdd`: 0. - **objectstack** `origin/main` `4d0b9cd54` (read-only `git grep`): `onQuickAdd` appears only in docs, CHANGELOGs, comments and diagnostic message text (`component.mdx`, the sdui-parser interim diagnostic and its test, the spec CHANGELOG, a `conversions/registry.ts` note). `packages/spec/src/ui/component.zod.ts` has 0 hits for `onQuickAdd`. Lit control on the same instrument: `quickAdd` hits 17 files, `component.zod.ts` among them. ## Verification All runs from the worktree root, through `os-verify-lock`, with `NODE_OPTIONS=--max-old-space-size=4096`. Pass counts are read from the vitest summary line, not from exit codes. Final head is `b1e88910f`. Its two commits over `e2bf34b97` are changeset text only, so the test and type-check runs below at `e2bf34b97` stand; the changeset gates were re-run at `b1e88910f` (last table row). | Run | Head | Result | |---|---|---| | `pnpm exec vitest run packages/plugin-kanban/src/` (verbose) | `611112e0e` | 64 files passed, 421 tests passed | | 8 named pins: the types 11234 / 6124 / zod-mirror-parity / 7340 / 8285 files, sdui-parser `kanban-quick-add-8285`, react 9357, scripts `check-handler-key-read-sites` | `611112e0e` | 8 files passed, 487 tests passed | | `pnpm exec vitest run packages/types/` plus the 14 test files outside types/scripts that import plugin-kanban (console, schema-catalog, app-shell, cli, core, i18n, plugin-calendar, plugin-list, runner) | `e2bf34b97` | 308 files passed, 8232 tests passed | | `pnpm exec vitest run scripts/__tests__/` (whole root suite, owed because docs were edited) | `e2bf34b97` | 177 passed, 2 skipped (179 files); 5371 passed, 2 skipped tests | | `pnpm --filter @object-ui/types type-check` (3 tsc projects; the test project lists 294 test files, the new pin among them) | `e2bf34b97` | exit 0 | | `pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-kanban^...' build` (13 packages), then `pnpm --filter @object-ui/plugin-kanban type-check` | `e2bf34b97` | build exit 0; type-check exit 0 with 0 `error TS`. The rebuilt `packages/types/dist/objectql.d.ts` carries `onQuickAdd?: never`, so the plugin read the new typings | | `check:handler-key-reads` | `e2bf34b97` | exit 0, every judged read declared, 2 exempted by ledger | | `check-changeset-presence`, `check-changeset-no-major`, `check:control-bytes`, `check:new-line-citations` | `e2bf34b97` | all exit 0; 0 new cross-file line citations | | `check-i18n-call-site-keys`, `check-i18n-en-drift`, `check-i18n-dead-keys` (the `kanban.uncategorized` translation hook moved modules) | `e2bf34b97` | all exit 0 | | `node scripts/markdown-test-inputs.mjs --audit` | `e2bf34b97` | exit 0: 107 candidate files adjudicated, 78 entries present | | `pnpm exec eslint --format json` over the 16 changed code files | `e2bf34b97` | 0 errors, 96 warnings. Every pre-existing file has the same message count as the base version linted through stdin; the 2 new files have 0 | | `check-governed-queue-guard --test` over the 19 paths | `e2bf34b97` | NOT GOVERNED | | `check-changeset-presence`, `changeset:check`, `check:changeset-claims`, `check:pending-changeset-literals`, `check-governed-queue-guard --test` over the 22 paths | `b1e88910f` | all exit 0; NOT GOVERNED | **Declared narrowing**: repo-wide `pnpm lint` and the full `pnpm test` farm are left to CI. eslint's population is its own `files: ['**/*.{ts,tsx}']` block. The file count, 16, is read from the JSON output. `eslint.config.js` configures no `projectService` and no `parserOptions.project`, so linting is not type-aware and this diff cannot move a verdict on an untouched file. ### Ablation: re-declare `onQuickAdd` as a runtime slot, one face at a time Run on the committed head `e2bf34b97` through objectstack's `scripts/ablation-replace.mjs` in wrap mode, inside a script with its own `trap` restore, under the lock. The predictions were written before the run. - **Zod leg**: `'retired'` becomes `'runtime-slot'` in `objectql.zod.ts`. The anchor went x1 to x0 and the blob `c106c73cb1ec` to `6b9fc742c9d5`. Vitest over the 11234 pin and the 6124 ledger read 2 failed, 341 passed. The two failures are exactly the predicted legs: the 11234 retired-disposition leg, and the 6124 "guidance wording distinguishes a runtime slot from a retired key" leg. - **TS leg**: `onQuickAdd?: never;` becomes the callable in `objectql.ts`. The anchor went x1 to x0 and the blob `dde43cddfea1` to `afedc0cab24b`. `tsc -p packages/types/tsconfig.test.json` exited 2 with 5 errors: - the 11234 pin: TS2344 twice (tombstone, not-callable) and TS2578 (the `@ts-expect-error` became unused); - the 6124 `RetiredIsNever` row: TS2344; - `zod-mirror-parity`'s type-map ledger: TS2322, because the drift entry is owed again. This fifth error was not in the written prediction. It is the right direction, since the ledger answers to the faces. - **Restore proven**: `git diff HEAD` over both paths is 0 bytes, both blob hashes equal HEAD's, and `git status --porcelain` has 0 lines. ## Acceptance notes (observations; nothing filed) - Three pending changesets said `onQuickAdd` is, or stays, a runtime slot on `object-kanban`, which would ship false in the same release: `8285-object-kanban-quick-add-retired.md`, `7804-object-kanban-handler-keys-judged.md` and `8285-kanban-quickadd-html-diagnostic.md`. Each false sentence is amended in this PR, and the rest of each file, frontmatter included, is byte-identical. The seat added the three files to the claim's file surface. - The objectui#6124 ledger's `RetiredIsNever` type block has no `ObjectKanbanSchema['onCardMove']` row. That key's `never` is pinned by a source read in the 7804 suite 3 leg instead. Not added here. - A few comments still say "`KanbanRenderer`'s lazy boundary", and several tests' pre-warm comments say their `'../KanbanImpl'` specifier matches `index`'s. The lazy import now lives in `KanbanBoardCore.tsx`. It resolves to the same module, so the pre-warm imports still hit the cache. The comments are not swept; per AGENTS.md they get repaired when a change touches those files anyway. I fixed the ones on lines this change touched. - The retired guidance text is the shared `handlerKeyRefusal` wording (the node-type remedy). The practical remedy for this key, mounting `KanbanRenderer`, is carried by the TS docblock, the docs and the changeset, the same as for `onCardMove`. - The card body says `Clause-②: yes`; the PM claim says `no (narrowing)`. This body copies the claim's line, as the dispatch order directs. Session: `https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec` (a dispatched `os-dev` run; branch `claude/issue-11234-kanban-onquickadd-retire`). --- _Generated by [Claude Code](https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 18d1a0a commit 52aad5c

22 files changed

Lines changed: 746 additions & 190 deletions
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
'@object-ui/types': minor
3+
'@object-ui/plugin-kanban': patch
4+
---
5+
6+
**BREAKING (scored `minor` per this repo's version-alignment convention)** —
7+
`ObjectKanbanSchema.onQuickAdd` is retired on `object-kanban` (objectui#11234).
8+
This completes ruling B of the director seat's decision batch #91
9+
(objectui#8285): the object-bound board does not grow an inline record-creation
10+
write path, and the Quick Add pair stays with a React host.
11+
12+
**Why it did nothing.** After objectui#8285 retired `quickAdd`, `onQuickAdd`
13+
stayed a runtime slot. A host-supplied function reached the board by identity,
14+
and on `object-kanban` it was never called, because the Quick Add control is
15+
gated on both halves of the pair. A TypeScript host or an AI that wrote the key
16+
compiled and got nothing.
17+
18+
- **`@object-ui/types`** — `ObjectKanbanSchema.onQuickAdd` is `?: never` on the
19+
TypeScript face, and `handlerKeyRefusal('onQuickAdd', 'retired', …)` on the
20+
zod mirror. An authored value is refused BY NAME (`custom` at `onQuickAdd`,
21+
the same code as before), and the message now says RETIRED instead of RUNTIME
22+
SLOT. This narrows a published TypeScript face: a typed host that wrote the
23+
key on an `object-kanban` node now gets a type error.
24+
- **`@object-ui/plugin-kanban`** — `ObjectKanban` renders an internal board
25+
that takes the Quick Add pair only as explicit props, and it supplies neither
26+
half. The internal board is not exported. `check:handler-key-reads` refuses a
27+
tombstone while a renderer on the registration still reads the key off the
28+
document, and this change removes that read from the `object-kanban` path.
29+
`KanbanRenderer` is unchanged for a host: it reads `schema.quickAdd` and
30+
`schema.onQuickAdd` exactly as before, and the control draws and calls the
31+
host's function.
32+
33+
**Migration.** Delete `onQuickAdd` from `object-kanban` nodes and from any
34+
`ObjectKanbanSchema` object a host builds. It was never called there. To offer
35+
Quick Add, mount `KanbanRenderer` from `@object-ui/plugin-kanban` in a React
36+
host and pass both `quickAdd` and `onQuickAdd` on its `schema`.
37+
38+
The objectui#8285 entry's closing note, which said `onQuickAdd` stayed a runtime
39+
slot on `object-kanban`, is amended in this change to match.

‎.changeset/7804-object-kanban-handler-keys-judged.md‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44

55
`ObjectKanbanSchema` now judges two of the three handler keys the kanban board reads off the
66
authored document (the `plugin-kanban` slice; director seat ruling of
7-
2026-09-07, decision batch #69): `onCardClick` and `onQuickAdd` are declared as objectui#6124
8-
RUNTIME SLOTS — callable on the TypeScript face, refused BY NAME in the zod mirror because
9-
JSON has no function value — and the message points at the node-type spelling an author can
10-
write instead.
7+
2026-09-07, decision batch #69): `onCardClick` is declared as an objectui#6124 RUNTIME SLOT —
8+
callable on the TypeScript face, refused BY NAME in the zod mirror because JSON has no function
9+
value — and the message points at the node-type spelling an author can write instead.
10+
`onQuickAdd` was declared the same way here; objectui#11234 retires it on `object-kanban` in the
11+
same release, so it is `?: never` on the TypeScript face and refused BY NAME as RETIRED.
1112

1213
Until now neither was declared anywhere. `BaseSchema` is `.passthrough()`, so a key no arm
1314
declares is not refused: it stops being judged and the value is KEPT, then reaches the

‎.changeset/8285-kanban-quickadd-html-diagnostic.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
html tier: name the inert `quickAdd` on `object-kanban` / `kanban` instead of calling it unknown
66

77
The Quick Add control is gated on BOTH `quickAdd` and an `onQuickAdd` handler, and
8-
`onQuickAdd` is a runtime slot — a function — that no parsed page can write and that
9-
`ObjectKanban` supplies none of its own for. So an authored `quickAdd: true` on either
8+
`onQuickAdd` takes a function, which no parsed page can write and which `ObjectKanban`
9+
supplies none of its own for. objectui#11234 retires `onQuickAdd` on `object-kanban` in the
10+
same release; it remains a host-supplied prop on `KanbanRenderer`'s `schema`. So an authored
11+
`quickAdd: true` on either
1012
`ObjectKanbanRenderer` tag has never produced a control.
1113

1214
Until now the only thing the tier said about it was `unknown-prop`, "has no prop

‎.changeset/8285-object-kanban-quick-add-retired.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,6 @@ control there. To offer Quick Add, mount `KanbanRenderer` from
4040
diagnostic code, or imported one of the four removed names, drops that branch:
4141
the key now draws `unknown-prop` like any other prop the block does not have.
4242

43-
Unchanged: `ObjectKanbanSchema.onQuickAdd` stays a runtime slot. It still reaches
44-
the board, and on `object-kanban` it is never called, because its partner is gone.
43+
`ObjectKanbanSchema.onQuickAdd` is retired on `object-kanban` too, by objectui#11234
44+
in the same release: on that element it was never called, because its partner is
45+
gone. The Quick Add pair on `KanbanRenderer` is unchanged.

‎content/docs/api/schema-reference.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -981,15 +981,15 @@ A drag-and-drop Kanban board. The `object-kanban` type key validates the shape t
981981

982982
> `groupField` is refused by name (objectui#7322): the renderer reads `groupBy`.
983983
984-
> **`quickAdd` and `allowCollapse` were rows of the table above and are not authorable on this board.** `allowCollapse` is **refused by name** by the strict authoring face, which does not declare it at all — a document carrying it fails validation rather than merely going unread, and `@object-ui/plugin-kanban` has no read site for it. `quickAdd` is **retired** on this board (objectui#8285, director seat, decision batch 91): the Quick Add control is gated on an `onQuickAdd` runtime slot and no `object-kanban` path supplies one, so the key never drew anything, and the ruling retired it rather than add inline record creation. The strict face tombstones it (refused by name), `ObjectKanban` no longer forwards it, and `@object-ui/sdui-parser` answers it with the ordinary `unknown-prop` warning. `@object-ui/types` mirrors this face and agrees on both: each is `never` on its TypeScript face and a retirement tombstone on its Zod face (objectui#8801, objectui#8285). The Quick Add pair itself lives on the `KanbanRenderer` component, which a React host mounts directly.
984+
> **`quickAdd` and `allowCollapse` were rows of the table above and are not authorable on this board.** `allowCollapse` is **refused by name** by the strict authoring face, which does not declare it at all — a document carrying it fails validation rather than merely going unread, and `@object-ui/plugin-kanban` has no read site for it. `quickAdd` is **retired** on this board (objectui#8285, director seat, decision batch 91): the Quick Add control is gated on an `onQuickAdd` handler as well, and no `object-kanban` path supplies one, so the key never drew anything, and the ruling retired it rather than add inline record creation. The strict face tombstones it (refused by name), `ObjectKanban` no longer forwards it, and `@object-ui/sdui-parser` answers it with the ordinary `unknown-prop` warning. `@object-ui/types` mirrors this face and agrees on both: each is `never` on its TypeScript face and a retirement tombstone on its Zod face (objectui#8801, objectui#8285). The Quick Add pair itself lives on the `KanbanRenderer` component, which a React host mounts directly.
985985

986986
> `columns` is declared on this face since objectui#8913, as the pair of array shapes `@objectstack/spec` declares — an array of `{ id, title }` lanes, **or** an array of bare value strings. A **mixed** array is refused: the renderer decides which shape it has from the first element alone, so a mix yields a blank lane and mis-bucketed cards. A lane accepts `id`, `title`, `cards`, `limit`, `className` and `collapsed`, which are the members the board implementations read; `id` is a **string** — the authored face keeps that narrowing, and since objectui#8993 a non-string lane id no longer renders every card twice: the bucketer's leftover sweep keys membership the way the injection already did (a lane `1` takes the group `'1'`). When a lane carries `cards`, each card is judged — a card with no `title` is refused. An undeclared lane key is accepted and dropped, not refused, which is this tolerant face's posture; the strict authoring face refuses it by name.
987987
>
988988
> ⚠️ The **bare-string array applies only to a board with no `groupBy`.** It is declared so this package does not refuse an authoring the protocol allows. The renderer reads a bare-string lane list only when a board has no `groupBy` — so on a board that *does* declare one the strings are ignored and the lanes come from the group field's picklist options or from the data. Since objectui#8990 made `groupBy` optional, a lane-less board is a valid authoring and this arm is live on it: the lanes are drawn, titled by the **raw strings** (a grouped board titles its lanes with the picklist *labels* instead). ⚠️ Such a board holds **no cards** — with no lane key the records are never distributed — and dragging a card writes nothing back. It is lane headings, not a populated board; to control the lanes of a working board, declare `groupBy` and write the `{ id, title }` array.
989989
>
990990
> The other keys the retired `kanban` arm alone declared — `cardTitle`, `swimlaneField`, `grouping` and `navigation` — are still undeclared on this face. The renderer reads them, so a board may carry them; they are simply not judged.
991991
992-
> **Handler keys are not authorable in JSON, and all three now say so by name.** Since objectui#7804 this face declares `onCardClick` and `onQuickAdd` as objectui#6124 **runtime slots**: a React host supplies the function through the TypeScript interface or as a React prop, and this validator **refuses the key by name** with a message pointing at the node-type spelling (`{ "type": "toast", … }`, an `action:button` node). Until then an authored `onCardClick: { "action": "toast" }` parsed **green** — `BaseSchema` is `.passthrough()`, so a key no arm declares is not refused, it stops being judged and the value is kept, then reaches a call site expecting a function. ⭐ The third key, `onCardMove`, is a **tombstone** since objectui#9342, not a runtime slot: an authored one reached **nothing** even as a function, because an object-bound board substitutes its own mover — so the TypeScript twin is `?: never` rather than callable, and the mover lives on `KanbanRenderer`'s React prop of the same name, a sibling of its `schema`.
992+
> **Handler keys are not authorable in JSON, and all three now say so by name.** Since objectui#7804 this face declares `onCardClick` as an objectui#6124 **runtime slot**: a React host supplies the function through the TypeScript interface or as a React prop, and this validator **refuses the key by name** with a message pointing at the node-type spelling (`{ "type": "toast", … }`, an `action:button` node). Until then an authored `onCardClick: { "action": "toast" }` parsed **green** — `BaseSchema` is `.passthrough()`, so a key no arm declares is not refused, it stops being judged and the value is kept, then reaches a call site expecting a function. ⭐ The other two keys are **tombstones**, not runtime slots, so their TypeScript twins are `?: never` rather than callable. `onCardMove` has been one since objectui#9342: an authored one reached **nothing** even as a function, because an object-bound board substitutes its own mover, and the mover lives on `KanbanRenderer`'s React prop of the same name, a sibling of its `schema`. `onQuickAdd` has been one since objectui#11234: a supplied one reached the board and was never called, because its partner `quickAdd` is retired here. `ObjectKanban` renders an internal board that takes the Quick Add pair only as explicit props and supplies neither half; the pair lives on `KanbanRenderer`'s `schema`, for a React host.
993993

994994
> `data` and `bind` are [`BaseSchema`](#baseschema) members, not narrowed here, but this face requires **one of** `bind`, `data`, `objectName` — the renderer's own record-source ladder (an external `data` prop → `bind` via `useDataScope` → this schema's own `data` → a fetch keyed by `objectName`). A purely static board (lanes carrying their own cards, no record source) authors `"groupBy"` and `"data": []`. ⚠️ The record-source rule is **separate** from the lane key and is unaffected by objectui#8990: omitting `groupBy` is fine, omitting all of `bind` / `data` / `objectName` is still refused, at the refinement rather than at `groupBy`.
995995

‎content/docs/plugins/plugin-kanban.mdx‎

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,21 @@ const onCardMove = (cardId: string, fromCol: string, toCol: string, index: numbe
8282
has no function value), never a member of the schema object below —
8383
`packages/plugin-kanban/README.md` documents this the same way.
8484

85-
Neither are `onCardClick` and `onQuickAdd`, and since objectui#7804 the
86-
`object-kanban` validator **says so by name**: both are declared as
87-
objectui#6124 **runtime slots**, so an authored `onCardClick: { "action":
88-
"toast" }` is now refused with a message pointing at the node-type spelling,
89-
where before it parsed green and was handed to a call site expecting a function.
90-
A React host still supplies either function through the TypeScript interface or
91-
as a React prop; only the JSON face refuses them. ⚠️ On `object-kanban` a
92-
supplied `onQuickAdd` reaches the board and is never called, because its partner
93-
`quickAdd` is retired there — see "Quick Add is a React-host capability" below.
85+
Neither is `onCardClick`, and since objectui#7804 the `object-kanban` validator
86+
**says so by name**: it is declared as an objectui#6124 **runtime slot**, so an
87+
authored `onCardClick: { "action": "toast" }` is now refused with a message
88+
pointing at the node-type spelling, where before it parsed green and was handed
89+
to a call site expecting a function. A React host still supplies the function
90+
through the TypeScript interface or as a React prop; only the JSON face refuses
91+
it.
92+
93+
⭐ `onQuickAdd` is a **tombstone** on `object-kanban` since objectui#11234, not a
94+
runtime slot. A supplied one used to reach the board and was never called,
95+
because its partner `quickAdd` is retired there. `ObjectKanban` now renders an
96+
internal board that takes the Quick Add pair only as explicit props, and it
97+
supplies neither half. So the TypeScript twin is `?: never` and the validator
98+
refuses the key by name. The pair lives on `KanbanRenderer` — see "Quick Add is
99+
a React-host capability" below.
94100

95101
⭐ `onCardMove` is declared too, since objectui#9342 — but as a **tombstone**,
96102
not a runtime slot. An authored one used to be accepted and silently dropped,
@@ -473,25 +479,27 @@ export function useBoard(initialColumns: KanbanColumn[]) {
473479
### Quick Add is a React-host capability, not a document one
474480

475481
The Quick Add button at the bottom of a lane is gated on **both** halves of a
476-
pair: `quickAdd`, and an `onQuickAdd` handler. `onQuickAdd` is a runtime slot —
477-
a function — so it can only be supplied by a React host that mounts the board
478-
component directly: `KanbanRenderer`, exported from `@object-ui/plugin-kanban`,
479-
forwards both halves by identity.
482+
pair: `quickAdd`, and an `onQuickAdd` handler. `onQuickAdd` is a function, so it
483+
can only be supplied by a React host that mounts the board component directly:
484+
`KanbanRenderer`, exported from `@object-ui/plugin-kanban`, reads both halves off
485+
its `schema` and forwards them by identity.
480486

481487
⚠️ There is **no node type key** that reaches it. The `kanban-ui` registration
482488
retired with objectui#8257, so a document naming that tag is now an
483489
`unknown-component` **error** rather than a board with a working pair. Import the
484490
component instead.
485491

486492
An **object-driven** board (`object-kanban`, or a kanban view inside `ListView` /
487-
`ObjectView`) has no Quick Add control, and `quickAdd` is **retired** on it
488-
(objectui#8285): the board does not create records inline. Both
489-
`ObjectKanbanSchema` faces in `@object-ui/types` refuse the key by name, as
490-
`@objectstack/spec` does, and `ObjectKanban` does not forward it — so even a
493+
`ObjectView`) has no Quick Add control, and **both** halves of the pair are
494+
**retired** on it: `quickAdd` since objectui#8285, and `onQuickAdd` since
495+
objectui#11234. The board does not create records inline. Both
496+
`ObjectKanbanSchema` faces in `@object-ui/types` refuse both keys by name, and
497+
`@objectstack/spec` refuses `quickAdd`. `ObjectKanban` renders an internal board
498+
that takes the pair only as explicit props, and it supplies neither — so even a
491499
host that hands the board both halves of the pair gets no control. A page
492-
written as constrained JSX draws the ordinary `unknown-prop` **warning** for it
493-
(`validateTree` in `@object-ui/sdui-parser`), since the block has no such prop.
494-
Delete the key; to offer Quick Add, mount `KanbanRenderer` as above.
500+
written as constrained JSX draws the ordinary `unknown-prop` **warning** for
501+
`quickAdd` (`validateTree` in `@object-ui/sdui-parser`), since the block has no
502+
such prop. Delete both keys; to offer Quick Add, mount `KanbanRenderer` as above.
495503

496504
## Bundle Size
497505

0 commit comments

Comments
 (0)