Skip to content

docs(skills): the page-builder guide's object-form example names its fields; a per-form override goes on a section entry (objectui#11550) - #11561

Draft
objectstack-fleet[bot] wants to merge 1 commit into
mainfrom
claude/issue-11550-guide-fields-names-only
Draft

objectstack-fleet[bot] wants to merge 1 commit into
mainfrom
claude/issue-11550-guide-fields-names-only

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #11550
Clause-②: no

Governed (skills/**). This PR stays DRAFT until an account in GOVERNED_APPROVERS approves it. An agent seat never submits that approval. The code half is objectui#11560; the two land independently, in either order.

Executes item 1 of triage ruling 5969880008 on the governed half: the published page-builder guide stops teaching the { name } entry in object-form fields.

Session: https://claude.ai/code/session_01FjqrwXPfSMkSfkKYDSRkN2 (os-dev, mode:subagent of the PM seat; identity = this branch).

What changes

One file, skills/objectui/guides/page-builder.md: the object-form example under "Form plugin example" (the os:check-marked json fence) and the note directly above it.

Before After
example fields [{ "name": "name", "label": "Name", "type": "text", "required": true }, { "name": "email", "label": "Email", "type": "text" }] ["name", "email"]
note heading "Grid columns key off field; form fields key off name." "Grid columns are { "field" } objects; object-form fields are names."
note body pairs ListColumn.field with FormField.name as one pair of words for opposite things keeps the grid half (a { "name": ... } column names no field and ObjectGrid drops it), and says that form labels, types and required come from the object fields, and that a per-form override goes on a sections[].fields entry, { "field": "email", "required": true }

Why. The form draws each entry by name and silently drops label, type and required; the declared member (ObjectFormSchema.fields) is string[]. The old note was written against the old example ("the two layers sit next to each other here"), so it would no longer be true of the respelled one. Where the override goes is the ruling's own sentence: "A label or required override goes on a section entry or on the object field. type is always the object field's." The section-entry override (label, required, visibleWhen on a { field } entry, on all six arms) is pinned by sectionEntryOverrides-10475.test.tsx.

Line budget (skills/** net at most 0)

  • The file: 352 lines before, 349 after (6 added, 9 removed, net -3).
  • The whole package, every tracked file under skills/objectui/ (which is all of skills/**): 5418 before, 5415 after.
  • Every SKILL.md (one file): 142 before, 142 after, untouched.
  • No line was bought by re-wrapping. The note stays five lines, and the -3 is the two fields entries collapsing into one line of names.

What the os:check gate judges, and the other doors

  • pnpm check:skill-examples at 6ce28b7 (packages built with pnpm turbo run build --concurrency=2 over the gate's own --build-filter): exit 0. It printed JSON phase: 70 fence(s) parsed, 0 failed., and --list shows the example's fence as [json] marked pass.
  • That gate parses a json fence and does not validate it. Its header says: "It is parsed, not validated against @object-ui/types' schemas." The validation doors were measured on objectui#11560's base with a throwaway probe. The authored node with names only is accepted by safeValidateSchema and by StrictAnyComponentSchema, and so was the old example. The node's properties bag is the spec's ComponentPropsMap['object-form'] row by reference, and that row types fields members as unknown until spec(ui): the ComponentPropsMap rows still type renderer-read members as z.unknown() — navigation on object-map / object-gantt / object-tree and conditionalFormatting on object-kanban accept 42 — the family close-out after #21445 objectstack#21464 types them.
  • Each exit 0: check:skills-paths, check:skill-eval-tokens, check:control-bytes, check:new-line-citations (0 new), check:doc-fences, and check-changeset-presence (no released package changed, so no changeset is owed).
  • check:changeset-claims names one pending changeset, .changeset/6475-gantt-block-face-declared.md, because it mentions this file. I read the paragraph: it is a census of the guide's gantt blocks, which this PR does not touch, so it is still true.
  • check-governed-queue-guard.mjs --test skills/objectui/guides/page-builder.md: GOVERNED (exit 3), as expected.

Order

The two PRs land independently. objectui#11560 changes plugin-form test fixtures and a pin, and does not read this file. This file's example passes every door measured above, with or without objectui#11560.

维护者速读(草稿)

改了什么:对外发布的 page-builder 技能指南里,object-form 示例的 fields 从「带 name、label、type、required 的对象」改成「字段名字符串」(["name", "email"]);示例上方那段说明同步改写,讲清楚标签、类型、必填来自对象字段,单个表单要覆盖时写在 sections[].fields 的条目上。只动这一个文件,净减 3 行。

为什么改:AI 和开发者照着这份指南写表单。旧示例教的写法,表单只认 name,另外三个键被静默丢掉。作者以为设了必填,实际没有生效。本仓声明的类型本来就是字符串数组,triage 裁定退役这种写法(裁决评论 5969880008 第 1 条)。

风险与代价(含回滚):风险低。只改文档文本,运行时零变化;已经按旧写法存下的元数据照样能显示(代码侧的读取保留,由 objectui#11560 的测试钉住)。回滚就是 revert 这一个提交。

席位意见:

你要做的:审读这一处示例与说明的改写;同意就在本 PR 留一条 APPROVED review,之后由认领席翻 ready 并入合并队列。


Generated by Claude Code

…fields; a per-form override goes on a section entry (objectui#11550)

Executes item 1 of triage ruling 5969880008 on the governed half. The
`os:check` `object-form` example taught `fields: [{ name, label, type,
required }, ...]`; the form draws each entry by name and drops the other
three keys, and the declared member is `string[]`. It now reads
`"fields": ["name", "email"]`. The note above it, which paired grid
`field` with form `name`, now says where labels, types and `required`
come from (the object fields) and where a per-form override goes (a
`sections[].fields` entry). Net -3 lines.

Claude-Session: https://claude.ai/code/session_01FjqrwXPfSMkSfkKYDSRkN2
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 1 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/6475-gantt-block-face-declared.md

  • names skills/objectui/guides/page-builder.md → skills/objectui/guides/page-builder.md — edited by this change

    Maintainer ruling, objectui#6475 (2026-08-27), Option A — enforce as-is, immediately, no warning window (the startup-stage no-gradualism rule, objectstack#12668: transitions do not get phased windows without named external-user evidence, and none exists here). A census of every gantt block reachable through ObjectGanttSchema in this repository — the examples/schema-catalog fixtures, content/docs/plugins/plugin-gantt.mdx, and the published skills/objectui/guides/page-builder.md guide — found zero blocks missing the trio.

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Compared the checked-out tree with d0c0c7fe9 (merge-base with origin/main): 1 file(s) changed outside .changeset/, read against 2084 pending declaration(s) that publish a body (2718 pending in total). · run

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 6ce28b777773fe401c4eeb1f0d62065fb91821e7
Local-runs: none

Scope: PR #11561, one file, skills/objectui/guides/page-builder.md, +6 / -9 against main (merge-base d0c0c7fe9, which is also the PR's recorded base; the file is byte-identical on main between that base and main's current tip, so the two-dot and three-dot diffs agree). Judged as published teaching against objectui main (read at d0c0c7fe9, by ref, no checkout) and the @objectstack/spec 17.6.0 tarball (npm pack, read in scratch). Inputs were card #11550 (body and all four comments, ruling 5969880008 included), the PR body and file list, and the 39 check-runs on the head.

① Derived judgments

Every sentence the diff puts into the guide, named right or wrong:

  1. "fields": ["name", "email"] in the os:check object-form example — RIGHT, and it teaches what ruling 5969880008 item 1 orders ("the guide and the fixtures are respelled to names"). objectui main declares the member as names on both faces: ObjectFormSchema.fields?: string[] (packages/types/src/objectql.ts) and the zod mirror fields: z.array(z.string()).optional().describe('Included fields') (packages/types/src/zod/objectql.zod.ts). The renderer's pool draws each entry by name and reads nothing else off it (ObjectForm.tsx: typeof fieldName === 'string' ? fieldName : (fieldName as any).name), which is the card's measured "silently drops label, type, required". The spec row the node is typed by (ComponentPropsMap['object-form'] in 17.6.0's dist/ui/index.d.ts) types fields as z.ZodArray of ZodUnknown, so the spec neither admits nor refuses either spelling today; the guide now teaches exactly objectui's declared member, which is the narrower and correct one. Pin "the guide's os:check example parses with names only" holds: Skill Example Check is success on the head. Noted for the reader of that gate: check-skill-examples.mjs's own header says a json fence "must parse" — the gate is syntax, so the semantic claim rests on the declared types above, not on the green.

  2. "Form labels, types and required come from the object fields" — RIGHT. generateField in ObjectForm.tsx builds label from fieldLabel(schema.objectName, name, field.label ...), type from mapFieldTypeToFormType(field.type, ...) and required from isRequiredInForm(field, ...), where field is objectSchema.fields[name]. This is the ruling's own sentence: "type is always the object field's."

  3. "a per-form override goes on a sections[].fields entry, { "field": "email", "required": true }" — RIGHT on both doors. Spec 17.6.0: FormSectionSchema.fields: z.array(z.union([z.string(), FormFieldSchema])), and FormFieldBaseSchema is keyed field: z.string() with label ("Display label override") and required: z.boolean().optional() ("Required override"). objectui main: ObjectFormSection.fields?: (string | FormField)[]; sectionFields.ts names { field: 'name', required: true, colSpan: 2 } as the canonical entry and normalizeSectionField applies fd.label and fd.required over the object field; sectionEntryOverrides-10475.test.tsx pins LABEL and REQUIRED on all six arms. This is the ruling's "A label or required override goes on a section entry or on the object field." Not touched: the section inline runtime entry (ruling item 2, kept) — the diff teaches nothing about it, right either way.

  4. "Grid columns are { "field" } objects ... a grid column written as { "name": ... } names no field and ObjectGrid drops it" — RIGHT, and unchanged in substance from main. Spec ListColumnSchema.field: z.string() is required; objectui's ListColumnSchema = stripImportedDefaults(SpecListColumnSchema); the grid's column filter is resolvesToDataColumn (columnSpellingDiagnostics.ts: typeof entry.field === 'string' and a non-empty entry.field), and its diagnostic reads "no field key, so the column names no field."

  5. Removed sentence, "FormField.name names the field a form input writes" — RIGHT to remove. FormField.name is the runtime/custom-field vocabulary (shape 3 and customFields); it was only in the note because the old example carried a FormField-shaped entry. With the example respelled, keeping it would re-teach the name key beside an example that no longer uses it. At the head, no fence in the guide carries a name-keyed form entry.

  6. Public surface: the published skill bundle changes by text only. SKILL.md is untouched (142 lines before and after), so the eval must_contain tokens are unmoved (Skill Eval Token Check success). Line budget as the claim set it: skills/** net at most 0 — measured 352 to 349 on the file and 5418 to 5415 over every tracked file under skills/, with no re-wrapped line (the note stays five lines; the -3 is the two object entries collapsing to one line of names).

Two notes, neither a defect: the override example is shown inline without its enclosing sections block, and the note does not restate that a { "field" } entry placed in TOP-LEVEL fields resolves to no name there (the registration description in plugin-form/src/index.tsx says so; the heading "object-form fields are names" carries the rule).

② Semver level

No changeset is owed and none is present — right. skills/** is not an npm package: there is no package.json under skills/objectui/ and it is outside the .changeset/config.json fixed group. objectui's own gate, scripts/check-changeset-presence.mjs, guards "the SOURCE of a package changesets versions" (fixed-group src/, the build entry, the files list minus markdown) and the published-contract fields of package.json; this diff touches none of it, and Changeset Declaration and Changeset Claim Re-read are success on the head. The one pending changeset that names this file, .changeset/6475-gantt-block-face-declared.md, cites it only as a census of gantt blocks, which this diff does not touch — still true.

Clause-②: no — right, with no arm. The published packages' accept sets do not move: the renderer's stored-value read of a { name } entry stays (that is PR A, objectui#11560, test-only), the zod mirror already refused the entry, and nothing is added to any public surface. The narrowing here is of what the guide teaches, not of a contract, so (narrowing) would be wrong and yes has no basis.

③ Boundary flags

Dev report 5970488708 deviations and open question, each answered for this PR:

  • Open question (who respells plugin-form/src/index.tsx's four fields descriptions and sectionFields.ts's warning text, which still say "{ name } is tolerated") — verified at main (index.tsx three fields rows plus the master-detail row; sectionFields.ts warnUnresolvedTopLevelField). Answered: this does not make the guide false — those descriptions also lead with "Bare field names to show" — so it does not block this PR. The seat's choice A (a patch round on this card after objectui#11536 lands, with a patch changeset because that text ships) is consistent with the ruling's "the authoring faces stop teaching it". ESCALATED as a tracked follow-up on card plugin-form draws { name } field entries and inline runtime-field section entries that objectstack's form view refuses; the page-builder guide teaches the field form and its extra keys are dropped #11550, not as a condition on this head.
  • "Stopped at the claim boundary" — right: the claim's surface for PR B is the one file, and the diff is that one file.
  • Worktree cut from d0c0c7fe9 — equals the PR's base and the merge-base with current main; the file has not moved on main since. No drift.
  • check:skill-examples needed a built dist — moot on the head: Skill Example Check is success.
  • Attribution — the head commit's trailer is the model-free pair (Claude-Session plus Co-authored-by: Claude); the PR body carries the session-URL footer and no model identifier. Conforms to both repos' rule.
  • Throwaway probe / write budget / drawerFirstLoadWindow-10190 — PR A matters; nothing of them is in this diff.
  • Governance tier: skills/** is Tier H in the register (scripts/pm/check-governed-merges.mjs: "Published skills/** stays Tier H"), and objectui's AGENTS.md says the same. So this PASS is the at-tier review record only: landing waits for an APPROVED review by an account in GOVERNED_APPROVERS, then the owning seat lands it. ⛔ No ready flip, no queue, no auto-merge before that word; and the needs:contract-review label still on the PR is what the merge_group leg refuses on (exit 6), so the owning seat removes it after adopting this record — a seat act, not this review's. Head repo equals base repo (not a fork); 15 changed lines, far under the 5,000-line terminal.
  • Check-runs on the head: 39; 36 success, 3 skipped (Test (coverage), the coverage-shard matrix, dependabot — conditional jobs), none failed, none in progress. Governed Surface Queue Guard success on the pull_request leg is the advisory exit 0 that leg gives a governed draft, not a reading that the PR is ungoverned.
  • The 维护者速读(草稿) section is present with 席位意见 blank, as the claim required; its claims check against main (fields?: string[]; the stored { name } read exists at main and its pin rides PR A, which the section names).

Implemented-by: claude/issue-11550-guide-fields-names-only
Reviewed-by: session_01FjqrwXPfSMkSfkKYDSRkN2

VERDICT: PASS

Record written 2026-10-03T15:41Z; at-tier review rendered by a subagent of the seat session named above, which adopts the verdict.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)

domain:ui seat 1 · session_01FjqrwXPfSMkSfkKYDSRkN2 · 2026-10-03T15:45Z · 所审 head 6ce28b777。本 PR 改的是受管面 skills/**,需要 os-zhuang 或 hotlong 的一条 APPROVED review 才能落地。

改了什么:对外发布的技能指南 skills/objectui/guides/page-builder.md 里,object-form 示例的 fields 由「带 name、label、type、required 的对象」改为字段名字符串 ["name", "email"]。示例上方那段说明同步改写:标签、类型、必填都取自对象字段;某个表单要单独覆盖时,写在 sections[].fields 的条目上(如 { "field": "email", "required": true })。只动这一个文件,净减 3 行,SKILL.md 未动。

为什么改:AI 和开发者照这份指南写表单。旧示例教的写法,表单只读 name,另外三个键被静默丢掉,作者以为设了必填,实际不生效。本仓声明的类型本来就是字符串数组,分诊裁决(卡上评论 5969880008 第 1 条)要求退役这种写法、指南改教字段名。

风险与代价(含回滚):风险低。只改文档文本,运行时零变化;按旧写法已存的元数据照样能显示,代码侧的读取保留,由 objectui#11560 的测试钉住,该 PR 已在合并队列。回滚即 revert 这一个提交。

席位意见:建议批准。

  • 席内复核与高档位契约复核均通过:PASS 记录 5970686472,同一 head。改写后的每一句都对照 objectui main 与 @objectstack/spec 17.6.0 核过,包括字段名示例、「标签/类型/必填取自对象字段」以及 sections[].fields 覆盖写法。
  • 发布技能包净减 3 行;不需要 changeset;CI 36 通过、3 个条件跳过、0 失败。
  • 已知遗留,不影响本 PR 的正确性:plugin-form 注册表的四条 fields 描述和一条控制台警告里仍写着「{ name } is tolerated」。按本卡的决定,等 objectui#11557 合并后由同一张卡的补丁轮改掉。

你要做的:在本 PR 留一条 APPROVED review(os-zhuang 或 hotlong 任一)。批准后由认领席翻 ready、挂 auto-merge,经合并队列落地;在此之前席位不翻 ready、不入队。


Generated by Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants