Repository navigation
docs(skills): objectstack-ui action examples and the SKILL.md action rows follow the ActionSchema refinements - #22184
Conversation
…ema refinements The published objectstack-ui skill taught two action shapes that `ActionSchema` refuses, so `defineAction(...)` threw at module load for anyone who copied them: - the "collect params, then execute server body" example authored `type: 'modal'` beside a `body`; a non-script action dispatches on `target` and never runs a body. It is now `type: 'script'` (the shape the refinement message prescribes), the heading moves with it, and the dead `target` goes. - the `opensInNewTab` + `newTabUrl` example authored `type: 'url'` with no `target`. `target` is required for `url`, and the zero-roundtrip `newTabUrl` fast path is read only by the server-action handler a `script` action reaches (a `url` action navigates to `target` and never consults `newTabUrl`), so the example takes the spec's own pinned legal pair: `type: 'script'` + `target` naming the registered handler. The one comment deleted from the first block restated the "Action Parameter Patterns" paragraph; it pays the token ratchet for the added `target` line. The eval's expected output for the bulk action names the corrected type. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXydFDyiQwNbGFkmwrcRQq
…y on a modal The Action Types table taught `modal` as "Open a dialog (typically collecting `params`, then executing `body`)" and the CRM blueprint row sent parameterized bulk mutations to `modal` — the shape `ActionSchema` refuses (`body` only runs for `type: 'script'`; a non-script action dispatches on `target` and never runs a body). The two rows now state what the schema accepts and what rules/actions.md teaches: the blueprint's parameterized bulk mutation is a `script`, and a `modal` opens the page its `target` names, with the note that a `body` runs only on `script`. One byte of growth, paid inside the file's ratchet headroom (3843 → 3844 tokens of 3856). Nothing else in the file moves. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CXydFDyiQwNbGFkmwrcRQq
Contract reviewServed-tier: Reviewed in-seat by the skills seat 1 (dispatching seat; this session is served at the contract-review tier, read off ① Derived judgments
② Semver levelNone. Prose-only edits to published skill text and one eval sentence; no package ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
维护者速读(终稿)席位:skills 席 1, 改了什么:只改对外发布的 objectstack-ui 技能的三处文字。① 为什么改:这三处教的都是 风险与代价(含回滚):纯文字,无代码、无 spec、无 changeset( 席位意见:同意合入,席内契约复核 PASS。一处与派发单不同、席位已核实并采纳: 你要做的:用授权账号( Generated by Claude Code |
…able block subset and its lint (objectstack-ai#22271) (objectstack-ai#22287) Fixes objectstack-ai#22271 Clause-②: no The Studio author's page skill (`skills/objectstack-ui/rules/pages.md`) learns the print page that card ① of the ruling of record on objectstack-ai#8346 (comment 6051470224, letter B′, maintainer 「8346 B′」) landed as objectstack-ai#22158 through PR objectstack-ai#22193: a printable document is a page that declares `print`; the eight `print` keys and what each means on paper; the three page types that may carry it; the printable block subset taught by reason, not as a copied list; the `print-page-block-unprintable` refusal; and the two list facts (list export has no `'pdf'`; a list prints through the view's `allowPrinting`). objectstack-ai#8346, objectstack-ai#22158, objectstack-ai#22193 and objectstack-ai#22184 are references — none of them is addressed here. Every sentence is true of `origin/main` at `c8bb3c8d9c` (this branch's base): the declaration is parsed and refused by `@objectstack/spec` (`PagePrintSchema`, `checkPagePrintComposition`) and by `@objectstack/lint` (`validatePrintPageBlocks`, rule `print-page-block-unprintable`, on `os validate` / `os build` / `os lint` and the page save door). What renders it is the console's print rendering — card ② of the same ruling, not on this tree — so the skill says in one clause that the rendering follows and that `os validate` warns the author today (the liveness row `print` is `planned` with `authorWarn`, `packages/spec/liveness/page.json`). No rendering is invented. ## What changes (three markdown files, nothing else) - `skills/objectstack-ui/rules/pages.md` — a new `### Print pages` section under Pages: what a print page is and the refused page kinds/types (`list` → `interfaceConfig.allowPrinting`, `utility`, `slotted` / `html` / `react`, a `full` page with no regions); the keys `paperSize`, `orientation`, `margins` (millimetres), `repeatHeader` / `repeatFooter` (the page's own `header` / `footer` regions; refused without that region), `pageNumbers`, `repeatTableHeaders` (on by default), `avoidBreakInside`; the printable subset stated as the property every member has (draws all it declares, in full, the same at any width) with the 13 members grouped by that property; the four refusal reasons with the blocks an author reaches for first (`record:related_list` / `object-grid` → `record:line_items`; `page:header`, sidebars, boards; `record:path`, tabs, buttons, inputs, forms → `record:details`; `flex` / `object-chart` / plugin widgets are outside the vocabulary); the rendering status in one clause; the two list facts; and one `os:check` invoice example (`type: 'record'`, `print` with margins / running header and footer / page numbers, `element:text` letterhead, `record:details`, `record:line_items`, a footer region). - `skills/objectstack-ui/SKILL.md` — one word in the Detailed Rules routing row for pages.md ("print pages"), so an author routing from the entry file finds the section. - `skills/objectstack-ui/rules/actions.md` — one sentence beside the `OpenInvoicePdfAction` example: a printable document is a page that declares `print` (link to the section), validated today, rendered once the console's print rendering ships. The example itself is untouched (with only card ① landed, a per-project endpoint is still the only working route to a PDF today). Paid by deleting the three "Best practices" bullets the file already states elsewhere: `confirmText` for destructive actions (line 20), `visible` to hide an action (line 18), `input.selectedIds` for bulk actions (the `list_toolbar` row and the `AddToCampaignAction` example); the `refreshAfter` bullet, which had no other home, stays. - `skills/objectstack-ui/evals/*.json` — untouched: no eval quotes a changed line. Eval 4's `expected_output` names the record-page lessons (`page:header` with `properties.actions`, `record:path` with `statusField` / `stages`) that now live in the component catalogue rows. ## Ratchet route and readings (`scripts/check-skills-token-ratchet.mjs`, convention ceil(utf8 bytes / 4)) **Why the section lives inside `pages.md` and not in a new file.** The ratchet admits no new file on an author's own: a discovered authored file with no ceiling is RED, and its remedy carries the maintainer-only marker — "adding a CEILINGS row prices new text into the bundle that ships to every customer project, which is the maintainer ruling this ratchet implements — not a step an author takes while landing the file." So the section is paid inside `pages.md` by deletion, with no re-wrap counted and no ceiling moved. | file | lines before → after | tokens before → after | ceiling | headroom after | | --- | --- | --- | --- | --- | | `skills/objectstack-ui/rules/pages.md` | 453 → 434 (−19) | 5679 → 5691 (+12) | 5692 | 1 | | `skills/objectstack-ui/SKILL.md` | 308 → 308 (0) | 3844 → 3847 (+3) | 3856 | 9 | | `skills/objectstack-ui/rules/actions.md` | 207 → 206 (−1) | 2104 → 2101 (−3) | 2108 | 7 | | whole package, all `skills/*/SKILL.md` (10 files) | 4408 → 4408 (0) | 52572 → 52575 (+3) | — | — | | whole `skills/objectstack-ui` authored bundle (9 priced files) | 2051 → 2031 (−20) | 26096 → 26108 (+12) | — | — | Measured on `c8bb3c8d9c` (before) and on this PR's head (after); `node scripts/check-skills-token-ratchet.mjs` at the head: `pages.md is 5691 tokens (ceiling 5692; headroom 1)`, `SKILL.md … 3847 (ceiling 3856; headroom 9)`, `actions.md … 2101 (ceiling 2108; headroom 7)`, exit 0. **Every deletion in `pages.md`, and where its content survives:** | deleted | survives in | | --- | --- | | "Pages let designers assemble record details, home pages, app launchers, and utility bars without writing React." | the Page Types table (`record` / `home` / `app` / `utility` rows) | | the whole `### Example — Record Detail Page` `os:check` block (52 lines) | the `definePage` + `regions` shape → the new invoice `os:check` block; `page:header` `title` / `subtitle` / `actions` → its catalogue row (extended); `record:highlights` `properties.fields` → its catalogue row (extended); `record:path` `statusField` / `stages` → its catalogue row (extended); the inlined `ConvertLeadAction` → `rules/actions.md` (same action, same skill); `template: 'three-column'` → Templates & Regions | | the relative-date placeholder list and the `DATE_MACRO_TOKENS` pointer in the Variable substitution note | `SKILL.md` → Date Macros — Filter Placeholders (the pointer stays, shortened) | | the "Actions in header" note | the `page:header` catalogue row ("ids of the bound object's actions, never a sibling action node") | | react tier: "Compose layout with inline style (real CSS); use the injected blocks for data. Do NOT use Tailwind className — see Styling a page below" | Styling a page → the `kind:'react'` bullet (inline style object) and the section's no-`className` rule | | react-blocks note: "It is the authoritative answer to what props does ObjectForm / ListView take — author against it, not from memory." | the same note's bold lead "Do not guess props — read the contract." | | react-blocks note: "(Maintainers: regenerate with pnpm --filter @objectstack/spec gen:react-blocks.)" | a maintainer process note, not customer teaching — AGENTS.md's regeneration table and `packages/spec/package.json` | | "See ADR-0065 (SDUI styling model)." | the section heading "Styling a page (ADR-0065)" | | the `src/docs/` tree diagram | Authoring rule 2 (filename stem = doc name) and Routing model (`/docs/` + name) | | "A platform-level Documentation portal … is a later, additive concern — author-side, nothing to model now." | the one deletion that is not a restatement: a roadmap remark; no authoring instruction is lost (there is no portal key to author) — flagged here for the reviewer | | "Live instances vs. structural views", second half (structural metadata → a `metadata` fence) | Inline metadata views — the `metadata` fence (the three-kinds table and its lint) | | "Scope is deliberately narrow: only state_machine, flow, permission." | the same section's "Three view kinds" table and the lint line ("`type` must be one of the three") | ## Controls (against the built `@objectstack/spec` dist at the head, `scripts/pm/os-verify-lock.sh` VERDICT command-exit 0) - Positive: the invoice example exactly as `pages.md` carries it → `PageSchema.safeParse` success = true, `print` parsed as `{"paperSize":"A4","margins":{"top":15,"right":12,"bottom":15,"left":12},"repeatHeader":true,"repeatFooter":true,"pageNumbers":true}`; `validatePrintPageBlocks({ pages: [example] })` → `[]`. - Refused by the lint rule (the same page holding unprintable blocks): rule `print-page-block-unprintable`, severity `error`, each with its reason — "`page:header` cannot be placed in a print page … it lays its action bar out to the screen width"; "`record:related_list` … draws only the first `limit` related records (5 unless set) … a window of the rows"; "`flex` … is not in the printable block subset, and nothing answers for how it prints"; "`record:path` … is an interactive stage control; the current stage is a field value, which `record:details` prints". - Refused at the parse: `print` on a `type: 'list'` page ("… set `interfaceConfig.allowPrinting: true`; to print a document, declare `print` on a page of `type: 'record'`, `'home'` or `'app'` with its blocks in `regions`"); `repeatHeader: true` with no `header` region ("Add a `{ name: 'header', components: [...] }` region holding the letterhead blocks, or delete `repeatHeader`"); `paperSize: 'A3'` ("expected one of A4 | A5 | Letter | Legal"); `print` on a `utility` page; `print` on a `kind: 'react'` page. - `pnpm --filter @objectstack/spec run check:skill-examples` at the head: "262 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them", exit 0 (the new invoice block is one of the 228 skills + docs blocks). ## Gates (derived on this tree by `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`: 24 families; every exit code captured before any pipe; `--ran` reconciliation: 24 accounted, 0 NOT-MEASURED) | command | exit | | --- | --- | | `node scripts/check-skills-token-ratchet.mjs` (+ `--self-test`) | 0 / 0 | | `pnpm --filter @objectstack/spec run check:skill-docs` | 0 | | `pnpm check:skill-identifier-liveness` · `check:skill-compatibility` · `check:skill-frame-sync` | 0 · 0 · 0 | | `pnpm check:doc-authoring` · `check:corpus-claim-drift` · `check:nul-bytes` | 0 · 0 · 0 | | `node scripts/check-doc-route-spelling.mjs --advisory` (+ `--self-test`) | 0 / 0 | | `pnpm --filter @objectstack/lint run check:doc-formula-expressions` | 3 before the lint/formula build (prerequisite, not a verdict) → 0 after `turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --filter=@objectstack/client-react --filter=@objectstack/client` (34/34 tasks) | | `node scripts/check-ci-filter-parity.mjs` · `check-closing-keyword-parity.mjs` (+ `--self-test`) · `check-comment-mask-corpus.mjs` | 0 · 0 / 0 · 0 | | `pnpm check:agent-test-spelling` · `check:cross-package-test-inputs` · `check:driver-memory-census` · `check:gitlink-declared` · `check:pm-governed-merges` · `check:refd-timer-probe` · `check:role-word` · `check:watch-hint-literal` | all 0 | Not run locally, CI's own: the type-check lanes and `pnpm lint` (no TypeScript or source file moves). No changeset: the diff publishes nothing from a released package (`skills/**` is not in any package's `files`), so `skip-changeset` is the declaration. ## Acceptance notes (not filed; carrier named) - `packages/spec/src/ui/app.zod.ts:108` declares a `doc` navigation-item type (`{ type: 'doc', book }` / `{ type: 'doc', doc }`) and `SKILL.md`'s nav table lists it, but `pages.md` → Routing model still says "There is no dedicated `doc` nav-item type yet, so use a `url` item", and eval 4's `expected_output` expects a `type: 'url'` nav item. A stale line in the published skill; a different defect class from this card, so not touched here. Carrier: the `domain:skills` seat, reported in this card's os-dev-report for filing. - The print-page example omits `template` (spec default `'default'`); whether a print page constrains `template` is card ②'s call (PR objectstack-ai#22193's own note). The skill does not speak about it. - Rendering: the skill's one clause ("the console's print rendering that applies it follows") is the only forward-looking sentence; the day card ② lands at a `.objectui-sha` pin, that clause and the `os validate` warning sentence are the two lines to revise. ## 维护者速读(草稿) **改了什么** — 给 Studio 作者技能的页面规则(`skills/objectstack-ui/rules/pages.md`)新增一节「Print pages」:可打印文档(发票、送货单、信函、报表)就是声明了 `print` 的普通页面,没有模板类型;八个 `print` 键各自对应纸面上的什么;只有带 `regions` 的 `record` / `home` / `app` 页面可以声明;打印页里只允许「整页画全、任意宽度布局不变」的块,其余块被 `print-page-block-unprintable` 拒绝并给出原因(只画一窗行、按屏幕布局、无可打印内容、词汇表之外);列表导出没有 `'pdf'`,列表靠视图的 `allowPrinting`。附一个会被 `check:skill-examples` 类型检查的发票页示例。SKILL.md 路由行加「print pages」一词;actions.md 的「打开发票 PDF」示例旁加一句指向打印页。 **为什么改** — objectstack-ai#8346 裁决 B′ 原话:「the Studio author's skill learns the print page when ① lands」;① 已随 PR objectstack-ai#22193 落地。spec 与 lint 会响亮拒绝写错的打印页,但没学过这个声明的 AI 作者根本不会去写它,只会继续给每个项目手搓一个 PDF 接口;技能文本是 `npx skills add` 直接发到每个客户项目的那份。 **风险与代价(含回滚)** — 发布技能包有 token 棘轮:`pages.md` 只剩 13 token 余量,新节靠删除同文件里重复的内容付账(记录详情页示例整块删除、其教学点并入组件目录表格;另 11 处重复句,逐条列在上文表格里,唯一一条非重复的是「文档门户是后续事项」这句路线图备注),净 +12 token,落在 5691/5692;SKILL.md +3、actions.md −3,三个上限都没动。风险一:AI 作者看到打印页示例,把 `print` 加到普通页面 —— 文本已写明只有文档才声明,spec 也会拒绝不合格的页面;风险二:今天没有渲染器读 `print`(裁决的卡 ② objectui 未落地),技能用一句话如实说明「已校验、渲染随后」,`os validate` 也会提示作者。回滚:整个 PR 只动三个 markdown 文件,revert 即可,无代码、无 changeset。 **席位意见** — **你要做的** — 核对「Print pages」一节的措辞与上文的删除清单;认可则 APPROVE 这个 draft PR,由你或授权审批落地(`skills/**` 为 Tier H)。 --- _Generated by [Claude Code](https://claude.ai/code/session_01CXydFDyiQwNbGFkmwrcRQq)_ --------- Co-authored-by: objectstack-fleet[bot] <objectstack-fleet[bot]@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
Fixes #22176
Clause-②: no
The published objectstack-ui skill taught action shapes that
ActionSchemarefuses;defineActionisActionSchema.parse, so copying eitherrules/actions.mdexample threw at module load, and twoSKILL.mdrows taught the same refused shape in prose (amodalthat executes abody). This PR changes the two example blocks (plus the one eval sentence that quoted the first) so both parse, and rewords the twoSKILL.mdrows so each states a shape the schema accepts — the smallest prose change that leaves no sentence beside a block, or in a table, false. Draft on a Tier H surface (skills/**): it lands on an authorized APPROVED review or the maintainer's hand; no seat readies it.What changed (one rule file, one eval sentence, two
SKILL.mdrows)type: 'modal'+target+params+body. The schema's refinement refuses abodyon any non-script action ("a non-script action dispatches ontargetand silently ignores its body. To collectparamsand then run the body, usetype: 'script'"). The lesson is the body, so the block is nowtype: 'script', the heading moves with it ("Script-typed action"), and thetargetline goes (a script with an inlinebodyhas nothing fortargetto name).params,body,locations: ['list_toolbar']and thectxsection under it are unchanged. The eval sentence that described the bulk action astype: 'modal'beside abodynow saystype: 'script'; itsmust_containlist is untouched.opensInNewTab+newTabUrlexample — the block authoredtype: 'url'with notarget; the first refinement requirestargetforurl. The block is nowtype: 'script'+target: 'open_invoice_pdf'(see the re-route below);opensInNewTab,newTabUrlandlocationsare unchanged.// Field-backed params resolve label/type/options from object metadata:) — it restates the "Action Parameter Patterns" paragraph in the same file and pays the token ratchet for the addedtargetline (the file sat 4 bytes under its ceiling).SKILL.md, two rows (patch round, same family). The Action Types table row formodalread "Open a dialog (typically collectingparams, then executingbody)" and the CRM blueprint row sent parameterized bulk mutations tomodal— the shape the refinement refuses and this PR removes fromrules/actions.md. The table row now reads "Open the page named bytarget; abodyruns only onscript" — the schema's own words (targetis "the modal/page name to open"; the refinement message says "keeptype: 'modal'withtargetnaming the page") and what the renderer does (objectui's modal executor opensaction.targetas the dialog schema). The blueprint row now sends parameterized bulk mutations toscript, the lesson of the "Script-typed action" block. The required-field column, the column widths and every other row are unchanged; no eval states the shape (modalanddialoghave zero hits underevals/).Positive control (measured, both legs)
A one-off node probe parses the two fenced blocks AS WRITTEN ON DISK against the built
packages/spec/dist/ui/index.mjs(build-input-hashe5e882202edc406c, built under the verify lock on this tree, rebuilt and re-measured on the patch-round head):959c209d56)eb522522f2; re-measured byte-identical on28a140bb45)AddToCampaignActionsafeParse.success: false, issue pathbody,defineActionthrowssuccess: true, no issues, no throwOpenInvoicePdfActionsafeParse.success: false, issue pathtarget,defineActionthrowssuccess: true, no issues, no throwBefore-leg messages, verbatim: "
bodyonly runs fortype: 'script'— a non-script action dispatches ontargetand silently ignores its body. To collectparamsand then run the body, usetype: 'script'; to open a page/modal, drop thebodyand keeptype: 'modal'withtargetnaming the page." and "Action 'target' is required when type is 'url', 'flow', 'modal', 'api', or 'form'."Dispatch assumption 3 — measured, falsified, re-routed
The order assumed the second block's repair is "keep
type: 'url', addtarget". Measured on the sibling renderer (objectui checkout9990f9e):ActionRunner.builtinExecutors.urlgoes toexecuteUrl, which readsaction.target || action.redirectand navigates; it never consultsopensInNewTabornewTabUrl(packages/core/src/actions/ActionRunner.ts).newTabUrlis read in exactly one place — the console server-action wrapper, behindaction.opensInNewTab && newTabUrl(packages/app-shell/src/utils/consoleServerAction.ts), and that wrapper is bound as thescripthandler (useConsoleActionRuntime.tsxandRecordDetailView.tsx:script: serverActionHandler). The spec's own refinement comment on thenewTabUrlpair says the same: no other path reads the key at all.type: 'script': the spec's legal-pair pin inaction-newtaburl-pair.test.ts(type: 'script', target: 'ssoOpen', opensInNewTab: true, newTabUrl: …), the doubled-redirect pin, and all objectuiconsoleServerActiontests.content/docs/ui/actions.mdxandcontent/docs/protocol/objectui/actions.mdxboth say "for an async handler that redirects, useopensInNewTab" and give atype: 'url'action theopenIncontrol instead.So
type: 'url'+ anytargetwould parse and teach a dead pair: the renderer would navigate totargetandnewTabUrlwould never fire (the schema text itself routes aurlaction's new tab throughopenIn, which is the previous subsection's lesson). Per the ruling intent — spec declaration first, and no wrong sentence on the shipped surface — the block takes the spec's pinned legal pair:type: 'script'+targetnaming the registered handler, with an inline comment that the handler is the POST half and is skipped whilenewTabUrlis set. No second URL is invented;newTabUrlkeeps the one URL. The alternative the order suggested (keepurl, addtarget: '/api/v1/invoice/${ctx.recordId}/pdf') would make the example parse but contradict the prose above it and the renderer; if the maintainer prefers it anyway, say so on this PR and it is a one-line change.skills/**readings (base959c209d56→28a140bb45)rules/actions.md: 208 → 207 lines; 2107 → 2104 tokens (ceiling 2108; headroom 1 → 4); 8428 → 8414 bytes.evals/views-apps-actions-pages.json: 65 → 65 lines; 1502 → 1502 tokens (ceiling 1505); 6005 → 6006 bytes.SKILL.md: 308 → 308 lines; 3843 → 3844 tokens (ceiling 3856; headroom 13 → 12); 15372 → 15373 bytes — one byte of growth, paid inside the file's headroom.skills/*/SKILL.md4408 → 4408 lines (210279 → 210280 bytes);skills/objectstack-ui/**2232 → 2231 lines, 124058 → 124046 bytes; ratcheted bundle 144047 → 144045 tokens; whole shipped tree 154833 → 154831 tokens.rules/actions.mdpaid by content deletion (the restated comment); the one byte inSKILL.mdpaid inside its headroom. Nothing re-wrapped.Verification (tree
28a140bb45, worktree clean)check:skill-examples(the control): exit 0 on28a140bb45after building@objectstack/clientand@objectstack/client-reactunder the lock — 262 marked examples across 3 surfaces type-check, the "Script-typed action" block among them (SKILL.mdcarries no marked block). Oneb522522f2the first run had refused with PREREQUISITE NOT MET (exit 3, no built declarations, nothing measured) and passed after the same build.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 24 families on this tree: the first round's 23 pluspnpm --filter @objectstack/spec run check:skill-docs, brought in bySKILL.mdentering the changeset (its generator reads only the frontmatter; it reports "Skill docs in sync"). All 24 ran on28a140bb45with exit codes captured before any pipe, all 0; the--ranreconciliation reads "24 derived, 24 run, 0 NOT-MEASURED, 0 UNRUN". Families: check-ci-filter-parity; check-closing-keyword-parity (+ self-test); check-comment-mask-corpus; check-doc-route-spelling (advisory + self-test); check-skills-token-ratchet (+ self-test); lint check:doc-formula-expressions; spec check:skill-docs; check:agent-test-spelling; check:corpus-claim-drift; check:cross-package-test-inputs; check:doc-authoring; check:driver-memory-census; check:gitlink-declared; check:nul-bytes; check:pm-governed-merges; check:refd-timer-probe; check:role-word; check:skill-compatibility; check:skill-frame-sync; check:skill-identifier-liveness; check:watch-hint-literal.28a140bb45: "skills/objectstack-ui/SKILL.md is 3844 tokens (ceiling 3856; headroom 12)" and "54 authored bundle file(s) within their ceilings".test/typecheckis owed.skills/**publishes nothing from a released package (skip-changeset).Acceptance notes
SKILL.mdsame-family finding reported on the first round (themodaltable row and the blueprint row) is folded into this PR in the patch round on the seat's revised file surface; it is item 4 above.opensInNewTabblock carries noos:checkmarker, socheck:skill-examplesdoes not see it; a marker would add a type check only (the refinement that refused it is invisible to tsc either way). Not added (scope).input(the modal params)" in thectxsection is left as is: the params dialog is still a modal for a script action withparams.维护者速读(草稿)
改了什么 — 发布的 objectstack-ui skill 里有三处教 AI 写 schema 会拒收的 action 写法。①
rules/actions.md的"收集参数再执行服务端 body"示例由type: 'modal'改为type: 'script',标题同步,去掉多余的target;② 同文件新标签页newTabUrl示例由type: 'url'改为type: 'script'并补上 schema 要求的target;③SKILL.md两行:Action Types 表里modal一行原写"打开对话框(通常收集params再执行body)",改为"打开target命名的页面;body只在script上运行",蓝图表里"参数化批量变更用modal"改为用script。另删一句与下文重复的注释以抵消 token 棘轮;评测文件里描述该示例的一句话同步改为script。为什么改 — spec 声明优先:
ActionSchema的两条 refinement 明确拒绝这两种写法(非script带body、url缺target),错误信息本身就给出了修法;渲染端只在script路径读取newTabUrl,保留type: 'url'只会让示例"能过校验但不起作用"。SKILL.md的两行与示例是同一个错误,同一次审批一并收口。北极星优先级 4:发布面上写给 AI 看的错句是产品缺陷。风险与代价(含回滚) — 只改文档与评测文字(三个文件),不改代码、不改 spec,不发包、无 changeset。
SKILL.md净增 1 字节,在该文件棘轮余量内。风险在于第二处示例的改法与派发单建议(保留url加target)不同,正文已给出实测依据;若维护者更倾向原建议,改回是一行。回滚 = revert 本 PR。席位意见 — (留空)
你要做的 — 审阅本 PR 并 APPROVE(或在评论里指明第二处改用「保留
url加target」),由席位落地。Generated by Claude Code