Uh oh!
There was an error while loading. Please reload this page.
docs(layout): name page:header as the canonical author key on the page-header docs (Phase 1) - #5922
Merged
Merged
Conversation
…e-header docs The layout `page-header` docs page taught the legacy alias as the author face: its only live demo writes `"type": "page-header"`, and nothing on the page said that the contract's key is `page:header`. The layout guide's PageHeader section did the same in two snippets. Phase 1 of the 2026-08-09 maintainer ruling (direction (b), phased): a banner that tells the truth about the key's status now, so an AI author copying from the page copies the canonical spelling, ahead of the full-page rewrite that is serialized behind objectui#3789. Measured against the installed @objectstack/spec, with controls: - ComponentPropsMap has `page:header` (row -> PageHeaderProps), and neither `page-header` nor `layout:page-header`; control `page:tabs` = present. - PageComponentType includes `page:header`, not `page-header`; control `page:footer` = present. - PageHeaderProps.safeParse rejects `icon` by name (retiredKey) and `description` as an unrecognized key of "this `page:header`", with a nonsense key as the negative control. - The alias is STILL ACCEPTED, not tombstoned and not dropped: registerLayout() registers `page-header` (namespace `layout`, no skipFallback, so the bare key too), and it is what the page's demo renders. No schema, registry or renderer change; the demo and the page's measured Styling/Props stay on the alias's renderer, which is Phase 2's job. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuPCi56cnGyykygi3z9w4m
os-zhuang
marked this pull request as ready for review
August 24, 2026 01:57
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #3906 — Phase 1 of the 2026-08-09 maintainer ruling (direction (b), phased). The reference is deliberately non-closing: the ruling keeps this card open for Phase 2 (the full-page rewrite to the canonical renderer, serialized behind #3789) and says the card is re-marked
pm:blocked/Blocked-by: #3789once Phase 1 lands. A closing keyword here would silently retire the card Phase 2 needs. My dispatch word asked for the closing spelling instead; the divergence is flagged to the PM seat in the report.What was wrong
The layout
page-headerdocs page taught the legacy alias as the author face. Its only live demo —examples/schema-catalog/src/schemas/layout-page-header/pageheader-with-actions.json— opens with"type": "page-header", and no line on the page said what the contract's key is.content/docs/guide/layout.mddid the same in its PageHeader section (ajsonUsage snippet and atypescriptSchema API block, both keyedpage-header).Premise re-verified on today's
origin/main(0fce2ef81) before writing: still true, nothing had corrected it.Measurement 1 — the canonical key, read from the SCHEMA
Read from the installed
@objectstack/spec(not from another doc page), each line with a control:Third, independent statement:
PageHeaderPropsis declaredstrictObject({ surface: 'thispage:header', ... })(src/ui/component.zod.ts:393), so the shape names itself — and its rejection messages quote that name back, as shown below.ComponentPropsMap['page:header'] = PageHeaderPropsis atsrc/ui/component.zod.ts:2428;'page:header'sits inPageComponentTypeatsrc/ui/page.zod.ts:56.Measurement 2 — what the alias actually does today
State: still accepted. Not tombstoned, not silently dropped.
packages/layout/src/index.ts:121—ComponentRegistry.register('page-header', PageHeader, { namespace: 'layout', ... }), with noskipFallback, andRegistry.ts:219registers the bare key alongsidelayout:page-headerin exactly that case. So apage-headernode renders today, and it is what this page's demo renders.page:headercomes fromComponentRegistry.register('header', PageHeaderRenderer, { namespace: 'page', skipFallback: true })inpackages/components/src/renderers/layout/containers.tsx:1744— the record-aware one.typethroughComponentPropsMap;page-headerhas no row, andPageComponentSchema.propertiesis an openz.record(z.string(), z.unknown())bag (src/ui/page.zod.ts). So a misspelled or retired key written under the alias is neither rejected nor reported.Note on method:
BaseSchemabeing.passthrough()makes "the alias survivessafeParse" a phantom assertion — true of any key — so the readings above are shape membership plus the parser's own named verdicts:The nonsense key is the negative control that makes the first three readings mean something.
Docs-surface sweep, with a control
Scope searched: the whole repo minus
node_modules/.git/dist, for the alias written as an author key —Control (same command shape, a string I had read with my own eyes in
pageheader-with-actions.json):"type": "button"→ 181 hits repo-wide, including the two in that file. The search is not broken.Hits, and the disposition of each:
content/docs/layout/page-header.mdx(the page itself; alias taught via the demo)content/docs/guide/layout.md:235(jsonUsage) and:251(typescriptSchema API)### Usageexamples/schema-catalog/src/schemas/layout-page-header/pageheader-with-actions.json:2examples/schema-catalog/test/pageheader-with-actions.test.tsx(3 sites)packages/layout/CHANGELOG.md:108content/docs/guide/slotted-pages.mdalready teachespage:headercorrectly and is left alone.Before / after for every instance changed
1.
content/docs/layout/page-header.mdx— before: the page opened straight intoThe PageHeader component provides a consistent header…, with the first mention ofpage:header39 lines down and framed as a prop discussion. After: a banner immediately under the frontmatter that names the canonical key, states the alias's measured state in the three-way vocabulary (accepted / tombstoned / dropped → accepted), says what the alias costs an author (no props dispatch), and points at Slotted pages for the canonical node. No existing alias mention was deleted — the alias still renders, and authors with stored documents need to know where they stand.2.
content/docs/guide/layout.md— before:## PageHeader Component→ intro →### Usagewith"type": "page-header", no statement of the canonical key anywhere in the section. After: the same truth in one blockquote between the intro and### Usage, so it sits above the snippet a reader copies. The snippets themselves are unchanged — they document the layout component, whose props (icon,showBack,children) are not the canonical node's.Neither edit touches the schema, the registry or a renderer: the page was what was wrong.
Verification
Gate union re-run after the final commit, at
1086a1215. Exit codes captured by redirect-then-capture (never across a pipe); verdict lines quoted as each gate printed them.check-doc-links.mjscheck-doc-component-types.mjscheck-changeset-presence.mjscheck-changeset-no-major.mjscheck-control-bytes.mjscheck-skills-paths.mjsNo ablation. This is a docs-only diff — there is no guard to delete and re-measure. Saying so plainly rather than staging a theatrical one. The substitute evidence is Measurements 1 and 2 above, each with its control.
check-doc-snippet-types.mjs— NOT RUN, and that is a precondition, not a verdict. It exited 1 with:naming 15 unbuilt packages (
packages/types,packages/core,packages/components, …). That state is identical with and without this diff — a markdown edit cannot build or unbuild a package. Reported as not-run rather than claimed green. What makes it a safe not-run for this diff specifically:content/docs/layout/page-header.mdxis covered by the gate (not inUNGATED_DOCS), but it contains 0ts/tsxfences — control:content/docs/guide/slotted-pages.mdhas 4 andcontent/docs/guide/architecture.mdhas 9, counted by the same command. The gate compiles nothing from it either way.content/docs/guide/layout.mdis in the gate'sUNGATED_DOCSdebt list, so its green would have said nothing about that page regardless.Lint narrowing, declared with its three pieces of evidence. The repo-wide
pnpm lint(turbo run lint) was not run; the narrowing is a measurement, not a skip:npx eslint --no-inline-config --format jsonon both changed files reports, for each,File ignored because no matching configuration was supplied.--format json: 2 results,errorCount: 0, and the only warning on each is that ignore notice — i.e. 0 linted files in this diff..md/.mdxfiles (git diff --name-only), no source, no config, no tsconfig. eslint applies no configuration to those extensions, so no rule's verdict on any untouched file can move.Control bytes, self-scanned beyond the gate: a control-character grep over both changed files returns no match (exit 1). Positive control: the same command over a file holding one
\x01byte matches (exit 0).Not touched, deliberately
⛔ No schema, registry or renderer change. ⛔ Nothing under
packages/. ⛔ The demo JSON stays on the alias — re-keying it is Phase 2 work and would invalidate the page's measured Styling section, which was taken against this renderer. ⛔ Not marked ready, no auto-merge.Generated by Claude Code