Skip to content

Commit 6260133

Browse files
committed
fix(spec): title SelectOptionSchema's six row properties
Studio renders a `type: 'repeater'` form field as a table whose column headers read `items.properties[k].title ?? k` off the JSON Schema derived from the metadata type schema. `SelectOptionSchema` carried no `title` on any of its six row properties, so the fallback arm ran and the property panel printed the raw machine keys — in every locale, English included. Add `.meta({ title })` to `label`, `value`, `description`, `color`, `default` and `visibleWhen`, and delete this carrier's two entries from the shrink-only `repeater-item-titles` ledger. One edit clears both: `field:options` and `object:fields.options` resolve to the SAME `SelectOptionSchema` object — `FieldSchema.options` is `z.array(SelectOptionSchema)` and `object.fields` is a record of that same `FieldSchema` — verified by object identity, not assumed. Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7056ca5 commit 6260133

2 files changed

Lines changed: 16 additions & 16 deletions

File tree

‎packages/spec/src/data/field.zod.ts‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -335,8 +335,8 @@ export const SelectOptionSchema = lazySchema(() => strictObject({
335335
// no existing pointer is shadowed (`alias-integrity.test.ts`, #7889).
336336
guidanceSets: [SELECT_OPTION_EDITABILITY_GUIDANCE],
337337
}, {
338-
label: z.string().describe('Display label (human-readable, any case allowed)'),
339-
value: SystemIdentifierSchema.describe('Stored value (lowercase machine identifier)'),
338+
label: z.string().describe('Display label (human-readable, any case allowed)').meta({ title: 'Label' }),
339+
value: SystemIdentifierSchema.describe('Stored value (lowercase machine identifier)').meta({ title: 'Value' }),
340340
/**
341341
* Optional secondary text for the option (objectui#6153, inheriting the
342342
* objectui#6140 ruling frame — maintainer 2026-08-25: a key that is
@@ -356,9 +356,9 @@ export const SelectOptionSchema = lazySchema(() => strictObject({
356356
* spelling on its own metadata type (maintainer ruling 2026-09-02 on
357357
* objectui#6153).
358358
*/
359-
description: z.string().optional().describe('Optional secondary/help text for this option. Lookup option search matches it in addition to the label; renderers may show it as supporting text.'),
360-
color: z.string().optional().describe('Color code for badges/charts'),
361-
default: z.boolean().optional().describe('Is default option'),
359+
description: z.string().optional().describe('Optional secondary/help text for this option. Lookup option search matches it in addition to the label; renderers may show it as supporting text.').meta({ title: 'Description' }),
360+
color: z.string().optional().describe('Color code for badges/charts').meta({ title: 'Color' }),
361+
default: z.boolean().optional().describe('Is default option').meta({ title: 'Default' }),
362362
/**
363363
* Per-option visibility predicate (CEL) — the option is offered only when this
364364
* evaluates TRUE. Omit = always available. Evaluated against the live `record`
@@ -384,7 +384,7 @@ export const SelectOptionSchema = lazySchema(() => strictObject({
384384
* rule-validator evaluates the picked value's `visibleWhen`) — hiding it in the
385385
* dropdown alone is bypassable.
386386
*/
387-
visibleWhen: EvaluatedExpressionInputSchema.optional().describe("Per-option visibility predicate (CEL) — option is offered only when TRUE (else omitted). Env: the live `record` plus the host predicate scope, which binds `current_user`. The one VISIBILITY predicate the SERVER also enforces — the rule validator refuses a write of a value whose predicate is false — so a user-gated CHOICE belongs here. e.g. P`record.country == 'cn'` or P`'admin' in current_user.positions`"),
387+
visibleWhen: EvaluatedExpressionInputSchema.optional().describe("Per-option visibility predicate (CEL) — option is offered only when TRUE (else omitted). Env: the live `record` plus the host predicate scope, which binds `current_user`. The one VISIBILITY predicate the SERVER also enforces — the rule validator refuses a write of a value whose predicate is false — so a user-gated CHOICE belongs here. e.g. P`record.country == 'cn'` or P`'admin' in current_user.positions`").meta({ title: 'Visible When' }),
388388
}));
389389

390390
/**

‎packages/spec/src/kernel/repeater-item-titles.test.ts‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,19 +84,19 @@ const FORMS: ReadonlyArray<readonly [string, unknown]> = [
8484
* Carriers still owed titles, as measured on `origin/main` at
8585
* e758131b3900eb13260f03643e295ca6d625c42b. SHRINK-ONLY — see the header.
8686
*
87-
* The remaining `view.*` / `field.*` entries were fenced out of #17232's
88-
* round by in-flight PRs on their carrier files (#17360 `view.zod.ts`,
89-
* #17477 `field.zod.ts` — `field.options` and `object.fields.options` are
90-
* the same `SelectOptionSchema`). This pin OBSERVES them without editing
91-
* them, which is why the set below is the rest of the class and not the
92-
* slice one PR could reach.
87+
* The remaining `view.*` entries were fenced out of #17232's round by an
88+
* in-flight PR on their carrier file (#17360 `view.zod.ts`). This pin
89+
* OBSERVES them without editing them, which is why the set below is the rest
90+
* of the class and not the slice one PR could reach.
9391
*
94-
* `dashboard:widgets` and `dashboard:globalFilters` were paid by #17505 and
95-
* DELETED from this set — a paid debt leaves no entry behind.
92+
* `dashboard:widgets` and `dashboard:globalFilters` were paid by #17505,
93+
* `field:options` and `object:fields.options` by #17506 — one edit for both,
94+
* because the two carriers resolve to the SAME `SelectOptionSchema` object
95+
* (`FieldSchema.options` is `z.array(SelectOptionSchema)` and `object.fields`
96+
* is a record of that same `FieldSchema`). All four are DELETED from this set
97+
* — a paid debt leaves no entry behind.
9698
*/
9799
const LEDGER: ReadonlySet<string> = new Set([
98-
'field:options',
99-
'object:fields.options',
100100
'view:columns',
101101
'view:sort',
102102
'view:tabs',

0 commit comments

Comments
 (0)