Skip to content

Commit 462d02f

Browse files
docs(spec): describe objectui's normaliseOptions as it is at the console pin (#24072)
Fixes #24047 Clause-②: no ## What changes Three comments in `packages/spec` still said that objectui's `normaliseOptions` rebuilds each option a field-backed action param inherits as a bare `{ label, value }` pair. At the console pin it keeps every key by spread. This PR rewrites the three comments and adds a prose-only changeset. Nothing else changes. - `packages/spec/src/ui/action.zod.ts`, the `ActionParam` `options` docblock. The paragraph that began "What this does NOT fix" said the drop "is tracked in objectui". It now describes objectui at the pin, with file, lines and pin sha. It also gives the reason that is still true for the guidance not prescribing "make it field-backed and inherit": the keys survive the resolver, but nothing in this repo measures a reader of `color`, `icon` or `disabled` on the inherited path. - `packages/spec/src/ui/action.zod.ts`, the comment above the option shape's `guidance` table. Same correction. It points at the docblock for the pin and line. - `packages/spec/src/ui/action.test.ts`, the comment inside `keeps each rejection pointing at where that vocabulary IS real`. Same correction. - `.changeset/24047-action-param-options-comments.md`: `'@objectstack/spec': patch`, prose only (see "Changeset" below). No `guidance` string, schema, export or message text changes. All 34 changed lines in `packages/spec` are `*` or `//` comment lines. The mechanical check is under Verification. ## The pin reading `.objectui-sha` = `f8267df93aba8fb09cc1cc0692eadc0c8c5a5e56`. I read the files with `git show PIN:PATH` from the local objectui object store. - `packages/fields/src/resolveActionParams.ts:503` declares `normaliseOptions`. Line `:515` is `return { ...raw, label, value: raw.value };`. - Lines `:650`-`:651` hold the field-backed branch, `authoredOptions ?? normaliseOptions(field.options, ownerName, param.field, ctx.fieldOptionLabel)`. An authored list still wins over the inherited one. - Line `:241` is in the `resolveOptionLabels` docblock. It names objectui `fbc23e094` as the change that made `normaliseOptions` keep keys. The card quoted `:503` at objectui `main` `94b2489f`. At the pin the return is on `:515`, the line the claim also read. ## Readers on the inherited path: a source read at the pin, not a runtime measurement The card asked for any reader measurement to go here, not into a comment. I read the dialog path at the same pin: - `packages/app-shell/src/views/ActionParamDialog.tsx:536` re-spreads each option to localise its label (`{ ...o, label: pickLocalized(o.label, language) }`). Inherited keys survive this step as well. - `packages/app-shell/src/utils/paramToField.ts:102` passes `options: param.options` into the widget's field metadata unchanged. - The four option widgets build each option from `value` and `optionDisplayLabel(option)` and nothing else: - `packages/fields/src/widgets/SelectField.tsx:227-230` - `MultiSelectField.tsx:191-206` - `RadioField.tsx:134-145` - `CheckboxesField.tsx:152-168` `optionDisplayLabel` (`packages/core/src/evaluator/optionRules.ts:173`) reads only `label` and `value`. Each `disabled` in those four files is the field-level `props.disabled` (`SelectField.tsx:213`, `MultiSelectField.tsx:201`, `RadioField.tsx:129`, `CheckboxesField.tsx:163`), never a per-option one. None of the four files reads `color` or `icon`. So at the pin, an inherited option's `color`, `icon` and `disabled` reach the widget, and none of the four widgets reads them. That matches the current `guidance` texts, so this PR proposes no `guidance` change; the card puts any such change on a card of its own anyway. This reading has limits. It is a static source read of the four widgets the docblock already names, not a rendered dialog, and no test in this repo pins it. For that reason the comments say "not measured from this repo" and do not say "no reader". ## Changeset The card allowed no changeset, or a prose-only `patch` changeset if a gate requires one. `Check Changeset` requires either a changeset or the `skip-changeset` label. `@objectstack/spec`'s `files` array ships `src/**/*.zod.ts`, so the rewritten docblock in `src/ui/action.zod.ts` goes into the published tarball verbatim. "Publishes nothing" would therefore be false, and the changeset is the accurate declaration. Its body carries `Clause-②: no`. ## Verification All readings below are on the final head `c89eef4e3b` (the merge of `main`) unless marked otherwise. Build and test ran through `scripts/pm/os-verify-lock.sh`, and each exit code was captured before any pipe. - `pnpm --filter @objectstack/spec build`: exit 0. - `pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2`: exit 0, `Test Files 692 passed (692)`, `Tests 20952 passed | 1 todo (20953)`. - `pnpm --filter @objectstack/spec typecheck`: exit 0, ending `check:test-typecheck: OK`. - `action.test.ts` alone with `--reporter=verbose`, on the pre-merge head `56ad5cf153`: `Tests 150 passed (150)`, including `keeps each rejection pointing at where that vocabulary IS real`. - `pnpm --filter @objectstack/spec check:generated`: exit 0, `All 14 generated artifacts are up to date`. The rewritten docblock reaches no generated artifact. `check:api-surface` reports `public API surface + factory signatures unchanged`, and `check:docs` reports `227 generated files in sync`. - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 85 commands. All 85 exit 0. `--ran` reconciliation: `85 derived, 85 run, 0 NOT-MEASURED, 0 UNRUN`. Among them, `check:nul-bytes` reports OK, `check-empty-changeset` reports 1 declaring changeset added, and `check-changeset-no-major` reports no `major` bump. - On the pre-merge head the same sweep had three exit-3 results (`PREREQUISITE NOT MET`: `check:doc-formula-expressions`, `check:dual-build-cjs-loads`, `check:lean-entry-closure`), because their input packages were not built. After a build of `@objectstack/formula`, `@objectstack/lint` and `@objectstack/objectql`, all three exit 0 on the final head. - Local-only family `node scripts/check-dev-prereqs.mjs`: exit 0. - Comment-only check: `git diff -U0 origin/main...HEAD -- packages/spec` has 34 changed lines, and 0 of them start with anything other than `*` or `//`. A control-byte scan of the three files finds nothing. - No ablation: the diff changes no tested behaviour. - Not run locally, left to CI: `test:repo` (Test Core), the type-check lanes, and the families `dispatch-gates` lists outside its runnable set. - Since the merge, `origin/main` has gained two `packages/lint`-only commits. Neither touches the three files here. ## Acceptance notes - `packages/spec/CHANGELOG.md` still describes the old drop in two entries (lines 40254 and 73939). Both are release-owned history. AGENTS.md routes any change to a released entry through a dedicated docs-only PR, so this PR leaves them as they are. - The same `options` docblock has a `visibleWhen` bullet that says objectui's inline branch "does `options: param.options`". At the pin, the inline branch returns `options: authoredOptions` (`resolveActionParams.ts:574`), which is `resolveOptionLabels(param.options, ctx.locale)`. That call returns the list untouched when every label is already a string, and rebuilds it by spread otherwise. So the bullet's point, that the list is lowered verbatim, still holds; only the quoted expression is out of date. The claim's file surface names only the two comment blocks above, so this PR does not edit the bullet. Carrier: none. ## Deviations - Labels: none written. The dispatch's write budget names `git push`, one `pr_create` and the report comment, and no label write. - `main` was merged once before this PR opened (`c89eef4e3b`), as AGENTS.md section 10 asks. The incoming commits do not touch any of the three files. Spec was rebuilt and re-verified on the merged head. --- _Generated by [Claude Code](https://claude.ai/code/session_01LFqdRsuM5nkqoJdHd5ztVk)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 27d7c2a commit 462d02f

3 files changed

Lines changed: 34 additions & 14 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
Clause-②: no
6+
7+
The `ActionParam` `options` docblock in `src/ui/action.zod.ts`, and the comment above that option
8+
shape's `guidance` table, no longer say that objectui's `normaliseOptions` rebuilds each option a
9+
field-backed param inherits as a bare `{ label, value }` pair. At the console pin (`.objectui-sha`
10+
`f8267df93aba`) it keeps every key of an inherited option by spread
11+
(`packages/fields/src/resolveActionParams.ts:515` in objectui). The comments now give the reason
12+
that remains for the `guidance` texts not telling an author to make a param field-backed and
13+
inherit `color`, `icon` or `disabled`: the keys survive, but no reader of them on that path is
14+
measured. No key, schema, export, accepted value or message text changes.

‎packages/spec/src/ui/action.test.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1754,8 +1754,9 @@ describe('action param option vocabulary — declared only where a renderer deli
17541754

17551755
// `color`: real one layer down, on the STORED-value display path. The
17561756
// sentence must no longer defer to #5016 as an open question — it is
1757-
// decided — and must not promise the field-backed inheritance route, which
1758-
// `normaliseOptions` still drops (ledger finding 18).
1757+
// decided — and must not promise the field-backed inheritance route:
1758+
// objectui's `normaliseOptions` keeps the key by spread, but no reader of
1759+
// it on that route is measured (ledger finding 18).
17591760
const color = messageFor({ label: 'A', value: 'a', color: 'red' });
17601761
expect(color).toContain('SelectOptionSchema');
17611762
expect(color).not.toContain('do not rely on it today');

‎packages/spec/src/ui/action.zod.ts‎

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -265,15 +265,18 @@ export const ActionParamSchema = lazySchema(() => strictObject(
265265
* `default` it would actively mislead, since a dialog param defaults
266266
* through `defaultValue` one level up.
267267
*
268-
* **What this does NOT fix**, deliberately, because it is objectui's and not
269-
* the spec's: a FIELD-BACKED param that inherits its list instead of declaring
270-
* one still loses the key. `resolveActionParam` reaches
271-
* `param.options ?? normaliseOptions(field.options, …)`, and `normaliseOptions`
272-
* rebuilds every inherited entry as `{ label, value }`. That drop predates
273-
* this change, is invisible to it (an authored `options` array wins over the
274-
* inherited one), and is tracked in objectui — so the guidance below still
275-
* refuses to prescribe "make it field-backed and inherit" (ledger finding 18:
276-
* a confidently wrong prescription is worse than none).
268+
* **A FIELD-BACKED param that inherits its list** instead of declaring one
269+
* takes objectui's path, not the spec's. At the console pin (`.objectui-sha`
270+
* `f8267df93aba`), objectui's `packages/fields/src/resolveActionParams.ts`
271+
* resolves it as `authoredOptions ?? normaliseOptions(field.options, …)`
272+
* (`:650`–`:651`), and `normaliseOptions` returns
273+
* `{ ...raw, label, value: raw.value }` per entry (`:515`), so every key of an
274+
* inherited entry survives the resolver. An authored `options` array still
275+
* wins over the inherited one. Surviving is not being read: no reader of
276+
* `color` / `icon` / `disabled` on an inherited option is measured from this
277+
* repo, so the guidance below still refuses to prescribe "make it
278+
* field-backed and inherit" (ledger finding 18: a confidently wrong
279+
* prescription is worse than none, and an unmeasured one is no cure for it).
277280
*
278281
* The aliases are anchored on `SelectOptionSchema`'s own curated table (the
279282
* same idea, one layer down) rather than on edit distance, and deliberately
@@ -307,9 +310,11 @@ export const ActionParamSchema = lazySchema(() => strictObject(
307310
// The per-option keys that are real one layer down but have no reader on
308311
// THIS path, plus the two that no spec shape declares at all. Each says
309312
// where the vocabulary lives and — critically — does NOT promise that a
310-
// field-backed param inherits it: `resolveActionParams`' `normaliseOptions`
311-
// rebuilds each inherited entry as `{ label, value }`, so that promise
312-
// would be false in exactly the way ledger finding 18 warns about.
313+
// field-backed param inherits it. objectui's `normaliseOptions` keeps an
314+
// inherited entry's keys by spread (the `options` docblock above cites the
315+
// pin and line), but no reader of them on the inherited path is measured
316+
// from this repo, so that promise would be an unmeasured one in exactly
317+
// the way ledger finding 18 warns about.
313318
//
314319
// `visibleWhen` is deliberately absent: it is a declared key now, and
315320
// `guidance` is consulted only from the `unrecognized_keys` path, so an

0 commit comments

Comments
 (0)