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.

fencebeforeaddedwhy this value
### Basic Grid Layout (:235)label: Contact Informationname: contact_informationsnake_case of its own label — the convention #13761 used for the sequence examples on this same page
### Custom Span Widths (:263)label: Product Detailsname: product_detailssame rule
### Responsive Breakpoints (:295)no label: — only columns: + fields:name: responsive_gridno 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.

gateresult
node scripts/check-docs-section-name.mjs --self-test✓ 54 cases pass, both arms driven RED on disk
node scripts/check-docs-section-name.mjs0 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-examples18 tagged YAML example(s) … validate against their declared live spec schemas
pnpm --filter @objectstack/spec run check:docsexit 0 (after gen:schema via the spec build)
pnpm --filter @objectstack/spec run check:skill-examples260 prose examples type-check across 3 surface(s)
pnpm check:doc-anchors · check:doc-authoring · check:docs-single-h1 · node scripts/check-doc-frontmatter.mjsexit 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-spellingexit 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-statesexit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions · check:doc-security-postureexit 0 (after building @objectstack/formula / @objectstack/lint)
pnpm check:nul-bytesscanned 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.mjsNOT 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-actionsgithub-actionsBot 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 queueAug 31, 2026
Merged via the queue into main with commit 002ddc5Aug 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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

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

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
docs(objectui): give the 3 singular `section:` examples in layout-dsl.mdx a `name` i18n anchor by os-project-manager · Pull Request #13882 · objectstack-ai/objectstack · GitHub
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.

fencebeforeaddedwhy this value
### Basic Grid Layout (:235)label: Contact Informationname: contact_informationsnake_case of its own label — the convention #13761 used for the sequence examples on this same page
### Custom Span Widths (:263)label: Product Detailsname: product_detailssame rule
### Responsive Breakpoints (:295)no label: — only columns: + fields:name: responsive_gridno 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.

gateresult
node scripts/check-docs-section-name.mjs --self-test✓ 54 cases pass, both arms driven RED on disk
node scripts/check-docs-section-name.mjs0 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-examples18 tagged YAML example(s) … validate against their declared live spec schemas
pnpm --filter @objectstack/spec run check:docsexit 0 (after gen:schema via the spec build)
pnpm --filter @objectstack/spec run check:skill-examples260 prose examples type-check across 3 surface(s)
pnpm check:doc-anchors · check:doc-authoring · check:docs-single-h1 · node scripts/check-doc-frontmatter.mjsexit 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-spellingexit 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-statesexit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions · check:doc-security-postureexit 0 (after building @objectstack/formula / @objectstack/lint)
pnpm check:nul-bytesscanned 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.mjsNOT 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-actionsgithub-actionsBot 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 queueAug 31, 2026
Merged via the queue into main with commit 002ddc5Aug 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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

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

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' docs(objectui): give the 3 singular `section:` examples in layout-dsl.mdx a `name` i18n anchor by os-project-manager · Pull Request #13882 · objectstack-ai/objectstack · GitHub
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.

fencebeforeaddedwhy this value
### Basic Grid Layout (:235)label: Contact Informationname: contact_informationsnake_case of its own label — the convention #13761 used for the sequence examples on this same page
### Custom Span Widths (:263)label: Product Detailsname: product_detailssame rule
### Responsive Breakpoints (:295)no label: — only columns: + fields:name: responsive_gridno 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.

gateresult
node scripts/check-docs-section-name.mjs --self-test✓ 54 cases pass, both arms driven RED on disk
node scripts/check-docs-section-name.mjs0 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-examples18 tagged YAML example(s) … validate against their declared live spec schemas
pnpm --filter @objectstack/spec run check:docsexit 0 (after gen:schema via the spec build)
pnpm --filter @objectstack/spec run check:skill-examples260 prose examples type-check across 3 surface(s)
pnpm check:doc-anchors · check:doc-authoring · check:docs-single-h1 · node scripts/check-doc-frontmatter.mjsexit 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-spellingexit 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-statesexit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions · check:doc-security-postureexit 0 (after building @objectstack/formula / @objectstack/lint)
pnpm check:nul-bytesscanned 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.mjsNOT 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-actionsgithub-actionsBot 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 queueAug 31, 2026
Merged via the queue into main with commit 002ddc5Aug 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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

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

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' docs(objectui): give the 3 singular `section:` examples in layout-dsl.mdx a `name` i18n anchor by os-project-manager · Pull Request #13882 · objectstack-ai/objectstack · GitHub
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.

fencebeforeaddedwhy this value
### Basic Grid Layout (:235)label: Contact Informationname: contact_informationsnake_case of its own label — the convention #13761 used for the sequence examples on this same page
### Custom Span Widths (:263)label: Product Detailsname: product_detailssame rule
### Responsive Breakpoints (:295)no label: — only columns: + fields:name: responsive_gridno 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.

gateresult
node scripts/check-docs-section-name.mjs --self-test✓ 54 cases pass, both arms driven RED on disk
node scripts/check-docs-section-name.mjs0 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-examples18 tagged YAML example(s) … validate against their declared live spec schemas
pnpm --filter @objectstack/spec run check:docsexit 0 (after gen:schema via the spec build)
pnpm --filter @objectstack/spec run check:skill-examples260 prose examples type-check across 3 surface(s)
pnpm check:doc-anchors · check:doc-authoring · check:docs-single-h1 · node scripts/check-doc-frontmatter.mjsexit 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-spellingexit 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-statesexit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions · check:doc-security-postureexit 0 (after building @objectstack/formula / @objectstack/lint)
pnpm check:nul-bytesscanned 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.mjsNOT 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-actionsgithub-actionsBot 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 queueAug 31, 2026
Merged via the queue into main with commit 002ddc5Aug 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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

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

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' docs(objectui): give the 3 singular `section:` examples in layout-dsl.mdx a `name` i18n anchor by os-project-manager · Pull Request #13882 · objectstack-ai/objectstack · GitHub
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.

fencebeforeaddedwhy this value
### Basic Grid Layout (:235)label: Contact Informationname: contact_informationsnake_case of its own label — the convention #13761 used for the sequence examples on this same page
### Custom Span Widths (:263)label: Product Detailsname: product_detailssame rule
### Responsive Breakpoints (:295)no label: — only columns: + fields:name: responsive_gridno 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.

gateresult
node scripts/check-docs-section-name.mjs --self-test✓ 54 cases pass, both arms driven RED on disk
node scripts/check-docs-section-name.mjs0 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-examples18 tagged YAML example(s) … validate against their declared live spec schemas
pnpm --filter @objectstack/spec run check:docsexit 0 (after gen:schema via the spec build)
pnpm --filter @objectstack/spec run check:skill-examples260 prose examples type-check across 3 surface(s)
pnpm check:doc-anchors · check:doc-authoring · check:docs-single-h1 · node scripts/check-doc-frontmatter.mjsexit 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-spellingexit 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-statesexit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions · check:doc-security-postureexit 0 (after building @objectstack/formula / @objectstack/lint)
pnpm check:nul-bytesscanned 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.mjsNOT 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-actionsgithub-actionsBot 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 queueAug 31, 2026
Merged via the queue into main with commit 002ddc5Aug 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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

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

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' docs(objectui): give the 3 singular `section:` examples in layout-dsl.mdx a `name` i18n anchor by os-project-manager · Pull Request #13882 · objectstack-ai/objectstack · GitHub
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.

fencebeforeaddedwhy this value
### Basic Grid Layout (:235)label: Contact Informationname: contact_informationsnake_case of its own label — the convention #13761 used for the sequence examples on this same page
### Custom Span Widths (:263)label: Product Detailsname: product_detailssame rule
### Responsive Breakpoints (:295)no label: — only columns: + fields:name: responsive_gridno 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.

gateresult
node scripts/check-docs-section-name.mjs --self-test✓ 54 cases pass, both arms driven RED on disk
node scripts/check-docs-section-name.mjs0 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-examples18 tagged YAML example(s) … validate against their declared live spec schemas
pnpm --filter @objectstack/spec run check:docsexit 0 (after gen:schema via the spec build)
pnpm --filter @objectstack/spec run check:skill-examples260 prose examples type-check across 3 surface(s)
pnpm check:doc-anchors · check:doc-authoring · check:docs-single-h1 · node scripts/check-doc-frontmatter.mjsexit 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-spellingexit 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-statesexit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions · check:doc-security-postureexit 0 (after building @objectstack/formula / @objectstack/lint)
pnpm check:nul-bytesscanned 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.mjsNOT 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-actionsgithub-actionsBot 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 queueAug 31, 2026
Merged via the queue into main with commit 002ddc5Aug 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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

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

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' docs(objectui): give the 3 singular `section:` examples in layout-dsl.mdx a `name` i18n anchor by os-project-manager · Pull Request #13882 · objectstack-ai/objectstack · GitHub
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.

fencebeforeaddedwhy this value
### Basic Grid Layout (:235)label: Contact Informationname: contact_informationsnake_case of its own label — the convention #13761 used for the sequence examples on this same page
### Custom Span Widths (:263)label: Product Detailsname: product_detailssame rule
### Responsive Breakpoints (:295)no label: — only columns: + fields:name: responsive_gridno 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.

gateresult
node scripts/check-docs-section-name.mjs --self-test✓ 54 cases pass, both arms driven RED on disk
node scripts/check-docs-section-name.mjs0 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-examples18 tagged YAML example(s) … validate against their declared live spec schemas
pnpm --filter @objectstack/spec run check:docsexit 0 (after gen:schema via the spec build)
pnpm --filter @objectstack/spec run check:skill-examples260 prose examples type-check across 3 surface(s)
pnpm check:doc-anchors · check:doc-authoring · check:docs-single-h1 · node scripts/check-doc-frontmatter.mjsexit 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-spellingexit 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-statesexit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions · check:doc-security-postureexit 0 (after building @objectstack/formula / @objectstack/lint)
pnpm check:nul-bytesscanned 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.mjsNOT 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-actionsgithub-actionsBot 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 queueAug 31, 2026
Merged via the queue into main with commit 002ddc5Aug 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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

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

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); docs(objectui): give the 3 singular `section:` examples in layout-dsl.mdx a `name` i18n anchor by os-project-manager · Pull Request #13882 · objectstack-ai/objectstack · GitHub
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.

fencebeforeaddedwhy this value
### Basic Grid Layout (:235)label: Contact Informationname: contact_informationsnake_case of its own label — the convention #13761 used for the sequence examples on this same page
### Custom Span Widths (:263)label: Product Detailsname: product_detailssame rule
### Responsive Breakpoints (:295)no label: — only columns: + fields:name: responsive_gridno 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.

gateresult
node scripts/check-docs-section-name.mjs --self-test✓ 54 cases pass, both arms driven RED on disk
node scripts/check-docs-section-name.mjs0 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-examples18 tagged YAML example(s) … validate against their declared live spec schemas
pnpm --filter @objectstack/spec run check:docsexit 0 (after gen:schema via the spec build)
pnpm --filter @objectstack/spec run check:skill-examples260 prose examples type-check across 3 surface(s)
pnpm check:doc-anchors · check:doc-authoring · check:docs-single-h1 · node scripts/check-doc-frontmatter.mjsexit 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-spellingexit 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-statesexit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions · check:doc-security-postureexit 0 (after building @objectstack/formula / @objectstack/lint)
pnpm check:nul-bytesscanned 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.mjsNOT 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-actionsgithub-actionsBot 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 queueAug 31, 2026
Merged via the queue into main with commit 002ddc5Aug 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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

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

@os-project-manager@claude