From 18ac64fcc46e1f4def19fa265953b0941434d099 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Oct 2026 22:08:06 +0000 Subject: [PATCH 1/2] docs(spec): describe objectui's normaliseOptions as it is at the console pin The ActionParam options docblock, the comment above its guidance table and one action.test.ts comment said objectui's normaliseOptions rebuilds each inherited option as { label, value }. At the console pin (.objectui-sha f8267df93aba) it returns { ...raw, label, value: raw.value } (packages/fields/src/resolveActionParams.ts:515), so inherited keys survive the resolver. The comments now say so, and give the remaining reason the guidance does not prescribe inheriting: no reader of color / icon / disabled on the inherited path is measured from this repo. Comments only: no guidance string, schema, export or message changes. Claude-Session: https://claude.ai/code/session_01LFqdRsuM5nkqoJdHd5ztVk Co-authored-by: Claude --- packages/spec/src/ui/action.test.ts | 5 +++-- packages/spec/src/ui/action.zod.ts | 29 +++++++++++++++++------------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/spec/src/ui/action.test.ts b/packages/spec/src/ui/action.test.ts index b5fbdba12f8..aff2f715863 100644 --- a/packages/spec/src/ui/action.test.ts +++ b/packages/spec/src/ui/action.test.ts @@ -1754,8 +1754,9 @@ describe('action param option vocabulary — declared only where a renderer deli // `color`: real one layer down, on the STORED-value display path. The // sentence must no longer defer to #5016 as an open question — it is - // decided — and must not promise the field-backed inheritance route, which - // `normaliseOptions` still drops (ledger finding 18). + // decided — and must not promise the field-backed inheritance route: + // objectui's `normaliseOptions` keeps the key by spread, but no reader of + // it on that route is measured (ledger finding 18). const color = messageFor({ label: 'A', value: 'a', color: 'red' }); expect(color).toContain('SelectOptionSchema'); expect(color).not.toContain('do not rely on it today'); diff --git a/packages/spec/src/ui/action.zod.ts b/packages/spec/src/ui/action.zod.ts index a3270bbbcfb..1652173322c 100644 --- a/packages/spec/src/ui/action.zod.ts +++ b/packages/spec/src/ui/action.zod.ts @@ -265,15 +265,18 @@ export const ActionParamSchema = lazySchema(() => strictObject( * `default` it would actively mislead, since a dialog param defaults * through `defaultValue` one level up. * - * **What this does NOT fix**, deliberately, because it is objectui's and not - * the spec's: a FIELD-BACKED param that inherits its list instead of declaring - * one still loses the key. `resolveActionParam` reaches - * `param.options ?? normaliseOptions(field.options, …)`, and `normaliseOptions` - * rebuilds every inherited entry as `{ label, value }`. That drop predates - * this change, is invisible to it (an authored `options` array wins over the - * inherited one), and is tracked in objectui — so the guidance below still - * refuses to prescribe "make it field-backed and inherit" (ledger finding 18: - * a confidently wrong prescription is worse than none). + * **A FIELD-BACKED param that inherits its list** instead of declaring one + * takes objectui's path, not the spec's. At the console pin (`.objectui-sha` + * `f8267df93aba`), objectui's `packages/fields/src/resolveActionParams.ts` + * resolves it as `authoredOptions ?? normaliseOptions(field.options, …)` + * (`:650`–`:651`), and `normaliseOptions` returns + * `{ ...raw, label, value: raw.value }` per entry (`:515`), so every key of an + * inherited entry survives the resolver. An authored `options` array still + * wins over the inherited one. Surviving is not being read: no reader of + * `color` / `icon` / `disabled` on an inherited option is measured from this + * repo, so the guidance below still refuses to prescribe "make it + * field-backed and inherit" (ledger finding 18: a confidently wrong + * prescription is worse than none, and an unmeasured one is no cure for it). * * The aliases are anchored on `SelectOptionSchema`'s own curated table (the * same idea, one layer down) rather than on edit distance, and deliberately @@ -307,9 +310,11 @@ export const ActionParamSchema = lazySchema(() => strictObject( // The per-option keys that are real one layer down but have no reader on // THIS path, plus the two that no spec shape declares at all. Each says // where the vocabulary lives and — critically — does NOT promise that a - // field-backed param inherits it: `resolveActionParams`' `normaliseOptions` - // rebuilds each inherited entry as `{ label, value }`, so that promise - // would be false in exactly the way ledger finding 18 warns about. + // field-backed param inherits it. objectui's `normaliseOptions` keeps an + // inherited entry's keys by spread (the `options` docblock above cites the + // pin and line), but no reader of them on the inherited path is measured + // from this repo, so that promise would be an unmeasured one in exactly + // the way ledger finding 18 warns about. // // `visibleWhen` is deliberately absent: it is a declared key now, and // `guidance` is consulted only from the `unrecognized_keys` path, so an From 56ad5cf1534c5414679ac5f92b9c0b380b3a8e2e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Oct 2026 22:15:44 +0000 Subject: [PATCH 2/2] docs(spec): add the prose-only patch changeset for the normaliseOptions comments @objectstack/spec ships src/**/*.zod.ts in its files[], so the rewritten comments in src/ui/action.zod.ts reach the published tarball verbatim. Claude-Session: https://claude.ai/code/session_01LFqdRsuM5nkqoJdHd5ztVk Co-authored-by: Claude --- .changeset/24047-action-param-options-comments.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .changeset/24047-action-param-options-comments.md diff --git a/.changeset/24047-action-param-options-comments.md b/.changeset/24047-action-param-options-comments.md new file mode 100644 index 00000000000..0c6bf95ea44 --- /dev/null +++ b/.changeset/24047-action-param-options-comments.md @@ -0,0 +1,14 @@ +--- +'@objectstack/spec': patch +--- + +Clause-②: no + +The `ActionParam` `options` docblock in `src/ui/action.zod.ts`, and the comment above that option +shape's `guidance` table, no longer say that objectui's `normaliseOptions` rebuilds each option a +field-backed param inherits as a bare `{ label, value }` pair. At the console pin (`.objectui-sha` +`f8267df93aba`) it keeps every key of an inherited option by spread +(`packages/fields/src/resolveActionParams.ts:515` in objectui). The comments now give the reason +that remains for the `guidance` texts not telling an author to make a param field-backed and +inherit `color`, `icon` or `disabled`: the keys survive, but no reader of them on that path is +measured. No key, schema, export, accepted value or message text changes.