Repository navigation
skills(ui): the page skill learns the print page — print, the printable block subset and its lint (#22271) - #22287
Conversation
…able block subset and its lint A printable document is a page that declares `print`; teach the keys and their paper meaning, the three page types that may carry it, the printable subset by reason, the `print-page-block-unprintable` refusal and the two list facts (no `'pdf'` export; `allowPrinting`). Paid inside `pages.md` by deleting the Record Detail example (its lessons move into the component catalogue rows) and restated lines; one routing word in SKILL.md; one pointer sentence in actions.md paid by its restated best-practice bullets. Claude-Session: https://claude.ai/code/session_01CXydFDyiQwNbGFkmwrcRQq Co-authored-by: Claude <noreply@anthropic.com>
…ount unchanged) Claude-Session: https://claude.ai/code/session_01CXydFDyiQwNbGFkmwrcRQq Co-authored-by: Claude <noreply@anthropic.com>
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. Published skill text; no package content, API or schema. ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
维护者速读(终稿)席位:skills 席 1, 改了什么:给 Studio 作者技能的页面规则( 为什么改:#8346 裁决 B′ 原话「the Studio author's skill learns the print page when ① lands」;① 已随 PR #22193 落地。spec 与 lint 会响亮拒绝写错的打印页,但没学过这个声明的 AI 作者根本不会去写它;技能文本是 风险与代价(含回滚):发布技能包有 token 棘轮,且棘轮自己拒绝作者新增文件(加 CEILINGS 行是维护者专属动作),所以新节在 席位意见:同意合入,席内契约复核 PASS。席位自己核过:八个键与默认值、三种页型、四种拒绝形态、13 种可打印块与 spec 源码一致;删除的内容在 PR 正文说的位置都在;树上没有别处链接到被删的 你要做的:用授权账号( Generated by Claude Code |
Fixes #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 #8346 (comment 6051470224, letter B′, maintainer 「8346 B′」) landed as #22158 through PR #22193: a printable document is a page that declaresprint; the eightprintkeys 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; theprint-page-block-unprintablerefusal; and the two list facts (list export has no'pdf'; a list prints through the view'sallowPrinting). #8346, #22158, #22193 and #22184 are references — none of them is addressed here.Every sentence is true of
origin/mainatc8bb3c8d9c(this branch's base): the declaration is parsed and refused by@objectstack/spec(PagePrintSchema,checkPagePrintComposition) and by@objectstack/lint(validatePrintPageBlocks, ruleprint-page-block-unprintable, onos validate/os build/os lintand 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 thatos validatewarns the author today (the liveness rowprintisplannedwithauthorWarn,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 pagessection under Pages: what a print page is and the refused page kinds/types (list→interfaceConfig.allowPrinting,utility,slotted/html/react, afullpage with no regions); the keyspaperSize,orientation,margins(millimetres),repeatHeader/repeatFooter(the page's ownheader/footerregions; 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 oneos:checkinvoice example (type: 'record',printwith margins / running header and footer / page numbers,element:textletterhead,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 theOpenInvoicePdfActionexample: a printable document is a page that declaresprint(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:confirmTextfor destructive actions (line 20),visibleto hide an action (line 18),input.selectedIdsfor bulk actions (thelist_toolbarrow and theAddToCampaignActionexample); therefreshAfterbullet, which had no other home, stays.skills/objectstack-ui/evals/*.json— untouched: no eval quotes a changed line. Eval 4'sexpected_outputnames the record-page lessons (page:headerwithproperties.actions,record:pathwithstatusField/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.mdand 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 insidepages.mdby deletion, with no re-wrap counted and no ceiling moved.skills/objectstack-ui/rules/pages.mdskills/objectstack-ui/SKILL.mdskills/objectstack-ui/rules/actions.mdskills/*/SKILL.md(10 files)skills/objectstack-uiauthored bundle (9 priced files)Measured on
c8bb3c8d9c(before) and on this PR's head (after);node scripts/check-skills-token-ratchet.mjsat 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:record/home/app/utilityrows)### Example — Record Detail Pageos:checkblock (52 lines)definePage+regionsshape → the new invoiceos:checkblock;page:headertitle/subtitle/actions→ its catalogue row (extended);record:highlightsproperties.fields→ its catalogue row (extended);record:pathstatusField/stages→ its catalogue row (extended); the inlinedConvertLeadAction→rules/actions.md(same action, same skill);template: 'three-column'→ Templates & RegionsDATE_MACRO_TOKENSpointer in the Variable substitution noteSKILL.md→ Date Macros — Filter Placeholders (the pointer stays, shortened)page:headercatalogue row ("ids of the bound object's actions, never a sibling action node")kind:'react'bullet (inline style object) and the section's no-classNamerulepackages/spec/package.jsonsrc/docs/tree diagram/docs/+ name)metadatafence)metadatafence (the three-kinds table and its lint)typemust be one of the three")Controls (against the built
@objectstack/specdist at the head,scripts/pm/os-verify-lock.shVERDICT command-exit 0)pages.mdcarries it →PageSchema.safeParsesuccess = true,printparsed as{"paperSize":"A4","margins":{"top":15,"right":12,"bottom":15,"left":12},"repeatHeader":true,"repeatFooter":true,"pageNumbers":true};validatePrintPageBlocks({ pages: [example] })→[].print-page-block-unprintable, severityerror, each with its reason — "page:headercannot be placed in a print page … it lays its action bar out to the screen width"; "record:related_list… draws only the firstlimitrelated 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, whichrecord:detailsprints".printon atype: 'list'page ("… setinterfaceConfig.allowPrinting: true; to print a document, declareprinton a page oftype: 'record','home'or'app'with its blocks inregions");repeatHeader: truewith noheaderregion ("Add a{ name: 'header', components: [...] }region holding the letterhead blocks, or deleterepeatHeader");paperSize: 'A3'("expected one of A4 | A5 | Letter | Legal");printon autilitypage;printon akind: 'react'page.pnpm --filter @objectstack/spec run check:skill-examplesat 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;--ranreconciliation: 24 accounted, 0 NOT-MEASURED)node scripts/check-skills-token-ratchet.mjs(+--self-test)pnpm --filter @objectstack/spec run check:skill-docspnpm check:skill-identifier-liveness·check:skill-compatibility·check:skill-frame-syncpnpm check:doc-authoring·check:corpus-claim-drift·check:nul-bytesnode scripts/check-doc-route-spelling.mjs --advisory(+--self-test)pnpm --filter @objectstack/lint run check:doc-formula-expressionsturbo 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.mjspnpm 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-literalNot 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'sfiles), soskip-changesetis the declaration.Acceptance notes (not filed; carrier named)
packages/spec/src/ui/app.zod.ts:108declares adocnavigation-item type ({ type: 'doc', book }/{ type: 'doc', doc }) andSKILL.md's nav table lists it, butpages.md→ Routing model still says "There is no dedicateddocnav-item type yet, so use aurlitem", and eval 4'sexpected_outputexpects atype: 'url'nav item. A stale line in the published skill; a different defect class from this card, so not touched here. Carrier: thedomain:skillsseat, reported in this card's os-dev-report for filing.template(spec default'default'); whether a print page constrainstemplateis card ②'s call (PR feat(spec)!: a page gains an optional print declaration and a linted printable block subset; the zero-reader document schemas retire whole (#22158) #22193's own note). The skill does not speak about it..objectui-shapin, that clause and theos validatewarning 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」示例旁加一句指向打印页。为什么改 — #8346 裁决 B′ 原话:「the Studio author's skill learns the print page when ① lands」;① 已随 PR #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