Repository navigation
Commit 1287b2f
docs(ui): generate the component reference for all 106 components from sdui.manifest.json; retire the Setup, Console and Studio pages into the metadata pages and rename the section User Interface (#27179)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} -->
_Requested by **Tesla** · [project
thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoD1czdsHPibaHqYrbaZSAWH)_
Fixes #27142
Clause-②: no
Metadata-protocol: none
Tier H: the diff edits `.github/workflows/lint.yml` (one new gate step),
so the maintainer lands it.
## 维护者速读
- **改了什么:** 新增 `scripts/gen-sdui-reference.mjs`,从根目录
`sdui.manifest.json` 生成文档站的「组件参考」(`content/docs/ui/components/`:7 个分类页加
1 个索引页),一次覆盖全部 106 个组件,每个组件列出输入项的类型、可选值、是否必填和说明。lint 新增一步 `pnpm
check:sdui-reference`:manifest 变了而参考页没重新生成,这一步就红。
- **为什么改:** 106 个可编写组件里有 89 个过去没有自己的文档页,输入项说明只存在于 manifest 文件里。manifest
由 CI 对照渲染器校验,参考页从它生成,就不会像 17 个手写页那样与渲染器脱节。
- **风险与回滚:** 只新增文档页、一个脚本和一个 lint
步骤,不发布任何包;另把这个目录登记为「生成物」(文档审计范围、合并驱动、role-word 扫描、os-regen-merge
分类四处名单)。回滚 = revert 本 PR。
- **先看:** ① `scripts/gen-sdui-reference.mjs` 的 `pageFor()`(分页规则)和
`cellText()`(MDX 转义);② `.github/workflows/lint.yml` 新增的那一步;③
`scripts/check-role-word.mjs` 的 `SKIP_SUBTREES`。
- **两分钟验证:** 运行 `node scripts/gen-sdui-reference.mjs --check`,应看到 `✓ …
matches sdui.manifest.json (106 components on 7 pages)`;或在预览站打开
`/docs/ui/components`,索引表有 106 行。
- **范围扩大(按你 12:23Z 的两条裁定:「删掉」「板块名用 User Interface」):** 删掉 `ui/` 下的
`setup-app`、`console`、`studio` 三页。三页中写给元数据作者的内容并入
`apps`、`views`、`actions`、`automation/approvals` 和
`deployment/validating-metadata`;产品导览统一指向
`build-without-code`。栏目名从「Views & Apps」改为「User Interface」。另加 3 条跳转,并改了 1
条旧跳转,避免形成跳转链。
- **需要你做的:** Tier H(改了 workflow),需要你审批并合并。
## Wider scope on head `906bbcaa` (maintainer rulings)
In the K3 card thread on 2026-10-28 at 12:23Z, Tesla answered 「删掉」 to
retiring the three Console-tour pages under `content/docs/ui/` and 「板块名用
User Interface」 to the section name. Head `906bbcaa` (on top of a merge
of `main`, `f4815b07`) carries both:
| Retired page | Where its author-facing facts went |
| :--- | :--- |
| `ui/setup-app.mdx` | `ui/apps.mdx`: `navigationContributions` and the
Setup group anchors |
| `ui/console.mdx` | `ui/views.mdx`: the record overlay and per-user
list state. `ui/actions.mdx` gains a troubleshooting line,
`automation/approvals.mdx` the record tab, and
`deployment/validating-metadata.mdx` `_diagnostics`, the sweep endpoint
and runtime predicate failure. The product tour is `build-without-code`.
|
| `ui/studio.mdx` | the same targets as `console`; the tour is
`build-without-code` |
Other changes:
- `content/docs/ui/meta.json` is titled "User Interface", as is the
index `navTitle`. `components` takes the three retired slots.
- `apps/docs/redirects.mjs`: `/docs/ui/setup-app` now redirects to
`/docs/ui/apps`, and `/docs/ui/console` and `/docs/ui/studio` to
`/docs/build-without-code`. `/docs/concepts/setup-app` is re-pointed to
`/docs/ui/apps` so that it does not chain.
- Two inbound links were fixed (`deployment/cloud-connection`,
`permissions/record-view-auditing`), and `handwritten-docs.json` was
regenerated.
- Local gates on `906bbcaa`: 67 derived; 66 exit 0.
`check:skill-examples` is NOT MEASURED: it exited 3 because it needs a
full spec build, so CI reads it.
- The earlier triage PASS (6467314546) was on `c935c785` and does not
cover this head. A delta review is handed to triage.
- For K4 #27145: any old objectui.org URL whose target was
`/docs/ui/console` or `/docs/ui/studio` (rows in #27170's table) must
now aim at `/docs/build-without-code` or the specific `apps`/`views`
anchor. Otherwise `check-docs-redirects` reds on a chain.
## What changes
- **`scripts/gen-sdui-reference.mjs`** (new). Reads `sdui.manifest.json`
and writes `content/docs/ui/components/`. Modes: write (default),
`--check` (exit 1 when the committed pages differ, naming the file and
the first differing line), `--self-test` (20 rows, a pinned battery
roster and floor, and the verdict handshake, copied from
`check-agent-model-declared.mjs`).
- **Layout: one page per namespace**, with two grouping rules. At 106
entries a page per component would be mostly one-row pages, since 48 of
them are HTML elements whose only inputs are `className` and `children`.
So:
- a component whose `tier` is `html` goes to **HTML Elements** (48);
- all `plugin-*` namespaces share **Plugin Blocks** (16), because each
holds one to three components;
- every other namespace has its own page: **Layout & Basic** (`ui`, 14),
**Page Structure** (`page`, 7), **Record** (11), **Elements** (6),
**Actions** (4).
- A namespace the manifest gains with no page row is refused, with the
remedy, so a person writes its title and introduction once.
- **Each component** is an `h2` with an explicit id (`action:button`
becomes `#action-button`), a line with its namespace, tier and whether
it is a container, and an inputs table: name, type (unions, enum values,
the array element type and the data binding included), required,
description. Manifest descriptions are escaped for MDX: braces, angle
brackets and pipes outside code spans, and pipes inside them.
- **The index page** links all 106 components to their anchors.
- **Every page** carries an AUTO-GENERATED comment and a visible callout
that name the regeneration command.
- **Absence is loud.** An unknown component key, input key or tier value
is refused rather than silently omitted from the reference. So are an
empty manifest, a key that is not its component's `type`, and two
components whose heading ids collide.
- **Drift check wiring.** Root aliases `check:sdui-reference`
(`--self-test` then `--check`) and `gen:sdui-reference`. A new
`lint.yml` step in job `lint-repo-tooling` (`Lint · repo & tooling
gates`) runs `pnpm check:sdui-reference`, placed next to the manifest's
own check, so a manifest change and its reference change land in one PR.
It needed a workflow edit because every gate in this repo is wired as
its own lint step; no existing family runs an arbitrary root alias.
- **Navigation.** `content/docs/ui/meta.json` lists `components` after
`setup-app`. `content/docs/ui/index.mdx` gains the matching card, which
`check-section-landing-index` requires.
- **The directory is registered as generated in the four places that
keep such a list:**
- `scripts/docs-audit/affected-docs.mjs` excludes
`content/docs/ui/components/**` from the hand-written audit scope beside
`references/`. `handwritten-docs.json` is regenerated and its readme
names both trees. The maintainer ruling recorded in that file's header
is that prompting authors about generated pages is wrong advice.
- `scripts/regen-artifacts.mjs` gets a `REGEN_ARTIFACTS` row, and
`.gitattributes` routes the directory to `merge=os-regen`.
`check:merge-driver` requires every generator to have a recorded
disposition. The pages are rendered whole, so regenerating after a merge
loses nothing a person wrote.
- `scripts/check-role-word.mjs` adds the directory to `SKIP_SUBTREES`,
beside `references/` and `protocol-upgrade/`, for the reason its header
gives: the fix site is the input description in the component's
registration under `ui/`, which this gate does not scan.
- `scripts/pm/os-regen-merge.sh` classifies the directory in
`OS_REGEN_CLASSES` as `whole` (commit a6ea420). CI's os-regen-merge
self-test requires every `merge=os-regen` path to have a class;
`dispatch-gates` did not derive that family locally, so it was first
seen red on CI.
## Why
A metadata author placing a component finds no page of its own for 89 of
the 106 authorable components today. Their inputs and descriptions exist
only inside the manifest file. The manifest is regenerated and compared
by CI's `Build & E2E UI`, so a reference generated from it cannot drift
from the renderer the way the 17 hand pages could.
## Tests
- `node scripts/gen-sdui-reference.mjs --self-test`: 20 cases pass, 20
batteries at their floor.
- `node scripts/gen-sdui-reference.mjs --check` on the PR head: `✓ …
matches sdui.manifest.json (106 components on 7 pages)`.
- All 106 manifest types are headings, each on exactly one page. The
generator asserts this on every run, and the self-test row "the real
manifest renders every component on exactly one page" asserts it against
the live manifest.
<details><summary>One-time ablation: a hand-edited generated line makes
--check exit non-zero</summary>
Through `scripts/ablation-replace.mjs` on the committed tree, which
drops `yes` from the Required cell of `tabs`'s `defaultValue` row in
`ui.mdx`:
```text
ablation-replace: anchor x1 -> x0
ablation-replace: blob b00794f -> 657a3cff533551a8703d7fcb0dc33c282c636fc5
ablation-replace: ok mutation landed: anchor 1 -> 0, blob b00794f -> 657a3cff5335
ablation-replace: running: node scripts/gen-sdui-reference.mjs --check
✗ gen-sdui-reference --check: content/docs/ui/components/ is not what sdui.manifest.json generates:
- ui.mdx differs from the manifest's rendering, first at line 160:
committed: "| `defaultValue` | `string` | | — |"
generated: "| `defaultValue` | `string` | yes | — |"
ablation-replace: command exited 1
ablation-replace: ok restored: blob == HEAD (b00794f) and `git diff HEAD` is empty
```
</details>
## Acceptance notes
**URL map for K4's redirects (#27145).** The old objectui.org pages are
under `https://www.objectui.org/docs/...` (`ui/apps/site/lib/source.ts`,
`baseUrl: '/docs'`). The new pages are on the objectstack docs site:
| Old path (objectui.org) | New path |
| :--- | :--- |
| `/docs/components` (`components/index`) | `/docs/ui/components` |
| `/docs/components/basic/icon` | `/docs/ui/components/ui#icon` |
| `/docs/components/basic/image` | `/docs/ui/components/ui#image` |
| `/docs/components/basic/span` | `/docs/ui/components/html#span` |
| `/docs/components/basic/text` | `/docs/ui/components/ui#text` |
| `/docs/components/complex/table` | `/docs/ui/components/html#table` |
| `/docs/components/data-display/alert` | `/docs/ui/components/ui#alert`
|
| `/docs/components/data-display/badge` | `/docs/ui/components/ui#badge`
|
| `/docs/components/disclosure/accordion` |
`/docs/ui/components/ui#accordion` |
| `/docs/components/form/button` | `/docs/ui/components/ui#button` |
| `/docs/components/form/label` | `/docs/ui/components/html#label` |
| `/docs/components/layout/box` | `/docs/ui/components/ui#box` |
| `/docs/components/layout/card` | `/docs/ui/components/ui#card` |
| `/docs/components/layout/container` |
`/docs/ui/components/ui#container` |
| `/docs/components/layout/flex` | `/docs/ui/components/ui#flex` |
| `/docs/components/layout/grid` | `/docs/ui/components/ui#grid` |
| `/docs/components/layout/stack` | `/docs/ui/components/ui#stack` |
| `/docs/components/layout/tabs` | `/docs/ui/components/ui#tabs` |
`check-docs-redirects` strips a `#fragment` before it resolves a
destination, so the anchored targets are legal redirect destinations.
**Worked examples the hand pages carry and the manifest lacks.** None of
it was copied into the generated pages:
- Live demos (`SchemaExample`) on every one of the 17 pages.
`form/button` has 15 (variants, sizes, icons, disabled and loading
states); `basic/text` has 10; the rest have 1 to 4.
- `basic/icon`: the section "`name` is identity, not the glyph", which
covers the refused name-as-glyph node and the `migrateIconNodeKeys`
converter from `@object-ui/types` for stored metadata.
- `layout/box`: "Migrating from `div`". `box` reads `children` only, and
an html page's `div` is refused at compile time with a refusal that
names `box`.
- `basic/span`: the deprecation of `span` for JSON-authored pages, which
does not apply to the html tier, and its migration guide.
- `complex/table`, `layout/*` and the others: hand-written TypeScript
`Schema` interfaces. Where these differ from the manifest, the manifest
is the contract.
**Observations, not filed:**
- `content/docs/ui/pages.mdx` § Components lists `record:chatter`,
`element:image`, `element:record_picker` and `element:text_input`, which
are not in the public manifest. The manifest carries
`record:discussion`, `element:definition-list` and `element:repeater`,
which that list omits. That list may be describing the spec's
`PageComponentType` rather than the public tier, so this is noted for
K2's merge of the UI pages, not filed.
- The `object-kanban` input `cardFields` uses the word "role" in its
description, a reserved word under ADR-0090 D3. The fix site is that
registration's description under `ui/`, outside this card's surface.
Triage's contract review (6467314546) asked for a carrier card. The
dispatching seat screened it out under the filing gate's business test:
the phrase is "the object's `highlightFields` role", which means
ADR-0085's semantic field role, not an access-control role. No reader is
misled about permissions, and check-role-word's own header says not
every hit is a bug (its examples are ARIA `role` and `sys_member.role`).
If the word should still change, the next PR that edits
`ui/packages/plugin-kanban/src/index.tsx` can say "designation".
- The serial constraint with K2 (#27170) stands: both edit
`content/docs/ui/meta.json`, so whichever lands second merges `main`.
## Verification
CI on head `c935c785` (merges `main` after K2 #27170 landed;
`content/docs/ui/meta.json` and the UI landing index list `console`,
`studio`, `components` in that order): every required context is green
(Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core,
Governed Surface Queue Guard, Lint UI, Type Check UI, Test UI, Build &
E2E UI), Build Docs is green, and `mergeable_state` reads clean.
Local, on head `ca0a9747`, which merges `main` at `b8615afa`:
- `pnpm --filter @objectstack/docs build`: green (`os-verify-lock`
VERDICT command-exit 0). Served with `next start`,
`/docs/ui/components/plugins` answers 200 with 16 tables and
`id="object-grid"`, and `/docs/ui/components` answers 200 with 107 table
rows (106 plus the header).
- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--ran …`: 99 derived families, 98 run green, 1 NOT MEASURED
(`check:dual-build-cjs-loads`, exit 3, which needs every published
package's dist and so is left to CI).
- First-run reds the sweep found, each fixed in this PR and re-run
green: `check-section-landing-index` (the landing card),
`check:docs-audit-scope` (the generated-tree exclusion),
`check:merge-driver` (the `REGEN_ARTIFACTS` row) and `check:role-word`
(`SKIP_SUBTREES`). `check:pm-dispatch-gates` was red once because it ran
while those edits were in progress; it was re-run on the committed tree
and is green.
- The gate families CI runs are read from this PR's check-runs, not
re-run locally.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(No spec change.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` is CI's to run.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. Nothing publishes, so there is no changeset (`skip-changeset`);
both declaration lines are filled in above.
- [x] 6. `packages/spec` 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.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L9qibwMyAnTNeas3ANYhyi
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L9qibwMyAnTNeas3ANYhyi)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 385aa12 commit 1287b2f
32 files changed
Lines changed: 2600 additions & 596 deletions
File tree
- .github/workflows
- apps/docs
- content/docs
- automation
- deployment
- permissions
- ui
- components
- scripts
- docs-audit
- pm
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
158 | 158 | | |
159 | 159 | | |
160 | 160 | | |
| 161 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1013 | 1013 | | |
1014 | 1014 | | |
1015 | 1015 | | |
| 1016 | + | |
| 1017 | + | |
| 1018 | + | |
| 1019 | + | |
| 1020 | + | |
| 1021 | + | |
| 1022 | + | |
| 1023 | + | |
| 1024 | + | |
| 1025 | + | |
| 1026 | + | |
1016 | 1027 | | |
1017 | 1028 | | |
1018 | 1029 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
63 | | - | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
64 | 70 | | |
65 | 71 | | |
66 | 72 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
759 | 759 | | |
760 | 760 | | |
761 | 761 | | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
762 | 771 | | |
763 | 772 | | |
764 | 773 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
252 | 252 | | |
253 | 253 | | |
254 | 254 | | |
255 | | - | |
256 | | - | |
| 255 | + | |
| 256 | + | |
257 | 257 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
777 | 777 | | |
778 | 778 | | |
779 | 779 | | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
| 842 | + | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
780 | 847 | | |
781 | 848 | | |
782 | 849 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
204 | 204 | | |
205 | 205 | | |
206 | 206 | | |
207 | | - | |
208 | | - | |
| 207 | + | |
| 208 | + | |
209 | 209 | | |
210 | 210 | | |
211 | 211 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
663 | 663 | | |
664 | 664 | | |
665 | 665 | | |
666 | | - | |
| 666 | + | |
667 | 667 | | |
668 | 668 | | |
669 | 669 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
749 | 749 | | |
750 | 750 | | |
751 | 751 | | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
752 | 808 | | |
753 | 809 | | |
754 | 810 | | |
| |||
0 commit comments