Skip to content

fix(spec): the bulk action param rejection text names the promoted field-backed route (#22780) - #22852

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-22780-bulk-promoted-route-text
Oct 11, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-22780-bulk-promoted-route-text

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #22780
Clause-②: no

Text only. Four sentences in packages/spec/src/ui/bulk-action.zod.ts said the bulk surface has no field-backed param route. Since objectui 29b99490, a PROMOTED bulk action's field-backed param resolves through the single-record dialog's resolver, and the console pin now carries that commit. Each sentence now names the promoted route. The authored bulkActionDefs[].params refusal stays; only its reason and remedy change. No schema shape, accept set or export moves.

Implemented by os-dev for the domain:spec seat 2, PM session session_016njDy8ozy9B9Ns5Y8kAWEK, branch claude/issue-22780-bulk-promoted-route-text.

What changed

  1. BulkActionParamSchema guidance.field (shipped in every field rejection). The reason is now "an authored bulkActionDefs[].params entry has no such route: it reaches the dialog as written". The remedy offers two fixes. Declare the param inline (name + type, plus object / labelField for a picker). Or declare the action on the object with the field-backed param ({ field }, plus objectOverride) and name it in the view's bulkActions. It also says the promoted form runs the action once per selected record, because that changes what the button does.
  2. The BULK_PARAM_WIDGET_CONFIG_KEYS prescription (shipped in every widget-config rejection). The "does not reach this dialog either" clause is replaced. A key declared on the FIELD reaches the bulk dialog only through a promoted action. The prescription names what that route carries, and says that of the refused keys only accept / maxSize arrive that way.
  3. The JSDoc above BULK_PARAM_WIDGET_CONFIG_KEYS says the same, with the measurement it rests on.
  4. The dependsOn JSDoc now reads "one vocabulary, two doors". The FIELD is read by the single-record dialog and by a promoted bulk def. The param is where an authored def writes the key.

What the promoted route carries, re-measured on objectui main dca25aff2

These are read from source: plugin-grid resolvePromotedBulkParams / toBulkParam, and @object-ui/fields resolveActionParam.

  • Carried from the field: type, translated label, options (with translated option labels), required, description (as the help text; the resolver reads field.help ?? field.description), placeholder, defaultValue, multiple, accept, maxSize. For a reference-bearing field (EXPANDABLE_FIELD_TYPES) it also carries reference (as object), displayField (as labelField) and dependsOn.
  • Resolved, then dropped by toBulkParam: lookupFilters, lookupColumns, lookupPageSize and descriptionField, plus the snake-only id_field / title_format reads.
  • Never read off the field: the rest of the widget-config list (min, max, step, precision, scale, rows, crop, capture, dimensions, defaultName, allowCreate, picker, subtitle, avatarField).
  • idField is not named as "dropped" in the shipped text. FieldSchema refuses idField (unrecognized_keys, measured), so no field can declare it.
  • Unchanged since 29b99490: git log 29b99490..origin/main over resolveActionParams.ts, resolveBulkActions.ts and bulkParamToField.ts lists no commit. The two commits since then that touch ObjectGrid.tsx (752504520, 69bca0af6) change the write census and the paged footer's fan-out query, not the param path; BulkActionDialog.tsx is unchanged.

This agrees with the card's list and objectui#12119's contract review. It adds required, description, placeholder and multiple, which the resolver also inherits.

Premise checks

  • Console pin: .objectui-sha = 0df67f237c. git merge-base --is-ancestor 29b99490 0df67f237c exits 0 in ../objectui. An exit 0 proves itself, so no control leg is owed.
  • Phrase family: I grepped packages/spec, content/docs and skills. Two other places carry the "no field-backed route" claim. Neither is edited here (see Acceptance notes). content/docs and skills/** carry no hit.
  • Generated artifacts: none of the four sentences reaches a generated artifact. check:generated reports all 14 up to date, and check:docs reports 226 generated files in sync. No reference page moves.

Pins

The two cases in bulk-action.test.ts that read these strings are re-pointed at named subjects instead of prose:

  • the widget-config case asserts `bulkActions` and `accept` / `maxSize` (it pinned 'no field-backed param route');
  • the field case asserts no rename arrow, plus `object` and `bulkActions; it pinned 'FIELD-BACKED' and 'Declare the param inline'.

Reverse verification. The fix was committed first. The base text of bulk-action.zod.ts was then restored into the tree with git restore --source=1eff3224d7 (blob 281e495c), and the file run went red: Tests 2 failed | 37 passed (39), exactly the two re-pointed cases. Restored with git checkout HEAD --: blob 2e798a0b equals the HEAD blob, and git status is clean. At HEAD: Tests 39 passed (39).

Tests and gates

Taken at cbce5d7831, which is origin/main 31b5a5f7f5 merged in. Since then main has moved by two commits (metadata-protocol, scripts/pm/os-regen-merge.sh, one docs page). Neither touches packages/spec or these files.

  • pnpm --filter @objectstack/spec build: exit 0.
  • pnpm --filter @objectstack/spec typecheck: exit 0, including check:test-typecheck OK.
  • pnpm --filter @objectstack/spec test: Test Files 648 passed (648), Tests 19342 passed | 1 todo.
  • pnpm --filter @objectstack/spec check:generated: "All 14 generated artifacts are up to date".
  • dispatch-gates --commands --repo objectstack-ai/objectstack: 85 commands derived, all run with exit codes recorded. --ran reconciliation: "85 derived famil(ies) accounted for — 84 run, 1 NOT-MEASURED".
    • Two gates first refused on an unbuilt prerequisite and went green once those packages were built: check:doc-formula-expressions (@objectstack/formula / @objectstack/lint) and check:lean-entry-closure (@objectstack/objectql).
    • NOT MEASURED: check:dual-build-cjs-loads. Reason: it reads every package's dist/ (88 unbuilt), a whole-repo build left to CI.
  • Lint, narrowed to the two changed .ts files:
    • Population: eslint.config.mjs files: ['**/*.{ts,…}'] and ['packages/**/*.{ts,…}']; neither file was reported ignored.
    • Count: --no-inline-config --format json gave 2 results, 0 errors, 0 warnings.
    • Invariance: the config sets no parserOptions.project / projectService, so no type-aware linting. --print-config shows parserOptions = { ecmaVersion, sourceType }. This diff cannot change a verdict on an untouched file. Repo-wide pnpm lint is CI's.

Acceptance notes (noted, not filed)

  • The same claim in the major-18 migration entry. packages/spec/src/migrations/entries/semantic/18.ui-bulk-action-param-unknown-keys-refused.ts (reason, and the replacement clause "field-backed ACTION-param contracts the bulk surface does not implement") still says "the bulk surface has no field-backed param route". The text is generated into registry.ts and printed by objectstack migrate meta. It is not edited here, for two reasons: it is outside this card's claimed file surface, and it brings its own gate family (gen:migration-registry, check:spec-changes, check:upgrade-guide, and the cli's migrate-meta-engine-guidance test). It is named for the seat in the report. Its conclusion for the widget-config keys still holds, except accept / maxSize on a promoted def.
  • packages/spec/CHANGELOG.md (17.5.0 entry) carries the same sentence. That file is release-owned, and the sentence was true when it shipped, so nothing is owed there.
  • Read from source, not pinned in objectui: an authored execution: 'aggregate' def that names a declared action and authors no params gets that action attached, and resolvePromotedBulkParams then resolves the action's field-backed params too. The shipped text names only the bulkActions route, the one objectui#12119 pins.
  • The objectui-side remainder is unchanged: forwarding the lookup settings on the bulk path. The prescription now states the gap instead of implying it.
  • Two other comments in the file are still true and are not edited: the module header's "converter for the PROMOTED direction (toBulkParam)" and the alias comment "toBulkParam maps the same three".

Generated by Claude Code

… route

The four sentences in bulk-action.zod.ts that said the bulk surface has no
field-backed param route now name the promoted route (an action the object
declares, named in the view's bulkActions), whose field-backed params the
renderer resolves through the single-record dialog's resolver. The authored
bulkActionDefs[].params refusal stays; its reason and remedy change, and the
widget-config prescription states which field settings the promoted route
carries. Text only: no schema shape or accept-set change.

Claude-Session: https://claude.ai/code/session_016njDy8ozy9B9Ns5Y8kAWEK
Co-authored-by: Claude <noreply@anthropic.com>
…hema has no idField)

Claude-Session: https://claude.ai/code/session_016njDy8ozy9B9Ns5Y8kAWEK
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 139 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json d48931871479ccc303e10493e80799225b220038 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 5eb015585338e7ccc1bad3c097d93ffc504ea9a4 — the merge of head cbce5d7831070cb1de797e8687ab7dbcea7b999f into base d48931871479ccc303e10493e80799225b220038, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 5eb015585338e7ccc1bad3c097d93ffc504ea9a4 && git checkout 5eb015585338e7ccc1bad3c097d93ffc504ea9a4
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d48931871479ccc303e10493e80799225b220038 cbce5d7831070cb1de797e8687ab7dbcea7b999f && git checkout -B drift-repro d48931871479ccc303e10493e80799225b220038 && git merge --no-ff cbce5d7831070cb1de797e8687ab7dbcea7b999f

node scripts/docs-audit/affected-docs.mjs --json d48931871479ccc303e10493e80799225b220038

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: cbce5d7831070cb1de797e8687ab7dbcea7b999f
Local-runs: none

Inputs read: card #22780 (body; comments 6106434300 triage, 6109279475 re-entry, 6109384020 claim, 6109848334 os-dev-report); PR #22852 (body, 3-file list, net diff origin/main...cbce5d78, merge-base 31b5a5f7f5); objectui#12119 body, its merge commit 29b99490, its contract review 6106031964; .objectui-sha on main and at the head, both 0df67f237c (29b99490 is its ancestor: exit 0, reverse control exit 1); objectui sources at that pin. Check-runs on the head: 35, 32 success, 3 skipped (Build Docs, Console Pin Gate, Packed-tarball smoke, path-filtered or opt-in), 0 failure or pending; all seven required contexts green (Lint & Repo Gates, TypeScript Type Check, Test Core, Dogfood Regression Gate, Build Core, Temporal Conformance (live PG + MySQL), Governed Surface Queue Guard). Not a fork PR, no governed surface, 100 changed lines, still draft.

① Derived judgments

  • Accept set unchanged — right. The .zod.ts diff touches guidance.field, the BULK_PARAM_WIDGET_CONFIG_KEYS prescription string and two docblocks only; shape keys, aliases, the BULK_PARAM_WIDGET_CONFIG_KEYS array and the dependsOn member are byte-identical. No export moves (Type Check · source gates green covers check:api-surface, check:generated --reconcile-only, check:docs, check:spec-changes, check:upgrade-guide); Spec property liveness green. No generated artifact carries any of the four sentences: a grep of the head tree for the old claim finds only packages/spec/CHANGELOG.md 17.5.0 and migration entry 18 (③).
  • The authored bulkActionDefs[].params field refusal still fires — right, only its reason and remedy moved. The objectui pin agrees: resolvePromotedBulkParams returns an authored def by reference when def.params is an array (resolveBulkActions.ts:202), and bulkParamToField does no field lookup — so "an authored entry reaches the dialog as written" is true.
  • Promoted route at the pin, (a) — right. ObjectGrid.resolveDialogParams (:5106-5116) awaits metadata.ensureType('object'), then resolvePromotedBulkParams(def, ctx), which calls resolveActionParams imported from @object-ui/fields (resolveBulkActions.ts:89-93, 197-212) — the single-record dialog's resolver. The four param-path files are byte-identical at 29b99490, the pin and dca25aff2 (the dev's anchor), so the report's measurement transfers to the pin.
  • (b) The key list — right, key by key. The resolver's field-backed output (fields/src/resolveActionParams.ts:766-784) inherits type (field.type), translated label (ctx.fieldLabel), options (normaliseOptions, labels through fieldOptionLabel), required, placeholder, helpText = field.help ?? field.description, defaultValue, multiple, accept, maxSize; for EXPANDABLE_FIELD_TYPES the lookupExtras block (:754-762) adds referenceTo (field.reference), displayField, dependsOn. toBulkParam (resolveBulkActions.ts:155-176) forwards exactly name, label, type, required, help, default, options, placeholder, multiple, object, labelField, dependsOn, accept, maxSize (plus the unresolvedField hand-off). Every key the prescription names arrives; nothing arrives that it omits. FieldSchema declares description and no help (field.zod.ts:1146), so "description (as the help text)" is the exact spec-side spelling; the dialog renders param.help under the control (BulkActionDialog.tsx:799) and seeds default (:150).
  • (c) — right. lookupFilters / lookupColumns / lookupPageSize / descriptionField are resolved (:757-761) and not forwarded by toBulkParam; so are the snake id_field / title_format reads (:756, :758).
  • (d) — right. FieldSchema is strictObject (field.zod.ts:1055) with no idField member (the spec's only idField is InlineGridColumnSchema, :957); authoring-key-lint.test.ts:163 pins shapeKeys(FieldSchema) without it, and authoring-key-lint.ts:146 carries the id_field guidance. idField is refused with unrecognized_keys; the resolver reads only snake id_field. The docblock's parenthesis is true, and not calling idField "dropped" is right.
  • "the rest are never read off the field" — true. RuntimeField (:348-429) declares none of min/max/step/precision/scale/rows/crop/capture/dimensions/defaultName/allowCreate/picker/subtitle/avatarField, and the output is built key by key with no spread of field.
  • "The promoted form runs the action once per selected record" — true at the pin. toBulkActionDef emits operation: 'custom', actionDef, batchSize: 25, no execution (:237-256); useBulkExecutor takes the non-aggregate branch and its perRow custom case calls runAction(def, row, params) per id (:306-335); ObjectGrid.runBulkActionRecord is one executeAction per row (:5163-5189), after the def's visible partition (:5128). Same semantics the file's own docblock (:340-343) and the superRefine message already state. The dev's added sentence is accepted.
  • "reaches this dialog only through a PROMOTED action ... named in the view's bulkActions" — true for the pinned door, narrower than source by one unpinned door. An authored execution: 'aggregate' def that names a declared action and authors no params gets the same toBulkActionDef promotion merged under it (:442-458) and resolvePromotedBulkParams then resolves its field-backed params (:202). A claim as narrow as the pinned enforcement (Prime Directive chore: version packages #10 corollary), disclosed by the dev; ③ finding 4.
  • dependsOn docblock — true. dependsOn is inherited only inside lookupExtras (reference-bearing types) and forwarded by toBulkParam; "a reference-bearing field's dependsOn" is the exact bound. "An AUTHORED def has no such route" true (:202).
  • Head vs pin. The head's .objectui-sha equals main's (0df67f237c), so every sentence true at the pin is true at the head. At the card's base (e84aeb36c, pin 20c6d351ad) the old sentences were true — the hold and the re-entry were both right.
  • Docblock citation "Measured on objectui main dca25aff2" — a reading, and it transfers: the pin is an ancestor of dca25aff2 and the resolver, adapter, dialog and resolveBulkActions.ts are identical between them.
  • Unchanged neighbours still true at the pin: the module header's "converter for the PROMOTED direction (toBulkParam)" and the alias comment "toBulkParam maps the same three" (resolveBulkActions.ts:164-170 still renames helpText/defaultValue/referenceTo/displayField); the objectOverride guidance ("A bulk param is always inline") is about this authored shape and holds.
  • Pins re-pointed — right, they pin subjects. :70-85: the closed-backtick bulkActions token matches "named in the view's bulkActions." in the prescription, and the accept / maxSize pair appears twice there; the route and the two keys are the sentence's subjects, and the one-prescription count pin (:435-439) still holds on "widget-config keys like". :420-429: no rename arrow for field, the backticked object (the inline remedy's picker target) and the open-backtick bulkActions prefix (matches the bulkActions: list literal). object alone would be a weak substring, but for { field: 'owner' } only the field guidance fires (the objectOverride guidance, which also says object, does not), and paired with the other two assertions the case pins both remedies. The report's reverse verification (base text restored: 2 failed / 37 passed, exactly these two) is consistent.
  • Refusal text pinned by a consumer or test: spec bulk-action.test.ts :83-84 and :423-428, both in this diff. cli: none — migrate-meta-engine-guidance.test.ts holds each printed block read from the registry at run time and lists ui-bulk-action-param-unknown-keys-refused at :300 as a roster id, not a text pin. objectui at the pin: none — BulkActionDialog.tsx:690 restates the authored-def refusal in a comment; the two spec-parity tests parse only.
  • Runtime strings carry no tracker number (Lint & Repo Gates green); the bracketed action-name placeholder inside the bulkActions list literal is the pattern the file already uses at :114 and :428.

② Semver level

  • Changeset .changeset/22780-bulk-param-promoted-route-guidance.md: @objectstack/spec: patch, body carries Clause-②: no, no ADR-0087 marker (none owed: nothing removed or renamed). Check Changeset green.
  • The diff publishes two reworded runtime refusal strings, two docblocks and two test pins: no accept-set move, no export move, no .describe() change, no generated reference page moves. Patch — right (a fix to shipped text in a released package takes patch, never none). Clause-②: no — right, no arm; the claim's line "no (describe and guidance text only; the accept set does not move)" right, with one precision: the diff moves guidance and prescription strings plus docblocks, not a .describe(), which is why check:docs reports nothing. The changeset body's list of what the promoted route carries is the prescription's list, verified above.

③ Boundary flags

open_questions: none declared.

Deviations, each answered:

  1. Model-free commit trailers and the session-URL PR footer — accepted: all three branch commits carry Claude-Session plus Co-authored-by: Claude (read off the branch), the form AGENTS.md requires; the harness reminder does not override it.
  2. origin/main moved after the merge — accepted, with a correction: at review time main is four commits past the merge-base (8c105e3d67, d489318714, 8558668a68, 12b9daf749), not two; 12b9daf749 touches 13 packages/spec files (automation value slots and migration entry 18.flow-value-slot-template-dialect-refused) but none of this PR's three files or bulk-action.*; the queue leg rebuilds on current main.
  3. The added "runs once per selected record" sentence — accepted, true at the pin (①).

Out-of-scope findings, each answered or escalated:

  1. Migration entry 18 — measured false at the pin, carrier: a NEW CARD. packages/spec/src/migrations/entries/semantic/18.ui-bulk-action-param-unknown-keys-refused.ts replacement (:25-26, "field-backed ACTION-param contracts the bulk surface does not implement") and reason (:53-55, "the bulk surface has no field-backed param route, so that value does not reach this dialog either") are false for the promoted route: the first as a blanket statement (true of the authored-def surface the entry governs), the second for accept / maxSize on a promoted def (true for every other refused key). Identical on main, untouched here. Carriers: registry.ts (gitignored, regenerated on install, build, test and typecheck), spec-changes.json (no committed copy; CI generates in memory), the upgrade guide (docs/protocol-upgrade-guide.md is tracked but renders no entry-18 text; spec is 17.7.0, major 18 unshipped) and the objectstack migrate meta printout (the cli guidance test holds each block from the registry and refuses tracker numbers; no literal pin). So the fix is text-only in one file with no regeneration commit; the gate family the dev names runs on every PR regardless. Why not a patch round here: the claim's file surface names bulk-action.zod.ts, its references, its pins and one changeset with "stop on breach", the dev honoured it, and a review of record does not widen a dispatched claim after the fact; the entry is ADR-0087 D3 release-text with a different reader (an upgrader replaying 17 → 18) and its own acceptance criterion (the printed block); it is cheap and unhurried before major 18 ships. Draft for the seat to file, unassigned:
    • Title: spec: migration entry ui-bulk-action-param-unknown-keys-refused says the bulk surface has no field-backed param route, but the console pin carries objectui 29b99490
    • Body: Filing gate ①: a metadata-authoring trap in shipped guidance text, carried forward from spec: bulk-action.zod.ts says the bulk surface has no field-backed param route, but since objectui#12119 a promoted bulk action's field-backed param resolves #22780 (PR fix(spec): the bulk action param rejection text names the promoted field-backed route (#22780) #22852), whose contract review found the same claim outside that card's file surface. packages/spec/src/migrations/entries/semantic/18.ui-bulk-action-param-unknown-keys-refused.ts says in replacement (about :25-26) that field / objectOverride are "field-backed ACTION-param contracts the bulk surface does not implement", and in reason (about :53-55) "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". Since objectui#12119 (29b99490, carried by .objectui-sha 0df67f237c) a PROMOTED bulk action, an action the object declares named in the view's bulkActions, resolves its field-backed params through the single-record dialog's resolver, and of the refused widget-config keys accept / maxSize arrive from the field that way; lookupFilters / lookupColumns / lookupPageSize / descriptionField are resolved and dropped, the rest are never read. The entry is printed by objectstack migrate meta (cli migrate-meta-engine-guidance.test.ts holds each block verbatim and refuses tracker numbers) and projected into registry.ts, spec-changes.json and the upgrade guide at generation time, none committed, so the fix is text-only in that one file with no regeneration commit: reword the two sentences to say the authored bulkActionDefs[].params surface has no such route, and name the promoted route and the two keys, in the wording PR fix(spec): the bulk action param rejection text names the promoted field-backed route (#22780) #22852 landed in bulk-action.zod.ts. Spec is at 17.7.0, so the text has not shipped in a major-18 tarball; packages/spec/CHANGELOG.md 17.5.0 carries the same sentence and stays as shipped (release-owned, true when written). Clause-②: no. Dedupe: ui-bulk-action-param-unknown-keys-refused field-backed; bulk surface has no field-backed param route migration; promoted bulk action field-backed migrate meta guidance.
  2. CHANGELOG 17.5.0 (:8679, :8689) — "historical record, not an erratum" confirmed. The sentence was true when 17.5.0 shipped (objectui 29b99490 merged 2026-10-11 and reached the pin through chore(objectui): bump the console pin to 0df67f237c5a (carries objectui#12089) #22812 the same day); AGENTS.md's docs-only amendment route is for an entry that was wrong when released, and the next entry, compiled from this changeset at version time, records the change through the ordinary channel. No edit owed.
  3. The bulk adapter drops the lookup settings — already escalated to the objectui domain:ui seat at 6106031964 finding 1; the spec prescription now states the gap, which is what that review asked of the objectstack follow-up. No objectstack card; the card-or-fold answer is still owed on the objectui side. Noted.
  4. The aggregate-def door (an authored execution: 'aggregate' def naming a declared action with no authored params resolves field-backed params too) — accepted as a claim narrower than the pinned enforcement; the door is unpinned in objectui (bulkFieldBackedParam-12105.test.tsx pins the bulkActions door only). The carrier the dev names ("whoever next touches resolvePromotedBulkParams") is not a card: escalated to the objectui domain:ui seat as a one-line rider on finding 3's card, pin that door or leave it unnamed; the spec text widens only after a pin.

Implemented-by: claude/issue-22780-bulk-promoted-route-text
Reviewed-by: session_016njDy8ozy9B9Ns5Y8kAWEK

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 11, 2026 14:38
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 11, 2026 14:38
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 11, 2026
Merged via the queue into main with commit 581ca7d Oct 11, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-22780-bulk-promoted-route-text branch October 11, 2026 15:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

2 participants