Repository navigation
Commit 3f69471
fix(spec): the ui-bulk-action-param-unknown-keys-refused migration text names the promoted field-backed bulk route (#22888)
Fixes #22858
Clause-②: no
## What changed
Text only, in one file:
`packages/spec/src/migrations/entries/semantic/18.ui-bulk-action-param-unknown-keys-refused.ts`,
the major-18 D3 entry that `objectstack migrate meta` prints. Two
clauses said the bulk surface has no field-backed param route. Since
objectui `29b99490` (an ancestor of the console pin), a PROMOTED bulk
action resolves its field-backed params through the single-record
dialog's resolver. Both clauses now say that the AUTHORED
`bulkActionDefs[].params` surface has no such route, name the promoted
route, and name its two keys (`accept` / `maxSize` arrive from the
field). The wording follows what `581ca7d4e4` landed in
`packages/spec/src/ui/bulk-action.zod.ts`: the module comment at
`:151`-`:170`, the `field` guidance at `:211`-`:221`, and the
widget-config prescription at `:252`-`:267`.
Plus a `patch` changeset for `@objectstack/spec`. No regeneration
commit: `registry.ts`, `spec-changes.json` and the upgrade guide are
git-ignored. `packages/spec/CHANGELOG.md` is not touched; it is
release-owned.
### Before and after (evaluated strings, clause by clause; every other
clause is byte-identical)
**`replacement`, before:**
> the keys that belong one layer out are pointed there (`visible` and a
capability gate belong on the DEF, `visibleWhen` belongs on an
`options[]` entry, `field` / `objectOverride` / `carryOver` /
`defaultFromRow` / `requiresFeature` are field-backed ACTION-param
contracts the bulk surface does not implement); and the widget-config
family (…) is answered with one prescription naming `FieldSchema` as the
shape those keys are real on.
**`replacement`, after:**
> the keys that belong one layer out are pointed there (`visible` and a
capability gate belong on the DEF, `visibleWhen` belongs on an
`options[]` entry, `carryOver` / `defaultFromRow` / `requiresFeature`
are ACTION-param contracts the bulk surface does not implement); `field`
/ `objectOverride` declare a FIELD-BACKED param, and an authored
`bulkActionDefs[].params` entry has no such route — it reaches the
dialog as written, and nothing on that path resolves it against the
object's field definitions — so the `field` rejection offers two fixes:
declare the param inline, or declare the action on the object with that
field-backed param and name it in the view's `bulkActions`, the PROMOTED
route, whose params are resolved through the single-record dialog's
resolver when the bulk dialog opens; and the widget-config family (…) is
answered with one prescription naming `FieldSchema` as the shape those
keys are real on, and saying that of them only `accept` / `maxSize`
declared there reach this dialog, through a promoted action.
**`reason`, before:**
> ⛔ Do not read their rejection as "the renderer ignores them", and ⛔ do
not answer it by declaring the key on the object's FIELD: the bulk
surface has no field-backed param route, so that value does not reach
this dialog either.
**`reason`, after:**
> ⛔ Do not read their rejection as "the renderer ignores them", and ⛔ do
not answer it by declaring the key on the object's FIELD and keeping the
authored def: an authored `bulkActionDefs[].params` entry has no
field-backed param route, so that value does not reach this dialog
either. The one route that consults the field is a PROMOTED action — an
action the object declares, named in the view's `bulkActions`, whose
field-backed params (`{ field }`) are resolved through the single-record
dialog's resolver when the bulk dialog opens — and of these keys only
`accept` / `maxSize` arrive that way: `lookupFilters` / `lookupColumns`
/ `lookupPageSize` / `descriptionField` are resolved and then dropped on
the bulk path, and the rest are never read off the field.
The diff also re-wraps two string-concatenation lines with no byte
change to the evaluated text. That was proved by evaluating `entry` at
the base `75b6d6a048` and at this head, splitting each field at clause
boundaries, and diffing: only the clauses quoted above differ, and
`surface` / `acceptanceCriteria` are identical.
## The claim, verified at the console pin
`.objectui-sha` at the base is `4997995022` (the card was filed against
`0df67f237c`; the pin has moved since). `git merge-base --is-ancestor
29b99490 4997995022` exits 0, which is self-proving even in a shallow
checkout. All readings use `git show 4997995022:…` in objectui:
- **The promoted route resolves field-backed params.**
`packages/plugin-grid/src/ObjectGrid.tsx:5174`-`:5183`:
`resolveDialogParams` calls `resolvePromotedBulkParams(def, {
objectName, objects, fieldLabel, fieldOptionLabel, locale })` when the
dialog opens, for a def carrying `actionDef` (a promoted one).
`packages/plugin-grid/src/resolveBulkActions.ts:197`-`:212`: that
function passes the action's params to `resolveActionParams` from
`@object-ui/fields`, the single-record dialog's resolver, then maps each
result through `toBulkParam`.
- **`accept` / `maxSize` arrive.**
`packages/fields/src/resolveActionParams.ts:781`-`:782` (field-backed
branch): `accept: param.accept ?? field.accept`, `maxSize: param.maxSize
?? field.maxSize`. `resolveBulkActions.ts:172`-`:173`: `toBulkParam`
forwards both.
`packages/plugin-grid/src/components/bulkParamToField.ts:163`, `:167`:
the `...extra` spread carries them onto the widget's field bag.
- **`lookupFilters` / `lookupColumns` / `lookupPageSize` /
`descriptionField` are resolved, then dropped.**
`resolveActionParams.ts:757`, `:759`-`:761` resolve them (with the snake
`id_field` read at `:756`). `toBulkParam`
(`resolveBulkActions.ts:159`-`:175`) emits none of them; its docblock at
`:146`-`:149` says so.
- **The rest are never read.** The field-backed return
(`resolveActionParams.ts:766`-`:784`, with the lookup extras at
`:754`-`:762`) reads no `min`, `max`, `step`, `precision`, `scale`,
`rows`, `crop`, `capture`, `dimensions`, `defaultName`, `allowCreate`,
`picker`, `subtitle` or `avatarField` off the field.
- **`carryOver` / `defaultFromRow` / `requiresFeature` stay
unimplemented on the promoted route too,** which is why the replacement
keeps that clause. `toBulkParam` forwards neither `carryOver` nor
`visible` (the target that `requiresFeature` is lowered into), and the
resolver context at `ObjectGrid.tsx:5177`-`:5183` passes no `row`, so
`defaultFromRow` has nothing to read.
`STEP18_RATIONALE` in
`packages/spec/src/migrations/registry.ts.template` carries no fragment
for this entry: the sorted list goes from `ui-ai-chat-window-retired`
(`:2747`) to `ui-form-layout-inline-grid-retired` (`:2762`), and the
template has zero `field-backed` hits. So nothing moves there.
The new text carries no tracker id.
`packages/cli/test/migrate-meta-engine-guidance.test.ts` refuses a hash
sign followed by four or five digits in the printed block.
## Verification
Every reading below is at head `30e041142c` (base `75b6d6a048`), in a
fresh worktree, with each verdict read from the tool's own line.
- **`@objectstack/spec` build** (lock): `VERDICT command-exit 0`. It
runs `gen:migration-registry` (`build-migration-registry --self-test:
ok`, wrote 422 semantic entries). The new text is in the generated
`registry.ts` (git-ignored) and in `dist/migrations` (one hit each), and
the old sentence has zero hits. The build left `git status` clean.
- **`@objectstack/spec` test** (`local` project): 648 files, 19375
passed, 1 todo, exit 0. **typecheck**: exit 0 (`tsc --noEmit`,
`check:scripts-typecheck`, `check:test-typecheck` OK).
- **`scripts/step18-rationale-merge.test.ts`** (`repo` project, the one
`repo` test that reads the template): 9/9 passed, exit 0.
- **CLI closure build** `turbo run build --filter='@objectstack/cli^...'
--concurrency=2`: 58/58 tasks successful, `VERDICT command-exit 0`.
- **`packages/cli/test/migrate-meta-engine-guidance.test.ts`**
(`--project integration`, after that build): 3/3 passed, exit 0. It
spawns `os migrate meta --all`, finds every semantic entry's block
verbatim in stdout (this one included), and refuses a tracker id in it.
- **`node scripts/check-adr-0087-registration.mjs --base origin/main`**:
`this PR adds no declared-breaking changeset (1 non-breaking
changeset(s) seen)`.
- **`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`** (no paths): 82 commands, of which 81 were
run, all exit 0. Three of them first answered `PREREQUISITE NOT MET`
(exit 3: `doc-formula-expressions`, `dts-closure`, `lean-entry-closure`,
which need built workspace packages). They were re-run after the CLI
closure build and exit 0. `--ran` reconciliation: `82 derived, 81 run, 0
NOT-MEASURED, 1 UNRUN`.
- The UNRUN one is `pnpm check:dual-build-cjs-loads`: NOT MEASURED,
reason: it needs a whole-workspace build, which this dispatch rules out.
- The spec gates that read `dist` (`check:api-surface`,
`check:authorable-surface`, `check:docs`, `check:spec-changes`,
`check:upgrade-guide`, `check:liveness`, `check:strictness-ledger` and
the rest, 18 in all) ran after the spec build.
`check:authorable-surface` prints only the allowed `baseRev`-lag info
line.
- **Artifact rosters** (the 50 families the no-path run lists,
PR-context guards wired to this PR): run after this PR opened, because
the PR-context guards read its number and body. Their results are in the
`os-dev-report` comment on the card, not here: this body is written
once.
- **NOT MEASURED**: the five path-scheduled CI jobs and the four
type-check lanes the derivation names as CI-only (Test Core, Temporal
Conformance, Dogfood Regression Gate, Dogfood Verify CLI, Build Core;
the workspace typecheck filters). Reason: they are CI's own shell over
CI's filters, with no local invocation.
- **Not merged with `origin/main`**: two commits have landed since the
base (`72b26ed4f5` and `40bc51f3f9`). Neither touches either path in
this diff. The PR's merge ref and the merge queue test the joint state.
## Acceptance notes
- **`acceptanceCriteria` is left untouched, as ruled.** Its closing
sentence still says that where a widget-config key's behaviour is gone,
"the remedy is a spec issue asking for the key, not a local workaround".
For `accept` / `maxSize` there is now a second remedy: the landed
prescription's "for `accept` / `maxSize` move the param onto a promoted
action". The `reason` clause printed just above it now states that
remedy, so the printed block is not wrong, only incomplete in its last
sentence. Whether to align that sentence is a call for the reviewing
seat. Carrier: the seat reviewing this PR. Noted here; no card is filed.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 6373952 commit 3f69471
2 files changed
Lines changed: 40 additions & 11 deletions
File tree
- .changeset
- packages/spec/src/migrations/entries/semantic
Lines changed: 14 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
Lines changed: 26 additions & 11 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
29 | 35 | | |
30 | 36 | | |
31 | | - | |
32 | | - | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
33 | 41 | | |
34 | 42 | | |
35 | 43 | | |
| |||
51 | 59 | | |
52 | 60 | | |
53 | 61 | | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
59 | 74 | | |
60 | 75 | | |
61 | 76 | | |
| |||
0 commit comments