From 4abe68103277ad49d73b34a043977924a570699a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 17 Sep 2026 00:55:58 +0000 Subject: [PATCH 1/2] feat(spec): declare `titleField` on `KanbanConfigSchema` `KanbanConfigSchema` was the one item-titled view config of its family that omitted `titleField`, while Gallery, Timeline, Calendar, Gantt and ListMap all declare the key under the same name and the same `z.string()`. The schema is a `strictObject`, so an author writing the key the board actually reads was refused by name. Declared optional, matching the shape `CalendarConfigSchema` already writes down for this exact key: absence resolves through the ADR-0079 record display-name chain, so requiring it would demand more than the renderer reads. Timeline and Gantt spell it required and are the two siblings this declaration deliberately does not copy. Tests carry the four-leg probe with both controls firing on the same call shape, plus the optionality leg. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude --- packages/spec/src/ui/view.test.ts | 39 +++++++++++++++++++++++++++++++ packages/spec/src/ui/view.zod.ts | 19 +++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/packages/spec/src/ui/view.test.ts b/packages/spec/src/ui/view.test.ts index 99a9235a326..05386b001bd 100644 --- a/packages/spec/src/ui/view.test.ts +++ b/packages/spec/src/ui/view.test.ts @@ -182,6 +182,45 @@ describe('KanbanConfigSchema', () => { expect(() => KanbanConfigSchema.parse(config)).not.toThrow(); }); + + // [#16894] Director seat, decision batch #87 (objectstack-ai/objectui#8367, + // maintainer 「批 #87 同意」): `titleField` joins this schema as optional + // `z.string()`. The legs below are the card's executable acceptance + // criterion — BOTH controls fire on the same call shape, so the PROBE's flip + // is evidence about the NAME and not about a parser that stopped refusing. + it('ACCEPTS `titleField` as a member, with both controls firing on the same shape', () => { + const canonical = { + groupByField: 'status', + columns: ['name', 'owner'], + }; + + // CONTROL-1 — the parser CAN refuse, on the named surface. + const control1 = KanbanConfigSchema.safeParse({ ...canonical, zzUnlikelyBogusKey__: true }); + expect(control1.success).toBe(false); + const issues = JSON.stringify(control1.error?.issues); + expect(issues).toContain('unrecognized_keys'); + expect(issues).toContain('zzUnlikelyBogusKey__'); + expect(issues).toContain('this kanban configuration'); + + // CONTROL-2 — a refusal is about the name: the canonical block is accepted. + expect(KanbanConfigSchema.safeParse(canonical).success).toBe(true); + + // PROBE — before this card: `ok=false unrecognized_keys=["titleField"]`. + const probe = KanbanConfigSchema.safeParse({ ...canonical, titleField: 'subject' }); + expect(probe.success).toBe(true); + // Accepted as a MEMBER, not merely tolerated: the value survives the parse. + expect(KanbanConfigSchema.parse({ ...canonical, titleField: 'subject' })) + .toMatchObject({ titleField: 'subject' }); + }); + + // ⛔ NOT required. `TimelineConfigSchema` and `GanttConfigSchema` spell + // `titleField` required and are the two siblings this declaration does not + // copy: absence resolves through the ADR-0079 record display-name chain, so + // requiring it would demand more than the renderer reads (#13748). + it('leaves `titleField` OPTIONAL — a board that omits it is a complete config', () => { + const parsed = KanbanConfigSchema.parse({ groupByField: 'status', columns: ['name'] }); + expect('titleField' in parsed).toBe(false); + }); }); describe('CalendarConfigSchema', () => { diff --git a/packages/spec/src/ui/view.zod.ts b/packages/spec/src/ui/view.zod.ts index 1d1cfd0bef6..a83fda5ae6f 100644 --- a/packages/spec/src/ui/view.zod.ts +++ b/packages/spec/src/ui/view.zod.ts @@ -1349,6 +1349,25 @@ export const KanbanConfigSchema = lazySchema(() => strictObject({ }, { groupByField: z.string().describe('Field to group columns by (usually status/select)'), summarizeField: z.string().optional().describe('Field to sum at top of column (e.g. amount)'), + /** + * [#16894] The one item-titled view config of the family that omitted this + * key. `GalleryConfigSchema`, `TimelineConfigSchema`, `CalendarConfigSchema`, + * `GanttConfigSchema` and `ListMapConfigSchema` all declare `titleField` + * under the same name and the same `z.string()`; this schema is a + * `strictObject`, so an author writing the key the board actually reads was + * refused BY NAME while the renderer honoured it. Declared here under the + * director seat's decision batch #87 (objectstack-ai/objectui#8367). + * + * OPTIONAL, deliberately — the shape `CalendarConfigSchema` already writes + * down for this exact key. Absence resolves through the ADR-0079 record + * display-name chain (`titleFormat` -> `displayNameField` -> type-aware + * derivation -> `'Untitled'`), so requiring it would demand more than the + * renderer reads, the exact shape the #13748 ruling forbids + * (「不要求超过渲染器真正需要的」). `TimelineConfigSchema` and + * `GanttConfigSchema` spell it required; they are the two siblings this + * declaration does NOT copy. + */ + titleField: z.string().optional().describe('Field displayed as the card title. Omit to fall back to the record display name (ADR-0079 resolver chain)'), columns: z.array(z.string()).describe('Fields to show on cards'), })); From 6d01b4b63e07b5fbdc0069ce8f5143167b9813ff Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 17 Sep 2026 01:00:41 +0000 Subject: [PATCH 2/2] chore(spec): regenerate artifacts for the kanban `titleField` declaration `authorable-surface/ui.json` gains `ui/KanbanConfig:titleField`; the three reference pages that inline the kanban config shape pick up `titleField?: string`. Regenerated, not hand-edited: `check:authorable-surface` wrote the first and `gen:docs` the rest. Adds the minor changeset for the widening. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude --- .changeset/16894-kanban-config-titlefield.md | 13 +++++++++++++ content/docs/references/api/protocol.mdx | 4 ++-- content/docs/references/data/object.mdx | 2 +- content/docs/references/ui/view.mdx | 15 +++++++++------ packages/spec/authorable-surface/ui.json | 1 + 5 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 .changeset/16894-kanban-config-titlefield.md diff --git a/.changeset/16894-kanban-config-titlefield.md b/.changeset/16894-kanban-config-titlefield.md new file mode 100644 index 00000000000..f35f9c0ebae --- /dev/null +++ b/.changeset/16894-kanban-config-titlefield.md @@ -0,0 +1,13 @@ +--- +"@objectstack/spec": minor +--- + +`KanbanConfigSchema` now declares `titleField` — optional `z.string()`, the key the board already reads and the schema refused by name (#16894). + +`KanbanConfigSchema` is a `strictObject`, and it was the one item-titled view config of its family that omitted the key: `GalleryConfigSchema`, `TimelineConfigSchema`, `CalendarConfigSchema`, `GanttConfigSchema` and `ListMapConfigSchema` all declare `titleField` under the same name and the same `z.string()`. An author writing `kanban: { titleField: 'subject' }` — the spelling the renderer honours — was refused with `unrecognized_keys=["titleField"]`, while objectui's own mirror accepted it only by not looking. Declared here under the director seat's decision batch #87 (objectstack-ai/objectui#8367), confirmed by the maintainer verbatim 「批 #87 同意」. + +**Clause-②: yes (widening)** — one new declared key on a published, strict accept set, so the set a consumer writes against grows. Nothing previously admitted is refused, and nothing is retired. Contract-review tier. + +- **Optional, not required.** The shape is the one `CalendarConfigSchema` already writes down for this exact key: absence resolves through the ADR-0079 record display-name chain (`titleFormat` → `displayNameField` → type-aware derivation → `'Untitled'`), so requiring it would demand more than the renderer reads — the shape ruling #13748 forbids (「不要求超过渲染器真正需要的」). `TimelineConfigSchema` and `GanttConfigSchema` spell it required and are the two siblings this declaration deliberately does not copy. +- **No migration, no tombstone.** Nothing moves or is renamed: a board authored before this release parses unchanged, and `kanban.titleField` is simply no longer refused. +- **The generated projections move with it** — `authorable-surface/ui.json` gains `ui/KanbanConfig:titleField`, and the `ListView` / `ObjectListView` kanban shape lines in `content/docs/references/ui/view.mdx`, `content/docs/references/api/protocol.mdx` and `content/docs/references/data/object.mdx` gain `titleField?: string`. diff --git a/content/docs/references/api/protocol.mdx b/content/docs/references/api/protocol.mdx index b6fd069ff8b..f34ecca6796 100644 --- a/content/docs/references/api/protocol.mdx +++ b/content/docs/references/api/protocol.mdx @@ -1640,7 +1640,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | @@ -1725,7 +1725,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | diff --git a/content/docs/references/data/object.mdx b/content/docs/references/data/object.mdx index d060942dc50..24152b29e25 100644 --- a/content/docs/references/data/object.mdx +++ b/content/docs/references/data/object.mdx @@ -374,7 +374,7 @@ const result = ApiMethod.parse(data); | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | diff --git a/content/docs/references/ui/view.mdx b/content/docs/references/ui/view.mdx index 3bf385bc51c..476d8fe7675 100644 --- a/content/docs/references/ui/view.mdx +++ b/content/docs/references/ui/view.mdx @@ -698,6 +698,7 @@ HTTP methods a view data source may request — the subset of `HttpMethod` witho | :--- | :--- | :--- | :--- | | **groupByField** | `string` | ✅ | Field to group columns by (usually status/select) | | **summarizeField** | `string` | optional | Field to sum at top of column (e.g. amount) | +| **titleField** | `string` | optional | Field displayed as the card title. Omit to fall back to the record display name (ADR-0079 resolver chain) | | **columns** | `string[]` | ✅ | Fields to show on cards | @@ -799,7 +800,7 @@ Map view configuration | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | @@ -934,6 +935,7 @@ View filter rule | :--- | :--- | :--- | :--- | | **groupByField** | `string` | ✅ | Field to group columns by (usually status/select) | | **summarizeField** | `string` | optional | Field to sum at top of column (e.g. amount) | +| **titleField** | `string` | optional | Field displayed as the card title. Omit to fall back to the record display name (ADR-0079 resolver chain) | | **columns** | `string[]` | ✅ | Fields to show on cards | ### Nested Shape: `ListView.calendar` @@ -1205,7 +1207,7 @@ Tab configuration for multi-tab view interface | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | @@ -1331,6 +1333,7 @@ View filter rule | :--- | :--- | :--- | :--- | | **groupByField** | `string` | ✅ | Field to group columns by (usually status/select) | | **summarizeField** | `string` | optional | Field to sum at top of column (e.g. amount) | +| **titleField** | `string` | optional | Field displayed as the card title. Omit to fall back to the record display name (ADR-0079 resolver chain) | | **columns** | `string[]` | ✅ | Fields to show on cards | ### Nested Shape: `ObjectListView.calendar` @@ -1802,7 +1805,7 @@ Tab configuration for multi-tab view interface | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | @@ -1887,7 +1890,7 @@ Tab configuration for multi-tab view interface | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | @@ -2128,7 +2131,7 @@ This schema accepts one of the following structures: | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | @@ -2304,7 +2307,7 @@ This schema accepts one of the following structures: | **selection** | `{ type?: Enum<'none' \| 'single' \| 'multiple'> }` | optional | Row selection configuration | | **navigation** | `{ mode?: Enum<'page' \| 'drawer' \| 'modal' \| 'split' \| 'popover' \| 'new_window' \| 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }` | optional | Configuration for item click navigation (page, drawer, modal, etc.) | | **pagination** | `{ pageSize?: integer; pageSizeOptions?: integer[] }` | optional | Pagination configuration | -| **kanban** | `{ groupByField: string; summarizeField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | +| **kanban** | `{ groupByField: string; summarizeField?: string; titleField?: string; columns: string[] }` | optional | Kanban-board configuration — applies when the view renders as a kanban layout | | **calendar** | `{ startDateField: string; endDateField?: string; titleField?: string; colorField?: string; … }` | optional | Calendar configuration — applies when the view renders as a calendar layout | | **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout | | **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration | diff --git a/packages/spec/authorable-surface/ui.json b/packages/spec/authorable-surface/ui.json index 4048f035433..89a82028b71 100644 --- a/packages/spec/authorable-surface/ui.json +++ b/packages/spec/authorable-surface/ui.json @@ -596,6 +596,7 @@ "ui/KanbanConfig:columns", "ui/KanbanConfig:groupByField", "ui/KanbanConfig:summarizeField", + "ui/KanbanConfig:titleField", "ui/ListChartConfig:chartType", "ui/ListChartConfig:dataset", "ui/ListChartConfig:dimensions",