Repository navigation
docs(skills,docs): teach page:header actions as declared action ids - #20257
Conversation
`PageHeaderProps.actions` is `z.array(z.string())` — ids resolved against the bound object's declared actions, with no built-in id registry. The objectstack-ui skill (pages.md catalogue row, example and note) and its eval taught full `Action` objects, and the layout-dsl docs page taught `actions: [edit, delete, share]` with no such actions declared; `os validate` flags both shapes. Teach the id, show where it resolves, and declare the action the docs example names. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MjvgiFAmjHqsxy1XLiVYfH
Contract reviewServed-tier: Face: ① Derived judgments
② Semver level
③ Boundary flagsNo Implemented-by: VERDICT: PASS Generated by Claude Code |
维护者速读(终稿)— PR #20257改了什么:修正 objectstack-ui 技能包( 为什么改:契约是 风险与代价(含回滚):只改教学文本,不动 spec、不动渲染器; 席位意见:已对分支与 pin 处的 objectui 逐条核过 —— 三处技能文字与契约一致,示例 id 你要做的:在本 PR 上 Approve。之后由本席清标、翻 ready、挂 auto-merge 走队列落地。 Generated by Claude Code |
Fixes #20173
Clause-②: no
The objectstack-ui skill (
rules/pages.mdand its eval) taught fullActionobjects inpage:header.properties.actions, and the layout-dsl docs page taughtactions: [edit, delete, share]with no such actions declared. The contract is action ids:packages/spec/src/ui/component.zod.tsdeclaresactions: z.array(z.string()).optional().describe('Action IDs to show in header'), and ruling B on #11592 (a completed card, not reopened here) made the canonicalpage:headerimplement that id lookup. This PR teaches the id and where it resolves; it changes no renderer and no spec (out of scope: the objectui object arm is an undeclared transition tolerance, retired on its own card).What changed
skills/objectstack-ui/rules/pages.md— catalogue row:actions: string[](action ids), 「inline」 dropped; example:actions: ['convert_lead']naming the action the same block declares onlead; the 「Actions in header」 note now says the ids resolve against the bound object's declared actions and that there is no built-in id registry (the 「no sibling action node」 rule stays).skills/objectstack-ui/evals/views-apps-actions-pages.json— eval 4expected_output: 「the declared action's id inproperties.actions」 instead of 「the Action object passed in」. Themust_containlist is unchanged on purpose: the prompt does not fix the action's machine name, so pinningconvert_leadthere would grade the author's naming, not the shape.content/docs/protocol/objectui/layout-dsl.mdx— Standard Template: one sentence states the rule (ids of the bound object's declared actions;edit/delete/shareresolve nowhere; the host draws its own Edit / Delete chrome), a taggedactionblock declaressend_statementonaccountwithlocations: [record_header], and the page namesactions: [send_statement]. Customer 360: the undeclared ids are replaced by a one-line YAML comment pointing at that rule (the header keeps its title; nothing on that page declares an action oncustomer, and a second declaration block was not worth the length).Where the id resolves (objectui at the pin
f8a9d0fb, read withgit -C ../objectui grep)packages/components/src/renderers/layout/containers.tsx(PageHeaderRenderer): the authored array goes throughresolveDeclaredActionIds(@object-ui/types,ui-action.ts) againstuseMetadataItem('object', headerObjectName).actions— the bound object's own declared actions. An id that resolves nowhere logs 「object … declares no action by that name. Declared: …」 and draws nothing. There is no built-in id registry; the host's Edit / Delete chrome arrives separately asheaderSystemActions(sys_edit,sys_deleteinapp-shell/src/views/RecordDetailView.tsx), so authorededit/deleteids neither resolve nor dedupe against it.Reach, measured at the public door (
os validate)Four plain-object configs in a scratch directory, run with the CLI's source entry the CLI's own e2e tests use:
node_modules/.bin/tsx packages/cli/bin/run-dev.js validate objectstack.config.ts(spec, lint, core, metadata-core built first;origin/mainat14ae40b0, which includes PR #20171).ConvertLeadActionobject inproperties.actions⚠ page "lead_detail_page" · page:header: actions.0: Invalid input: expected string, received object(component-props-invalid)actions: ['convert_lead'], action declared withobjectName: 'lead'✓ Validation passed, no advisoriesactions: ['edit', 'delete', 'share'], none declared✗ Author-time rules failed (3 issues)—action-name-undefinedatproperties.actions[0..2]: 「names action "edit", which is defined by no action in this stack … The header draws no button for it」send_statementdeclared onaccount,actions: ['send_statement']✓ Validation passed, no advisories(The source entry prints oclif MODULE_NOT_FOUND noise for unrelated commands whose packages are not built here —
i18n:check,migrate:meta; thevalidatecommand loaded and ran in every case, as the◆ Validateblock that follows the noise shows.)Line and token budget (
skills/**, token = ceil(utf8 bytes / 4), the ratchet's own convention)skills/objectstack-ui/rules/pages.mdskills/objectstack-ui/evals/views-apps-actions-pages.jsonskills/objectstack-ui/**(11 tracked files)Net lines: 0 per file, 0 for the package. No ceiling moved;
node scripts/check-skills-token-ratchet.mjsis green at head9c49e874(pages.md … 5692 tokens (ceiling 5692; headroom 0)).Gates (derived from the diff by
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, run at head9c49e874)46 commands derived, 46 run, every exit code captured before any pipe as 0;
--ranreconciliation: 「46 derived famil(ies) accounted for — 46 run, 0 NOT-MEASURED」. The rows this diff bears on:pnpm --filter @objectstack/spec run check:skill-examples— 「259 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass」 (theos:checkblock in pages.md is one of them; needed@objectstack/client/client-reactbuilt).pnpm --filter @objectstack/spec run check:yaml-examples— 「19 tagged YAML example(s) … validate against their declared live spec schemas」 (the newos:check-yaml actionblock and bothpageblocks included).node scripts/check-skills-token-ratchet.mjs— 54 authored files within ceilings.pnpm check:doc-authoring,check:doc-anchors,check:docs-single-h1,check:docs-spec-enumerations,check:skill-frame-sync,check:skill-identifier-liveness,check:skill-compatibility,check:nul-bytes— all 0.typecheck/testis owed; the diff has no ① build closure.NOT MEASURED, CI's own: the
Build Docsjob (fumadocs build ofcontent/docs),Test Coreshards, the type-check lanes.Changeset
skip-changeset: the diff isskills/**andcontent/docs/**only. No workspace package lists either path infiles[](grep over everypackages/**/package.json);create-objectstackshipsdist/README.md/CHANGELOG.mdand installs the catalog at scaffold time vianpx skills add objectstack-ai/objectstack/skills(itssrc/skills-install.ts), not from the tarball. Built-dist grep after the build: the diff's text (send_statement, 「ids oflead's actions」, 「(action ids)」) hits nothing under any built package'sfiles[]; the positive control 「Action IDs to show in header」 hitspackages/spec/dist/ui/index.js.Acceptance notes
14ae40b0:git grep -nE "actions: \[[A-Z][A-Za-z]*Action" -- skills content/docs→ 1 hit (pages.md:90);page:headerexamples with undeclared ids → 2 (layout-dsl.mdx:96,:997) plus the skill's object-shaped one. At head: the regex sweep → 0 hits; everypage:headerexample on both surfaces names only declared ids or none (layout-dsl.mdx:109send_statement, declared at:88;pages.md:90convert_lead, declared at:69; Customer 360 names none).layout-dsl.mdx:636record:related_listwithactions: [standard_new]—action-name-undefineddeliberately does not walk that key (actionsis declared separately onrecord:related_list), so it is not the header family; whetherstandard_newis a renderer built-in was not measured here. Carrier: none.origin/mainmoved from the dispatch's08c8484ato14ae40b0before the branch was cut; every site in the card was re-read there and is unchanged.维护者速读(草稿)
改了什么 — 修正 objectstack-ui 技能包(
rules/pages.md、eval 4)与 layout-dsl 文档页里page:header的actions写法:从「传入完整 Action 对象」/「[edit, delete, share]」改为声明在绑定对象上的 action id,并说明 id 在哪里解析(对象自己声明的 actions,没有内建 id 注册表)。文档示例补了一段send_statement的 action 声明,让示例自洽。为什么改 — 契约是
PageHeaderProps.actions: string[](#11592 裁决 B,objectui 已实现)。技能与文档教的形状,os validate一个报component-props-invalid,一个报三条action-name-undefined(本 PR 正文有实测命令与输出);跟着技能写的 AI 作者会直接写出被校验器拒收、渲染器不画按钮的元数据。风险与代价(含回滚) — 只改教学文本,不动 spec、不动渲染器;
skills/**净行数 0、token 棘轮读数不变(5692/1505 均持平,pages.md 字节 22765→22767,仍在 22768 上限内)。回滚 = revert 本 PR,无其他依赖。席位意见 — (留空)
你要做的 — 阅读
pages.md与layout-dsl.mdx两处措辞后,在本 PR 上给出 APPROVED review(Tier H,skills/**只由维护者批准落地)。Generated by Claude Code