Repository navigation
Commit efb1620
docs(skills): objectstack-ui teaches the record page as the Console ships it (#28498)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} -->
_Requested by **Tesla** · [project
thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6Jao3RTKFDeacUTyxyZSaPbuLw)_
Fixes #27059
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么:** `skills/objectstack-ui` 新增一份按顺序讲记录页的规则文件
`rules/record-page.md`(六条:对象声明产出页面 → 关系标志塑造关联列 → slotted 页只覆盖一个槽位 →
`kind: 'full'` 是逃生口 → 默认抽屉、对象声明才整页 → 读者自己的选择不是元数据);`SKILL.md` /
`rules/pages.md` / `rules/navigation.md` 里写着 Details / Related /
Timeline 三页签的旧句子换成一行指针;新增一份 eval;文档站「How a list opens a
record」改成现行的打开方式与读者自选项,记录页一节补上关联面板的读者排布。
- **为什么改:** 主干的记录页已是页签行 + 右栏(#28116、#28394),skill 还在教三页签、`tabs` 槽位和
`modal` / `split` / `popover`;照着写的 AI 会为「线索页把任务放最前」去写整页,而正确答案只是子对象外键上的
`relatedList: 'primary'`。
- **风险与回滚:** 纯教学文本加一条 ratchet 行,无 schema、键或运行时改动;回滚即 revert 本 PR。
- **先看:** `skills/objectstack-ui/rules/record-page.md`,再看
`scripts/check-skills-token-ratchet.mjs` 新增的那一行及其出处注释。
- **两分钟验证:** `node scripts/check-skills-token-ratchet.mjs`、`pnpm
check:role-word`、`cd ui && node scripts/check-skill-eval-tokens.mjs`;再读
`skills/objectstack-ui/evals/record-page.json` 的两道题,确认各只有一个正确答案。
- **需要你做的:** Tier H(governed `skills/**`):席位 ACCEPT + 同 head 契约 PASS 后经
os-zhuang 批准;批准同时覆盖为新文件定价的 CEILINGS 行(ratchet 脚本对新行印 ⛔
MAINTAINER-ONLY,此处依卡 #27059 的 40 行预算与座席「记录页单独一份规则文件」的裁定写入)。
## What changes
- **`skills/objectstack-ui/rules/record-page.md`** (new, 65 lines). Six
numbered items, read in order, each the remedy for the one before it:
(1) the object's declarations produce the page (`nameField`,
`highlightFields`, `stageField` with option `kind`, `fieldGroups` +
`Field.group`, `relatedLists`, `editMode`), and the surface's width lays
it out (two columns from 760 px, three field columns from 1120 px, one
column below; a tab row that enlarges one panel at
`?related=object.field`; no per-surface layout key); (2) the
relationship flags shape the related column (`relatedList: 'primary'`
body table / `true` panel / `false` off, `relatedListTitle`,
`relatedListColumns`, `relatedListFilter` AND-composed with the parent
condition and honoured by the count, rows in the child's default
list-view `sort`, the parent's `relatedLists` order) — "put the tasks
first on the lead page" is `relatedList: 'primary'` on `task.lead`, no
page file; (3) `kind: 'slotted'` overrides one of six slots, each whole,
no `tabs` slot (`slots.tabs` / `slots.related` / `slots.relatedLists`
refused), `isDefault`; (4) `kind: 'full'` is the escape hatch; (5) the
drawer unless the object's list view declares `navigation: { mode:
'page' }` (`new_window`, `none`; `modal` / `split` / `popover` removed
in 18.3.0 and refused; field count never decides; a phone always pages);
(6) the reader's own choices are not metadata (Open records split /
drawer / page, the ⊟ right column per object, the panels' order and
hidden set per user × object, the pane's width and ⤢; the `size` bucket
only nudges the drawer's starting width). One `<!-- os:check -->`
example: `Field.masterDetail('lead', { relatedList: 'primary',
relatedListFilter: … })` on a `controlled_by_parent` child.
- **`skills/objectstack-ui/SKILL.md`.** The two related-list paragraphs
that taught a Details / Related / Timeline tab strip, a shared Related
tab and the removed `relatedLayout` keys become a two-line pointer at
the new file; one Detailed Rules link; the CRM blueprint's "Record
pages" row now says the page is synthesized and a `*.page.ts` is for a
slotted slot or a `kind: 'full'` layout.
- **`rules/pages.md`**: the `tabs`-slot sentence is a one-line pointer.
**`rules/navigation.md`**: the Surface bullet points at §5–6 of the new
file and no longer promises a mobile page or a record `Page` for layout;
the Overlay-width bullet moved into §6 whole.
- **`evals/record-page.json`** (new) + a README bullet. Eval 1: tasks
first on the lead page → `relatedList: 'primary'` + `relatedLists`;
never `definePage`, `slots.tabs`, `kind: 'full'`. Eval 2: contracts open
as a full page, and "open beside the list" → `navigation: { mode: 'page'
}` on the list view and the Display menu for the reader; never `mode:
'split'` / `'modal'` / `'popover'` / `definePage`.
- **`scripts/check-skills-token-ratchet.mjs`.** A CEILINGS row for the
new file pinned at its landed count (996) with its provenance (the
card's budget, the seat's ruling, the 403 tokens the three rows below
gave up). `SKILL.md` 3851 → 3544, `rules/navigation.md` 2273 → 2197,
`rules/pages.md` 5692 → 5669, each lowered to the measurement.
- **`content/docs/ui/views.mdx`** "How a list opens a record": the live
modes (`drawer` default, `page` by declaration, `new_window`, `none`;
`modal` / `split` / `popover` removed in 18.3.0, `os migrate meta --from
17` drops a stored one; a phone always pages), the reader's **Open
records** choice (Split default / Drawer / Full page, kept in the
browser, not offered for a page-declared object, ↗ always the full
page), the pane's edge, ⤢ / Open as full page / Collapse to drawer, the
`size` bucket. **`content/docs/ui/pages.mdx`** record pages: the related
panels are the reader's to arrange (⠿ / ⌥↑ ⌥↓, hide, bring back; per
user × object). Both pages are the redirect targets of the two guide
pages the card names (`/docs/guide/slotted-pages`,
`/docs/guide/user-state-persistence`, `apps/docs/redirects.mjs`).
- **`.changeset/27059-skills-record-page-rules.md`**:
`@objectstack/skills` patch, `Clause-②: no`, `Metadata-protocol: none`.
## Why
`main` ships record page v4 (#28116 right column + ⊟, #28394 tab row,
#28477 phone pass) and @objectstack/spec 18.3.0 retired `modal` /
`split` / `popover` and the `tabs` slot, but the published ui skill
still taught the three-tab page, a `tabs` slot and the retired modes —
so an agent writing from the skill authored a full page for a one-flag
change and metadata for choices that belong to the reader. The card asks
for one ordered 「记录页」 section that teaches what `main` ships now and an
eval that catches the full-page reflex.
## Line budget
PM-set and binding: the package's hand-authored markdown (`SKILL.md` +
`rules/*.md`) grows by at most 40 lines; paid with the sentences the
retirements made false.
| File | Lines before | Lines after | Δ lines | Tokens before | Tokens
after |
|:--|--:|--:|--:|--:|--:|
| `SKILL.md` | 307 | 287 | −20 | 3851 | 3544 |
| `rules/actions.md` | 204 | 204 | 0 | 2258 | 2258 |
| `rules/dashboards.md` | 464 | 464 | 0 | 6259 | 6259 |
| `rules/list-views.md` | 311 | 311 | 0 | 3129 | 3129 |
| `rules/navigation.md` | 162 | 157 | −5 | 2273 | 2197 |
| `rules/pages.md` | 428 | 427 | −1 | 5689 | 5669 |
| `rules/record-page.md` | — | 65 | +65 | — | 996 |
| **Package** | **1876** | **1915** | **+39** | **23459** | **24052
(+593)** |
Tokens are the ratchet's own reading (`ceil(bytes / 4)`, the SKILL.md
stamp line excluded). Outside the teaching set: `evals/README.md` 36 →
40 lines, `evals/record-page.json` new (40 lines).
## Tests
Run in the worktree at head 198adea, stdin closed, exit captured before
any pipe:
- `node scripts/check-skills-token-ratchet.mjs` → every objectstack-ui
row at or under its ceiling: `SKILL.md` 3544/3544, `rules/navigation.md`
2197/2197, `rules/pages.md` 5669/5669, `rules/record-page.md` 996/996
(headroom 0 each); `--self-test` 97 cases pass. Exit 0 / 0.
- `cd ui && node scripts/check-skill-eval-tokens.mjs` → every
`must_contain` token of `evals/record-page.json` is taught by the bundle
(`relatedList: 'primary'`, `relatedLists`, `navigation: { mode: 'page'
}`, `Display menu`); 0 untaught rows. Exit 0.
- `pnpm check:role-word` → "OK, no new occurrences of the reserved word"
(318 files; baseline 48 files / 156 occurrences unchanged). Exit 0.
- `pnpm check:skill-identifier-liveness` → Leg 1: 486 citations over 71
files checked, Leg 2: 8 sections, 0 gaps. Exit 0.
- `pnpm --filter @objectstack/spec run check:skill-examples` → 278
marked examples across 114 files type-check against the dts build of
`@objectstack/spec` and `@objectstack/client-react...` (built with `env
-u OS_SKIP_DTS` under the verify lock, VERDICT command-exit 0); the new
`Field.masterDetail('lead', { relatedList: 'primary', relatedListFilter:
… })` block included. Exit 0.
- `pnpm --filter @objectstack/lint run check:doc-security-posture` → 32
`ObjectSchema.create` examples in 259 marked blocks carry an `os
validate`-clean posture. Exit 0 (the first run refused the example for
`security-owd-unset`; the second commit declares `sharingModel:
'controlled_by_parent'`).
- `pnpm check:doc-authoring`, `pnpm --filter @objectstack/spec run
check:skill-docs`, `check:docs`, `check:liveness`,
`check:corpus-claim-drift`, `check:docs-transcript-drift`,
`check:docs-redirects`, `check:doc-anchors` → exit 0 each.
- `node scripts/check-sdui-manifest.mjs` → manifest present and intact
(sha256 029ea9f8fca7…, 106 components). Exit 0.
- Eval read-through: eval 1's model answer contains `relatedList:
'primary'` and `relatedLists` and none of `definePage` / `slots.tabs` /
`kind: 'full'`; eval 2's contains `navigation: { mode: 'page' }` and
`Display menu` and none of `mode: 'split'` / `mode: 'modal'` / `mode:
'popover'` / `definePage`.
## Acceptance notes
- The card names `skills/objectstack-ui/references/pages.md`; that path
does not exist (`references/` holds only the generator-owned `_index.md`
and `react-blocks.md`). The page rules live in `rules/pages.md`, edited
here.
- The two guide pages the card names were folded by K2 into
`content/docs/ui/pages.mdx#record-pages` (already current for the tab
row, the tiers and the slot table) and `content/docs/ui/views.mdx` (the
stale half, rewritten here); no page moved, so no generated reference
regenerates.
- The card's ruling 2 (a per-user *timeline column* switch) is
superseded on `main` by #28116: 动态 is the right column's first panel,
never a column, and the reader's switch is the right column (⊟, `⌘⇧,`)
plus the panel arrangement. The skill teaches what ships, per the
dispatch.
- Every rule sentence names a `main` code path at 8c3f9d9:
`ui/packages/plugin-detail/src/synth/recordPageColumns.ts` (760 / 1120
px tiers), `RecordTabRow.tsx` + `synth/recordTabRow.ts` (the tab row,
`?related=`), `synth/relatedArrangement.ts` (order / hidden per user ×
object), `ui/packages/app-shell/src/views/objectViewRecordNavigation.ts`
(the view's `navigation`, else drawer whatever the field count; a phone
→ page), `ui/packages/app-shell/src/utils/recordOpenPreference.ts`
(`split` default, drawer-mode objects only),
`packages/spec/src/ui/view.zod.ts` `NavigationModeSchema` (the 18.3.0
retirements and their fix), `packages/spec/src/ui/page.zod.ts` (six
slots, `tabs` retired, `isDefault`),
`packages/spec/src/data/field.zod.ts` (`relatedList*`, the inherited
sort), `packages/spec/src/data/object.zod.ts` (`nameField`,
`highlightFields`, `stageField`, `fieldGroups`, `relatedLists`,
`editMode`).
- Out of this PR's file surface, two places still say "Details tab /
shared Related tab": `skills/objectstack-data/rules/relationships.md`
(the `relatedList` tri-state bullets) and the `relatedList`
`.describe()` text in `packages/spec/src/data/field.zod.ts`, which ships
to the generated references. Carrier: ui seat 1 (the first is a sibling
skill with its own ratchet row, the second is spec text); not touched
here.
- The four files PR #28488 holds open (`rules/actions.md`,
`rules/dashboards.md`, `rules/list-views.md`,
`evals/views-apps-actions-pages.json`) are untouched, per the seat's
overlap ruling.
- The new CEILINGS row: the ratchet prints ⛔ MAINTAINER-ONLY for a file
carrying no ceiling. It is written here under the card's PM-set line
budget and the seat's ruling that the record page gets its own rules
file, pinned at the landed count with the deletions that pay it down
named; the Tier H approval of this PR is the maintainer's say on it.
- The word "role" does not appear in the new file (`check:role-word`
baseline unchanged); the ADR is cited by number only.
## Verification
- Derived: `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` against this diff → 87 commands (54 pnpm, 33
node). Every one run with `</dev/null`, its exit code recorded on disk
as `<command> :: exit <code>` before any pipe.
- Reconciled: `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --ran ran.list` → **87 derived, 87 run, 0
NOT-MEASURED, 0 UNRUN**; all 87 exits are 0.
- Three gates were re-run after a fix or a prerequisite and recorded at
their final exit: `check:doc-security-posture` (exit 1 on the first
example, 0 after the `sharingModel` commit),
`check:dual-build-cjs-loads` (exit 3 until the packages closure was
built with declarations, then 0), `check:lean-entry-closure` (exit 1
while that build was rewriting `packages/spec/dist` under it, 0 after).
- Builds under the shared verify lock (`scripts/pm/os-verify-lock.sh`,
slot kept): spec dts + lint closure (VERDICT command-exit 0, 3m20s),
`@objectstack/client-react...` (0, 5m17s), `turbo run build
--filter='./packages/**' --filter='@objectstack/dogfood^...'` (0,
8m52s).
- Not measured here, CI's: the type-check lanes, Build Core / Build
Docs, the artifact-roster families and the always-runs tail the
derivation lists outside its 87. No ui package or test changed (teaching
text, docs and one gate script only), so no ui test imports a changed
package.
- Writes: `git push` ×2, relay `pr_create` ×1 (this PR), `os-dev-report`
comment ×1 via `post-stamped`; GitHub MCP read calls only (`issue_read`,
`pull_request_read`), 0 MCP writes.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(No schema change.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; the lint-tier gates above pass.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: n/a (not touched).
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the 维护者速读 block is the
maintainer's requested Chinese digest).
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L9qibyV5bs9mFPca9yJkR5)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L9qibyV5bs9mFPca9yJkR5
---------
Co-authored-by: os-tesla <yinlianghui@steedos.com>
Co-authored-by: Claude <noreply@anthropic.com>1 parent 044d201 commit efb1620
10 files changed
Lines changed: 187 additions & 48 deletions
File tree
- .changeset
- content/docs/ui
- scripts
- skills/objectstack-ui
- evals
- rules
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
203 | 203 | | |
204 | 204 | | |
205 | 205 | | |
206 | | - | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
207 | 210 | | |
208 | 211 | | |
209 | 212 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
515 | 515 | | |
516 | 516 | | |
517 | 517 | | |
518 | | - | |
519 | | - | |
520 | | - | |
521 | | - | |
522 | | - | |
523 | | - | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
524 | 536 | | |
525 | 537 | | |
526 | 538 | | |
527 | 539 | | |
528 | | - | |
529 | | - | |
530 | | - | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
531 | 543 | | |
532 | 544 | | |
533 | 545 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
639 | 639 | | |
640 | 640 | | |
641 | 641 | | |
642 | | - | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
643 | 649 | | |
644 | 650 | | |
645 | 651 | | |
| |||
855 | 861 | | |
856 | 862 | | |
857 | 863 | | |
858 | | - | |
| 864 | + | |
| 865 | + | |
| 866 | + | |
| 867 | + | |
| 868 | + | |
859 | 869 | | |
860 | 870 | | |
861 | 871 | | |
862 | 872 | | |
863 | 873 | | |
864 | | - | |
| 874 | + | |
| 875 | + | |
| 876 | + | |
| 877 | + | |
| 878 | + | |
| 879 | + | |
| 880 | + | |
| 881 | + | |
| 882 | + | |
| 883 | + | |
| 884 | + | |
| 885 | + | |
| 886 | + | |
| 887 | + | |
| 888 | + | |
| 889 | + | |
| 890 | + | |
| 891 | + | |
| 892 | + | |
| 893 | + | |
865 | 894 | | |
866 | 895 | | |
867 | 896 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
102 | 102 | | |
103 | 103 | | |
104 | 104 | | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
118 | | - | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
| 105 | + | |
| 106 | + | |
128 | 107 | | |
129 | 108 | | |
130 | 109 | | |
| |||
146 | 125 | | |
147 | 126 | | |
148 | 127 | | |
| 128 | + | |
149 | 129 | | |
150 | 130 | | |
151 | 131 | | |
| |||
207 | 187 | | |
208 | 188 | | |
209 | 189 | | |
210 | | - | |
| 190 | + | |
211 | 191 | | |
212 | 192 | | |
213 | 193 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
12 | 16 | | |
13 | 17 | | |
14 | 18 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
124 | 124 | | |
125 | 125 | | |
126 | 126 | | |
127 | | - | |
| 127 | + | |
128 | 128 | | |
129 | 129 | | |
130 | 130 | | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
136 | 134 | | |
137 | 135 | | |
138 | 136 | | |
139 | 137 | | |
140 | 138 | | |
141 | 139 | | |
142 | | - | |
143 | | - | |
144 | | - | |
145 | 140 | | |
146 | 141 | | |
147 | 142 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | | - | |
41 | | - | |
| 40 | + | |
42 | 41 | | |
43 | 42 | | |
44 | 43 | | |
| |||
0 commit comments