Skip to content
This repository was archived by the owner on Oct 26, 2026. It is now read-only.

Commit 64c173d

Browse files
docs(skills): the page-builder guide authors object-grid and object-gantt in the properties bag (objectui#10859; objectui#11276 rider) (#11404)
Part of #10859 Clause-②: no Part of #11276 — the declared rider: objectui#11276's release comment 5940114124 names this seat as carrier of the "Grid plugin example", so the two examples move in ONE governed PR and one approval covers both. ⛔ Governed surface (`skills/**`): this PR stays a DRAFT until an authorized approver (`os-zhuang` / `hotlong`) leaves an APPROVED review; the claiming seat lands it after that. `node scripts/check-governed-queue-guard.mjs --test skills/objectui/guides/page-builder.md` answers GOVERNED (exit 3). ## What changes One file, `skills/objectui/guides/page-builder.md`, its "Plugin integration in page schemas" section: - **"Grid plugin example"**: the `object-grid` node now writes `objectName` and `columns` inside `properties`; `bind` stays on the node, where objectui's `BaseSchema` declares it (the bag refuses it, measured below). PR objectui#11399 moved the arm and listed this governed producer without editing it. - **"Gantt plugin example"**: the `object-gantt` node now writes `objectName` and the `gantt` block inside `properties`. PR objectui#11337 moved the arm; this is the governed item objectui#10859's release comment 5926748567 names. - **The section lead** no longer says plugin widgets "read their configuration off the node exactly like the built-in renderers do". It names the three arms that take the bag (`object-grid`, `object-form`, `object-gantt`) in the wording the "Form plugin example" header used, says `objectui validate` refuses the flat spelling by name, says `object-kanban` declares its keys on the node, and keeps the true half: the `props` envelope is not read. - **The prose after the gantt fence** that taught the flat `startDateField` / `endDateField` keys "on the node" (the ObjectView / ListView flatten product) is retired. One sentence replaces it: the flat keys are refused by name and the refusal points at `properties.gantt.KEY`. The sentence on the required trio and the README pointer stay. - The "Form plugin example" header drops the sentence the lead now carries, so the three bag examples read alike. No other file. No changeset: `node scripts/check-changeset-presence.mjs` answers "No source or published contract of a released package changed in this range, so no changeset is owed" — the PR objectui#11397 precedent: `skills/**` sits outside every changesets-versioned package, so the repo rule asks for neither a bump nor an empty-frontmatter declaration; `skip-changeset` has no reader in this repo, so no label either. ## Line readings (`skills/**`, governed; budget net 0, no growth) | reading | before (`6aa029b63f`) | after | net | |---|---|---|---| | `skills/objectui/guides/page-builder.md`, whole file | 353 | 352 | −1 | | whole bundle, every file under `skills/` (27 files) | 5408 | 5407 | −1 | | every `SKILL.md` under `skills/`, summed | 142 | 142 | 0 | `git diff --numstat` for the one file: 23 added, 24 deleted. The two bag wrappers cost 4 lines; the retired gantt prose (6 lines out, 2 in) and the shortened form header paid for them. No token ratchet is defined for `skills/**` in this repo, so lines are the only reading. ## Measured (worktree at base `6aa029b63f`, then at the head; Node v22.22.2 from /opt/nvm, since the container's v22.22.0 is refused by objectui's engine-strict install) **Every `os:check` JSON fence in this file, through both authoring faces**, read from the BUILT `@object-ui/types` dist: `safeValidateSchema` (the function `objectui validate` calls) and `StrictAnyComponentSchema`. The file has 8 such fences plus one `typescript` and one `tsx` fence the faces do not judge. | fence (by `type`) | at base, tolerant / strict | at head, tolerant / strict | |---|---|---| | `card` (section 3) | ACCEPT / ACCEPT | ACCEPT / ACCEPT | | `action:button` | ACCEPT / ACCEPT | ACCEPT / ACCEPT | | `statistic` | ACCEPT / ACCEPT | ACCEPT / ACCEPT | | `card` (expression boundaries) | ACCEPT / ACCEPT | ACCEPT / ACCEPT | | `object-grid` | REFUSE / REFUSE: `invalid_type` at `objectName` and at `columns` ("Did you mean `objectName` → `properties.objectName`?"), plus the record-source rule at `properties.objectName` | ACCEPT / ACCEPT | | `object-form` | ACCEPT / ACCEPT | ACCEPT / ACCEPT | | `object-kanban` | ACCEPT / ACCEPT | ACCEPT / ACCEPT (fence unchanged) | | `object-gantt` | REFUSE / REFUSE: `invalid_type` at `objectName` and at `gantt` ("Did you mean `gantt` → `properties.gantt`?"), plus the record-source rule at the root | ACCEPT / ACCEPT | Count: 2 of 8 refused at base, 0 of 8 at head. **Public door, end to end.** The built `packages/cli/dist/cli.js validate` on each fence as a standalone document: the base grid and gantt fences exit 1, "Schema validation failed", printing the `Did you mean … → properties.…` lines above; the head grid and gantt fences exit 0, "Schema is valid", `Type: object-grid` / `Type: object-gantt`. **The rewrites' edges, each a probe document on the same two faces:** - `bind` inside the grid's bag: REFUSE on both faces, `unrecognized_keys` at `properties` naming `bind` ⇒ `bind` stays on the node, as PR objectui#11399's producers keep it. - The gantt trio: a bag `gantt` block missing `endDateField`, `startDateField` or `titleField` is refused at `properties.gantt.KEY` on both faces; the block with only the three is accepted ⇒ "Those three `gantt` keys are the required ones; every other option … is optional" stays true. - The flat gantt keys on the node (`startDateField` / `endDateField` / `titleField`): REFUSE on both faces, each "Did you mean `KEY` → `properties.gantt.KEY`?" ⇒ the replacement sentence says what the validator prints. - `object-kanban` in a bag (`properties.objectName` / `properties.groupBy`): the tolerant face accepts it (passthrough), the strict face refuses `unrecognized_keys` naming `properties`; the flat kanban node is accepted by both ⇒ the lead's "`object-kanban` declares its keys on the node" is the measured reading, and that example is untouched. - Control: `{ "type": "timeline" }` is accepted by both faces. **The same sweep over every other `.md` under `skills/`** (report only, nothing edited): 16 files, 70 `os:check` JSON fences. The marker's contract for a JSON fence is "parses" (`check-skill-examples.mjs`), not "is a component document", and most refusals are fences whose root is not a node at all (no `type`: `project-setup.md` 5, `schema-expressions.md` 6, `plugin-development.md` 1, `rules/protocol.md` 1). Three fences with a registered `type` are refused: `guides/data-integration.md` and `guides/schema-expressions.md` each carry a `list` node written with `bind` only, refused at `items` (`invalid_type`) by both faces, and `guides/plugin-development.md` carries a `type: "module"` fence (`invalid_union` at `type`, a custom plugin type the guide registers in prose). `rules/protocol.md`'s `card` fence with a `props` envelope is refused by the strict face only, which is the trap that passage documents. Listed in the dev report for the seat; not this PR's. ## Gates (every exit code captured to a file before any pipe) At `ccb4e7fb`, the guide commit (`skills/**` is byte-identical at the head `d83d2748`, which only merges `origin/main` `7728c67c`): | gate | exit | verdict line | |---|---|---| | `pnpm exec turbo run build` of `check-skill-examples.mjs --build-filter`'s closure, `--concurrency=2`, under the shared verify lock | 0 | `Tasks: 29 successful, 29 total` | | `pnpm check:skill-examples` | 0 | `Every marked skill example holds up against the built types.` (`JSON phase: 70 fence(s) parsed, 0 failed.`) | | `pnpm exec vitest run` of the 12 ledgered test files that read `skills/objectui/**` (per `scripts/markdown-test-inputs.mjs`: `skill-guide-provider-envelope`, `skill-guide-data-table-binding`, `skill-guide-permission-config`, `check-installed-spec-pin-claims`, `check-shell-escape-residue`, `check-skill-eval-tokens`, `check-skill-examples`, `check-skills-paths`, `doc-version-claims`, `dollar-dialect-alias-census`, `layered-read-declared-path-4016`, `markdown-test-inputs`), under the lock | 0 | `Test Files 12 passed (12) · Tests 389 passed (389)`, `VERDICT command-exit 0` | At the head `d83d2748`: | gate | exit | verdict line | |---|---|---| | `pnpm check:skills-paths` | 0 | `OK (89/90 stated path(s) resolve across 20 guide file(s); 1 baselined)` | | `pnpm check:skill-eval-tokens` | 0 | `Every must_contain token is taught by its own skill bundle.` | | `pnpm check:control-bytes` | 0 | `OK (scanned 9926 tracked text file(s); skipped 85 binary)`; a self-scan of the file for control bytes: 0 hits | | `pnpm check:shell-escape-residue` | 0 | `OK (5/5 root(s) resolved …)` | | `node scripts/check-changeset-presence.mjs` | 0 | `No source or published contract of a released package changed in this range, so no changeset is owed.` | | `pnpm changeset:check` | 0 | `No changeset declares a major bump.` | | `pnpm check:new-line-citations` | 0 | `0 new citation(s), enforcement report-only` | | `pnpm check:changeset-claims` (report-only) | 0 | names `.changeset/6475-gantt-block-face-declared.md`, whose census sentence says this guide's `gantt` block carries the trio: still true (the block moved into the bag with all three keys), so no dated note is owed | | `node scripts/check-governed-queue-guard.mjs --test skills/objectui/guides/page-builder.md` | 3 | `GOVERNED — 1 of 1 path(s)`, the expected answer: draft until approval | Not run, with the reason: `check:doc-snippets`, `check:doc-examples`, `check:doc-example-ids` and `check:doc-fences` scan `content/docs` and the package READMEs (`check-doc-snippet-types.mjs`'s header says `skills/objectui/**` is not claimed by any gate there), so they cannot read this diff. Repo-wide `pnpm lint` and `pnpm test` are CI's runs. ## 维护者速读(草稿) **改了什么。** 只改一份对外发布的技能指南 `skills/objectui/guides/page-builder.md` 的「Plugin integration in page schemas」一节,净 −1 行。两个示例(`object-grid` 的 Grid 示例、`object-gantt` 的 Gantt 示例)从「属性平铺在节点上」改成写进 spec 规定的 `properties` 包;`bind` 留在节点上。该节的导语不再说「插件组件像内建渲染器一样直接从节点上读配置」,改为点名三个走 `properties` 包的类型(`object-grid` / `object-form` / `object-gantt`)、说明 `objectui validate` 会按键名拒收平铺写法、`object-kanban` 仍在节点上声明键,并保留「`props` 信封不会被读」这句真话。Gantt 示例下面那段教「也可以把 `startDateField` 等键平铺在节点上」的文字删掉,换成一句:平铺键会被按名拒收,拒收信息会指向 `properties.gantt.KEY`。 **为什么改。** 这两个示例是 AI 写页面时的直接范本,而它们教的写法现在会被 `objectui validate` 拒收(实测:基线上的两段 JSON 用构建后的 CLI 校验都是 `exit 1`、「Schema validation failed」;改后两段都是 `exit 0`、「Schema is valid」)。拒收来自已合并的 PR objectui#11337(object-gantt,objectui#10859 第 6 批)和 PR objectui#11399(object-grid,objectui#11276):按平台 spec 的 `PageComponentSchema`,组件自己的属性只能住在 `properties` 包里,objectui 的校验面与 `os validate` 对齐。指南是受管面,所以那两张卡都把它列出而没有改,由本 PR 一次性承接,一次批准覆盖两个示例。文件里其余 6 个 `os:check` JSON 示例在基线上就通过校验,本次全部复测通过。 **风险与代价(含回滚)。** 零代码、零发布包改动、无 changeset(门禁判定本次不欠)。风险只有措辞是否准确,每句都有上面的实测支撑(包括 `bind` 放进包里会被拒、gantt 三个必填键、kanban 平铺仍被接受)。回滚 = revert 这一个 commit,指南回到旧写法——但旧写法会被校验器拒收,所以不建议回滚。本 PR 是受管面(`skills/**`),须人工批准后才能落地。 **席位意见。** (留空) **你要做的。** 若认可措辞,在本 PR 留一条 APPROVED review;认领席随后转 ready 并走合并队列落地。 ## Acceptance notes - Observed, not filed, for the seat: three `os:check` JSON fences in other governed guides are refused with a registered `type` (two `list` nodes refused at `items`, one `module` custom type), listed under "Measured" above. Not this PR's file; the dev report carries them with dedupe words. - Observed, not filed: the "Kanban plugin example" writes `bind` on the node, which objectui's `BaseSchema` declares and both objectui faces accept; the spec's `PageComponentSchema` refuses `bind` (recorded on objectui#10859 at the batch-6 ACCEPT as a `BaseSchema`-wide question). Unchanged here. - The branch head is a merge of `origin/main` `7728c67c` (objectui#11395, `packages/layout`) into the guide commit; the merge commit carries git's default message, and objectui forbids force-push, so it is not rewritten. The squash landing drops it. Session `session_01JG2jy8a9su7ia4Hx7zxv42`, seat `domain:ui#2`, account `os-sales`. --- _Generated by [Claude Code](https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent abca986 commit 64c173d

1 file changed

Lines changed: 23 additions & 24 deletions

File tree

‎skills/objectui/guides/page-builder.md‎

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -229,22 +229,24 @@ no second copy.
229229

230230
## Plugin integration in page schemas
231231

232-
When pages need heavy widgets (grids, forms, kanbans, charts), import the plugin package and ensure its components are registered before rendering. Plugin
233-
widgets read their configuration off the node exactly like the built-in
234-
renderers do (`schema.objectName`, `schema.columns`, `schema.fields`,
235-
`schema.gantt`) — the `props` envelope is not read here either.
232+
When pages need heavy widgets (grids, forms, kanbans, charts), import the plugin package and ensure its components are registered before rendering. An
233+
`object-grid`, `object-form` or `object-gantt` takes its props in the spec's
234+
`properties` bag, and `objectui validate` refuses the flat spelling by name;
235+
`object-kanban` declares its keys on the node. The `props` envelope is not read here either.
236236

237-
**Grid plugin example:**
237+
**Grid plugin example** — `bind` is a node key, so it stays beside the bag:
238238
<!-- os:check -->
239239
```json
240240
{
241241
"type": "object-grid",
242-
"objectName": "products",
243-
"columns": [
244-
{ "field": "name", "label": "Name", "type": "text" },
245-
{ "field": "price", "label": "Price", "type": "currency" },
246-
{ "field": "status", "label": "Status", "type": "select" }
247-
],
242+
"properties": {
243+
"objectName": "products",
244+
"columns": [
245+
{ "field": "name", "label": "Name", "type": "text" },
246+
{ "field": "price", "label": "Price", "type": "currency" },
247+
{ "field": "status", "label": "Status", "type": "select" }
248+
]
249+
},
248250
"bind": "products"
249251
}
250252
```
@@ -255,8 +257,7 @@ renderers do (`schema.objectName`, `schema.columns`, `schema.fields`,
255257
> names the field a form input writes. A grid column written as `{ "name": ... }`
256258
> names no field, so `ObjectGrid` drops it.
257259
258-
**Form plugin example** — an `object-form` takes its props in the spec's `properties`
259-
bag, and `objectui validate` refuses the flat spelling by name:
260+
**Form plugin example:**
260261
<!-- os:check -->
261262
```json
262263
{
@@ -288,11 +289,13 @@ bag, and `objectui validate` refuses the flat spelling by name:
288289
```json
289290
{
290291
"type": "object-gantt",
291-
"objectName": "project_task",
292-
"gantt": {
293-
"titleField": "name",
294-
"startDateField": "start_date",
295-
"endDateField": "end_date"
292+
"properties": {
293+
"objectName": "project_task",
294+
"gantt": {
295+
"titleField": "name",
296+
"startDateField": "start_date",
297+
"endDateField": "end_date"
298+
}
296299
}
297300
}
298301
```
@@ -301,12 +304,8 @@ Those three `gantt` keys are the required ones; every other option — the tree,
301304
dependency, baseline, resource-view, quick-filter, working-calendar and
302305
read-only surfaces — is optional and documented in
303306
[`@object-ui/plugin-gantt`'s README](https://github.com/objectstack-ai/objectui/blob/main/packages/plugin-gantt/README.md).
304-
The same configuration can also be written as flat `startDateField` /
305-
`endDateField` / ... keys **on the node** — never under `props`, which no
306-
`ui:*` renderer reads. That flat spelling is the internal ObjectView / ListView
307-
flatten product: it is taken only when there is no `gantt` block, and a node
308-
carrying both renders the block and warns about the ignored top-level keys.
309-
Author the `gantt` block.
307+
A flat `startDateField` / `endDateField` / `titleField` on the node is refused
308+
by name; the refusal points at `properties.gantt.KEY`, the block's one home.
310309

311310
Import plugins in your app entry point to trigger registration:
312311
<!-- os:check -->

0 commit comments

Comments
 (0)