Skip to content

docs(objectui): give the 3 singular section: examples in layout-dsl.mdx a name i18n anchor - #13882

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-13759-layout-dsl-singular-section-name
Aug 31, 2026
Merged

docs(objectui): give the 3 singular section: examples in layout-dsl.mdx a name i18n anchor#13882
os-project-manager merged 1 commit into
mainfrom
claude/issue-13759-layout-dsl-singular-section-name

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes #13759

content/docs/protocol/objectui/layout-dsl.mdx teaches form sections twice over: as a
sections: sequence, and as a singular section: mapping — one section on its own.
The sequence examples were given name anchors by the sweep that added the gate's YAML arm
(#13761). The three singular ones were outside that sweep's population and stayed nameless, so
each renders its authored label in every locale, on the page whose job is to teach the opposite.

Three edits in one file, plus a changeset. No schema, no gate, no script moves.

The three edits, decided per site

They are not three copies of one edit — the third carries no label: at all.

fence before added why this value
### Basic Grid Layout (:235) label: Contact Information name: contact_information snake_case of its own label — the convention #13761 used for the sequence examples on this same page
### Custom Span Widths (:263) label: Product Details name: product_details same rule
### Responsive Breakpoints (:295) no label: — only columns: + fields: name: responsive_grid no label to snake_case, so a descriptive name, and no invented label:

The third site is the one worth stating in full. It is deliberately minimal so the breakpoint
discussion is about columns collapsing, and none of the three fences' ASCII "Rendered Grid"
diagrams draw a section header — inventing a label: would have desynchronised the diagram
printed directly below it. The i18n symptom the other two carry does not even arise for a
section with no heading to mis-render; what it gains is the stable identifier itself, which is
what FormSectionSchema.name is for.

name goes above label in all three, matching the sequence examples on this page.

FormSectionSchema finding — name is declared and optional, confirmed by ablation

packages/spec/src/ui/view.zod.ts:2417, FormSectionSchema is a strictObject declaring:

name: z.string().optional().describe('Stable section identifier for i18n lookup (snake_case)'),

Its JSDoc: "Stable identifier for translation lookup. snake_case convention. When provided,
translation bundles can target this section's label and description."
So adding name is
exactly what the convention wants, and it stays .optional() — no schema change here, per
#10709 and reaffirmed by #10830.

These three fences are typechecked: each carries {/* os:check-yaml FormSectionSchema key=section */}, and check:yaml-examples validates them against the live schema. That gate
is green on this branch, and a reverse verification proves the green is a real reading
rather than a vacuous one — misspelling one added key namenmae on the committed tree
drove it RED with the schema's own message:

· at (root): Unrecognized key(s) on this form section: `nmae`. Did you mean `nmae` → `name`?
MUTATED check:yaml-examples exit = 1

The mutation was confirmed on disk before the run (injected spelling count 1, removed spelling
count 0, blob hash moved 777c1364…bac9f27e…), and the restore leg was proven by bytes,
not by an exit code: git checkout HEAD -- returned the file to 777c1364…, identical to its
HEAD blob, with git diff HEAD empty. That the suggester maps nmaename is itself the
proof that name is a declared member of the schema.

Re-run census — the population is 4, not 3, and the 4th is filed rather than fixed

Re-running the #13759 census on 8c6a7fc0b with the same yaml parser and the same
classifyYamlFence the #11887 arm uses, over all of content/docs/**, with no marker filter:

yaml fences scanned : 148   (judged 145, skipped 3 on a syntax error)
singular `section:` mappings : 4
  concept.mdx:426     label="Billing Info"          keys=label|fields
  layout-dsl.mdx:235  label="Contact Information"   keys=label|columns|fields
  layout-dsl.mdx:263  label="Product Details"       keys=label|columns|fields
  layout-dsl.mdx:295  label=null                    keys=columns|fields

The card's "exactly these 3" was scoped by MARKER, not by shape, and the difference is not
drift: - section: is present at concept.mdx:425 in af01080e3, the very commit the card
measured on. Two controls separate the hypotheses — re-running the census with the gate's own
YAML_SECTIONS_KEY pre-filter yields 0 (so the card did not use it), while grep for
os:check-yaml FormSectionSchema key=section across content/docs/** yields exactly the card's
3, all in this file. concept.mdx carries no os:check-yaml markers at all.

The 4th is not fixed here, and that is a judgement rather than an omission. Its - section:
sits under a customizations: sequence of overlay entries that is declared nowhere in
packages/spec (the only customizations in the authorable surface is tenant.zod.ts's
free-form z.record), and the ```json "Final Merged Layout" fence directly below it carries two
more nameless sections that neither arm of the gate judges, because json is in neither
fence-language set. Repairing the YAML half alone would teach an anchor that vanishes from the
merged output two paragraphs later. That whole cluster is the population question this PR is
fenced out of, so it is filed bare and unassigned as #13880 with the measurement, for triage to
route.

⚠️ These three sites are correct now and still UNGUARDED — deliberately

check-docs-section-name judges sections: sequences in both of its arms: the TS arm
bracket-matches sections: [ … ] literals, the YAML arm walks every sections: sequence for
mapping items. A singular section: mapping is outside both, which is exactly how these three
drifted in the first place. This PR does not widen the gate — triage's ruling on #13759
fenced that out, because widening means deciding which YAML keys introduce a form section at
all, and that wants taking together with how the os:check-yaml marker vocabulary is read.

So nothing here stops a fourth nameless singular section: appearing tomorrow. The gate's own
sweep line makes the blind spot visible on this branch — it reports 0 nameless while naming
19 YAML section mapping(s) JUDGED, and none of those 19 are these 3.

Is a follow-up warranted? Yes, in my judgement, and #13880 is it — but as the population
card, not as a "fix 3 more sites" card. The recurring shape across #10830, #11887 and now
#13759 is that each pass closes one selector and the next selector drifts, so the useful
follow-up decides the gate's population once (which keys, and which fence languages) rather than
sweeping a fourth spelling. Routing and priority are triage's call; the card is filed unlabelled
and unassigned.

Changeset

.changeset/docs-layout-dsl-singular-section-name.md, "@objectstack/docs": patch.

Added rather than skipped, and the reasoning either way: apps/docs is still private: true,
so nothing publishes from it — but .changeset/config.json sets privatePackages: { version: true, tag: false }, so private packages are versioned and a changeset here is legal and
meaningful. The skip-changeset route does not apply: its closed list is docs/adr/**,
.claude/**, scripts/pm/**, tests/workflow and comments, and content/docs/** is in none of
them. Precedent on main is the same shape — .changeset/docs-meta-index-detaches-folder-index.md
and .changeset/flow-refusal-enumeration-four-pages.md both take "@objectstack/docs": patch
for a content/docs/** correction.

Verification

All commands below ran on the final commit, fb218e6dd.

The gate family was derived from the real change set, not from a hand-written list:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack → 36 families from the
2 changed paths. 35 green, 1 NOT MEASURED, none red.

gate result
node scripts/check-docs-section-name.mjs --self-test ✓ 54 cases pass, both arms driven RED on disk
node scripts/check-docs-section-name.mjs 0 nameless form-section examples — 405 docs file(s) · 22 section literal(s) JUDGED · 19 YAML section mapping(s) JUDGED
pnpm --filter @objectstack/spec run check:yaml-examples 18 tagged YAML example(s) … validate against their declared live spec schemas
pnpm --filter @objectstack/spec run check:docs exit 0 (after gen:schema via the spec build)
pnpm --filter @objectstack/spec run check:skill-examples 260 prose examples type-check across 3 surface(s)
pnpm check:doc-anchors · check:doc-authoring · check:docs-single-h1 · node scripts/check-doc-frontmatter.mjs exit 0
check:docs-audit-scope · check:docs-redirects · check:corpus-claim-drift · check:role-word · check:react-page-adapter-contract · check:published-readme-links · check:section-landing-index · check:doc-route-spelling exit 0
check:changeset-gate-self-tests · check:objectui-changeset · check:empty-changeset · check:changeset-no-major · check:adr-0087-registration · check:keyed-text-bounds · check:pm-half-states exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions · check:doc-security-posture exit 0 (after building @objectstack/formula / @objectstack/lint)
pnpm check:nul-bytes scanned 7608 text file(s) … no raw ASCII control bytes
pnpm lint (repo-wide eslint . --no-inline-config) exit 0 — full sweep, not narrowed
node scripts/check-test-completeness.mjs NOT MEASURED (exit 3)

That last row is the documented prerequisite branch, not a failure: the gate grades a saved
turbo run test log that only CI produces, and it says so itself — "the local reading for this
gate is NOT MEASURED. ⛔ It is not a red, and there is nothing here to fix."

Four gates first refused with "Nothing was measured" because packages were unbuilt
(check:doc-formula-expressions, check:doc-security-posture, check:docs,
check:skill-examples). They were not recorded as reds — the packages were built
(turbo run build for spec, lint, formula, then client-react, client; both runs
through scripts/pm/os-verify-lock.sh, VERDICT command-exit 0) and all four re-run green.
The builds left the worktree clean: git status --porcelain empty afterwards, so gen:schema
moved no tracked baseline.


Generated by Claude Code

….mdx a `name` anchor

`layout-dsl.mdx` teaches form sections both as a `sections:` sequence and as a
singular `section:` mapping. The sequence examples were given `name` anchors in
the YAML-arm sweep; the three singular ones were outside that population and
stayed nameless, so each renders its authored label in every locale on a page
whose job is to teach the opposite.

The third site is not a copy of the other two -- it carries no `label:` at all,
so it takes a descriptive `name` and no invented label: the "Rendered Grid"
diagram directly below it draws no section header.

`FormSectionSchema.name` stays `.optional()`; no schema moves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Aug 31, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 16:39
@os-project-manager
os-project-manager added this pull request to the merge queue Aug 31, 2026
Merged via the queue into main with commit 002ddc5 Aug 31, 2026
32 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-13759-layout-dsl-singular-section-name branch August 31, 2026 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] 3 singular section: form-section examples in layout-dsl.mdx have no name — outside both arms of check-docs-section-name

2 participants