Repository navigation
Commit 238222d
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 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`). #8346, #22158, #22193 and #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 #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」示例旁加一句指向打印页。
**为什么改** — #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](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>
1 parent ee6aa01 commit 238222d
3 files changed
Lines changed: 71 additions & 92 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
146 | 146 | | |
147 | 147 | | |
148 | 148 | | |
149 | | - | |
| 149 | + | |
150 | 150 | | |
151 | 151 | | |
152 | 152 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
184 | 184 | | |
185 | 185 | | |
186 | 186 | | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
187 | 190 | | |
188 | 191 | | |
189 | 192 | | |
| |||
200 | 203 | | |
201 | 204 | | |
202 | 205 | | |
203 | | - | |
204 | | - | |
205 | | - | |
206 | | - | |
207 | | - | |
| 206 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
13 | | - | |
| 12 | + | |
14 | 13 | | |
15 | 14 | | |
16 | 15 | | |
| |||
47 | 46 | | |
48 | 47 | | |
49 | 48 | | |
50 | | - | |
| 49 | + | |
51 | 50 | | |
52 | 51 | | |
53 | 52 | | |
54 | 53 | | |
55 | | - | |
56 | | - | |
| 54 | + | |
| 55 | + | |
57 | 56 | | |
58 | 57 | | |
59 | 58 | | |
60 | 59 | | |
61 | 60 | | |
62 | | - | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
63 | 101 | | |
64 | 102 | | |
65 | 103 | | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
| 104 | + | |
73 | 105 | | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
80 | 110 | | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
111 | 122 | | |
112 | 123 | | |
113 | 124 | | |
114 | 125 | | |
115 | | - | |
116 | | - | |
117 | | - | |
118 | | - | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | 126 | | |
128 | 127 | | |
129 | 128 | | |
| |||
192 | 191 | | |
193 | 192 | | |
194 | 193 | | |
195 | | - | |
196 | | - | |
197 | | - | |
198 | | - | |
199 | 194 | | |
200 | 195 | | |
201 | 196 | | |
202 | 197 | | |
203 | | - | |
204 | | - | |
| 198 | + | |
205 | 199 | | |
206 | 200 | | |
207 | | - | |
208 | 201 | | |
209 | 202 | | |
210 | 203 | | |
| |||
294 | 287 | | |
295 | 288 | | |
296 | 289 | | |
297 | | - | |
298 | | - | |
| 290 | + | |
299 | 291 | | |
300 | 292 | | |
301 | 293 | | |
| |||
328 | 320 | | |
329 | 321 | | |
330 | 322 | | |
331 | | - | |
332 | | - | |
333 | | - | |
334 | | - | |
335 | | - | |
336 | | - | |
337 | 323 | | |
338 | 324 | | |
339 | 325 | | |
| |||
382 | 368 | | |
383 | 369 | | |
384 | 370 | | |
385 | | - | |
386 | | - | |
387 | | - | |
388 | | - | |
| 371 | + | |
389 | 372 | | |
390 | 373 | | |
391 | | - | |
392 | | - | |
393 | | - | |
| 374 | + | |
394 | 375 | | |
395 | 376 | | |
396 | 377 | | |
| |||
424 | 405 | | |
425 | 406 | | |
426 | 407 | | |
427 | | - | |
428 | | - | |
| 408 | + | |
429 | 409 | | |
430 | 410 | | |
431 | 411 | | |
| |||
0 commit comments