feat(spec,showcase): aggregate bulk dispatch — allow _selectedIds through the param gate, document execution: 'aggregate' (objectui#3139) - #4461
Merged
Conversation
…ough the param gate, document execution: 'aggregate' (objectui#3139)
objectui 17.1 adds an aggregate single-call mode for bulkActionDefs: an
execution: 'aggregate' entry dispatches its named object action ONCE for the
whole selection with every selected id injected as params._selectedIds. The
capability lives on the VIEW def (per the spec-17 action.bulkEnabled
tombstone); this is the server-side contract half:
- ACTION_PARAM_BUILTIN_KEYS += '_selectedIds' so the ADR-0104 strict gate
does not 400 an aggregate dispatch against a param-declaring action —
dispatcher-injected, never authorable, pure widening (+ pin test)
- bulkActionDefs describe documents the aggregate contract: read
params._selectedIds (not recordId), all-or-nothing results, batchSize
N/A, maxRecords advised, ${ctx.selection.ids} toolbar interpolation
- liveness view.json: bulkActionDefs evidence now cites the dispatch path
(useBulkExecutor aggregate branch), pinned to objectui@636ac2f
- showcase: showcase_recalc_selection specimen — the recalc endpoint gains a
_selectedIds batch branch, task.view's bulk_actions declares the aggregate
def next to the per-record fixtures, tour doc updated
- content/docs/references regenerated (check:generated green)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S9aiswZBzoVYsyLKRuGByE
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…(objectui#3139)
`bulkActionDefs` was absent from the List View properties table entirely — the
Zod `.describe()` was its only author-facing documentation, so the aggregate
mode had nowhere an author would look. Adds the table row plus a "Bulk Actions
Over a Selection" section contrasting the two keys and both execution modes,
and states the aggregate contract authors need: read `params._selectedIds`
(not `recordId`), all-or-nothing results, `batchSize` N/A, `maxRecords` for
expensive work, and the `${ctx.selection.ids}` toolbar interpolation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S9aiswZBzoVYsyLKRuGByE
…bjectui commit objectui#3141 squash-merged as 4bf612c; the branch sha the note carried is not in objectui's main history. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S9aiswZBzoVYsyLKRuGByE
Browser-verifying the aggregate specimen surfaced a dead affordance: the action:bar renderer treats a missing/empty `locations` as "every location" (objectui action-bar.tsx), so a locations-less action also lands on the LIST TOOLBAR — where there is no selection, so the dispatch carries no `_selectedIds` and the endpoint rejects it (HTTP 400). Declaring `record_more` + `recordIdParam` puts the single-record entry where the endpoint's per-record branch handles it and keeps it off the toolbar. The renderer's lenient rule is filed as objectui#3142 — it contradicts DeclaredActionsBar's strict one, and an aggregate-only action is the shape that makes the difference visible. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S9aiswZBzoVYsyLKRuGByE
The aggregate specimen declared a label + successMessage with no zh-CN translation, which the coverage ratchet caught (456 → 460). Translating both recalc surfaces — the per-record one was already untranslated — brings the showcase to 452; baseline updated per the ratchet-down prompt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S9aiswZBzoVYsyLKRuGByE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
objectui 侧:objectstack-ai/objectui#3141(实现)/ objectstack-ai/objectui#3139(诉求)。
背景
列表勾选 N 行后,一类「聚合批量动作」无法表达:动作被调用一次、拿到全部选中记录 id、服务端产出单一聚合产物(勾选多台设备 → 逐台生成二维码 → 一个 zip 一次下载;同类还有批量打印、合并导出 PDF)。objectui 的批量 runner 对
custom只有逐行分支。objectui#3141 用视图侧的
bulkActionDefs[].execution: 'aggregate'表达它(不动ActionSchema—— spec 17 的action.bulkEnabled墓碑明示多选工具栏只由视图bulkActions/bulkActionDefs驱动),渲染器一次分发并注入params._selectedIds: string[]。不需要新的服务端路由:聚合动作的目标(api/url/script)本就能承接 id 数组。本 PR 是这套契约的服务端一半。改动
1.
ACTION_PARAM_BUILTIN_KEYS+='_selectedIds'(packages/spec/src/ui/action-params.zod.ts)没有这一条,任何声明了 params 的聚合动作都会被 ADR-0104 严格参数门以
unknown_field400 掉 —— 而_selectedIds是分发器注入的,作者永远无法把它声明为 param。与recordId/objectName同性质。纯放宽:不声明 params 的动作本就不走校验(所以 zip 场景对旧服务端也可用,这是加固而非硬依赖),也没有哪个作者书写的参数包会合法地带这个 key。附 pin 测试。2.
bulkActionDefs的.describe()扩写为作者侧契约(view.zod.ts)这个 key 在
content/docs里没有任何文档,describe 是事实上的作者文档。现在写明:两种执行模式、服务端应读params._selectedIds而非recordId、结果 all-or-nothing(无法覆盖整批的处理器必须拒绝)、batchSize在聚合模式不适用而maxRecords仍生效、以及${ctx.selection.ids}/${ctx.selection.count}的工具栏插值。content/docs/references/ui/view.mdx随gen:docs再生成。3. liveness ledger(
packages/spec/liveness/view.json)bulkActionDefs行的证据原本只指到ListView.tsx的转发点(一个 forwarding seam)。按 ledger README 的规矩,证据要指分发路径(authoring/preview 渲染器不算 runtime consumer —— 2026-06 那次扫描 13 条里错了 10 条正是这个原因),现在补上useBulkExecutor的聚合分支与ObjectGrid.runBulkActionAggregate,并 pin 到 objectui 的实现 commit。4. showcase specimen
showcase_recalc_selection(api 动作)+task.view.ts的bulk_actions视图用execution: 'aggregate'声明它,与同视图里showcase_mark_done(script,逐行)/showcase_recalc_estimate(api,逐行)并排 —— 一个视图里同时看到两种执行模式。recalc 端点新增_selectedIds批量分支(一次调用重算整批并报计数;all-or-nothing)。showcase_tour_ui.md同步。验证
pnpm --filter @objectstack/spec test— 7230 通过pnpm --filter @objectstack/spec check:generated— 8 项生成物全绿(gen:docs已按--fix只再生成确认过期的那一项)check:liveness/check:empty-state— 通过packages/runtime全量 1023 通过;examples/app-showcasetypecheck 通过已知遗留(已立案,不在本 PR 范围)
bulkActionDefs在 spec 里仍是z.array(z.record(z.string(), z.any()))—— 形状契约实际住在 objectui 的@object-ui/types,作者拼错 key 静默通过(ADR-0018 第二词汇表 + ADR-0078 silently-inert)。类型化 +validate-action-name-refs覆盖已按 Prime Directive #10 开 issue:#4457。🤖 Generated with Claude Code
https://claude.ai/code/session_01S9aiswZBzoVYsyLKRuGByE
Generated by Claude Code