Skip to content

docs(layout): add maxVisible and mobileMaxVisible to page-header's PageHeaderProps key list - #6257

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-6083-pageheader-docs-keys
Aug 25, 2026
Merged

docs(layout): add maxVisible and mobileMaxVisible to page-header's PageHeaderProps key list#6257
yinlianghui merged 1 commit into
mainfrom
claude/issue-6083-pageheader-docs-keys

Conversation

@yinlianghui

@yinlianghuiyinlianghui commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Fixes#6083

content/docs/layout/page-header.mdx:66-68 claimed @objectstack/spec/ui's PageHeaderProps "declares title / subtitle / breadcrumb / actions / recordChrome / showStar / showCopyId / aria" — 8 keys, where the shape declares 10 live ones. The two omitted (maxVisible, mobileMaxVisible) are live and authorable, so an author following this page never learns the header's action-overflow budget is authorable at all.

Scope is this one file. The guide's copy of the same list (content/docs/guide/layout.md) is #5923's surface and is untouched here.

Derivation — independently re-derived, not copied

Measured against the installed @objectstack/spec17.2.0, by walking the zod shape's DECLARED keys:

DECLARED KEY COUNT: 11
["title","subtitle","icon","breadcrumb","actions","recordChrome",
"showStar","showCopyId","maxVisible","mobileMaxVisible","aria"]
key optional innerType
title true union
subtitle true union
icon true never <-- ADR-0087 D2 tombstone
breadcrumb true boolean (default)
actions true array
recordChrome true boolean (default)
showStar true boolean (default)
showCopyId true boolean (default)
maxVisible true number
mobileMaxVisible true number
aria true object
TOMBSTONES (optional -> never): ["icon"]
LIVE COUNT: 10

Corroborations from the same run:

  • safeParse of an object carrying all ten live keys → success = true, and all ten come back in the output.
  • The tombstone rejects by name: safeParse({title, icon})success = false, message `page:header` property `icon` was removed in @objectstack/spec 17.0.0 (#6946, ADR-0087 D2) — no renderer ever read it … Delete the key.

Why the method is load-bearing. Deriving from a safeParseoutput would have been wrong: safeParse({title, subtitle}) returns only ["title","subtitle","breadcrumb","recordChrome","showStar","showCopyId"] — an optional key with no default never appears in a parse result. That list would have silently deleted actions and aria from the page and never surfaced the two keys this PR is about. Reproduced here rather than taken on trust.

The repo already encodes this distinction: packages/layout/src/__tests__/page-header-authorable-keys.test.tsx derives from the shape and skips tombstones, for exactly the reason its header records — Object.keys(shape) reports a key whether the spec accepts it or rejects it by name.

Cross-check against PR #6082zero delta

#6082 (content/docs/guide/layout.md, #5923, unmerged at the time of writing) recorded its own derivation. Compared key-by-key against this one: identical — same 11 declared keys in the same order, same icon tombstone (optional -> never), same 10 live, and the same per-key optional/default/inner-type annotations. Two independent derivations against the same installed spec agree; nothing was copied from the unmerged PR.

What the added paragraph asserts, and why it is true

The new text says maxVisible / mobileMaxVisible belong to the contract, not to the component this page documents. Verified in-tree:

  • packages/layout/src/ (the @object-ui/layoutPageHeader this page documents) — zero occurrences of maxVisible or mobileMaxVisible.
  • The canonical page:header renderer reads both: packages/components/src/renderers/layout/containers.tsx:1410 (maxVisible / mobileMaxVisible are overridable on the page:header …) and :1440 (… ?? 1 for the mobile budget), with the designer inputs at :1789 giving defaults 3 and 1 — matching the shape's own describe() text, which the page now quotes verbatim rather than paraphrasing.

That is the same component-vs-contract split this page already draws for icon, and it is deliberately not smoothed into consistency: icon remains simultaneously a spec tombstone and a live @object-ui/layout registration input (packages/layout/src/index.ts declares four: title, subtitle, icon, actions). Different renderers, not a contract defect.

Verification

Anchor uniqueness asserted before writing (showCopyId / aria: exactly 1 occurrence, at line 68). Mutation proven on disk by grepping the injected text and the removed text separately: after the edit, showCopyId / aria → 0 and showCopyId / maxVisible / mobileMaxVisible / aria → 1. git diff HEAD --stat shows this one file, 11 insertions / 2 deletions.

Gate union re-run after the final commit, at 3ffa34b94, tree clean:

gateexitits own verdict line
node scripts/check-changeset-presence.mjs0"No source of a released package changed in this range, so no changeset is owed."
node scripts/check-control-bytes.mjs0"OK (scanned 5161 tracked text file(s); skipped 85 binary)."
node scripts/check-doc-component-types.mjs0"Every documented component type is registered."
node scripts/check-doc-links.mjs0"Links are valid across 15 scan roots."
node scripts/check-doc-fence-languages.mjs0"every TypeScript block in 223 document(s) is fenced ts/tsx/typescript …"
node scripts/check-doc-snippet-types.mjs0"267 of 267 block(s) judged, 0 failed."

Gate set derived by enumerating each workflow's own step list, not from a remembered list. ci.yml and lint.yml both carry ':(exclude,glob)content/**' in their in-job Decide whether this change needs a full run step, so a content/**-only diff skips their heavy work.

No changeset, on the gate's own verdict quoted above.

⚠️ What the green does NOT mean — and what still passes on a revert

check-doc-component-types reads type literals in code blocks; check-doc-snippet-types compiles ts/tsx fences. Neither parses a prose key list in an .mdx. The edited claim is prose, in no fence. So their green means this PR broke nothing — it is not evidence the new list is correct. The zod-shape derivation above is the verification.

Measured rather than asserted. Reverting the fix in place (git checkout HEAD~1 -- content/docs/layout/page-header.mdx, proven on disk: injected text → 0, pre-fix text → 1, 13612 → 12986 bytes) and re-running the gates:

gate, on the REVERTED (defective) fileexit
check-doc-component-types0 — identical verdict line
check-doc-links0 — identical verdict line
check-doc-fence-languages0 — identical verdict line
check-control-bytes0 — identical verdict line
check-changeset-presence0 — identical verdict line
check-doc-snippet-types0 — identical verdict line

Every assertion in this repo still passes on a revert. Six for six, byte-identical verdict lines, including on the wrong 8-key list. That is the finding, not a passing test: nothing mechanical guards this claim in either direction. Predicted before running; observed as predicted.

The restore command was git -C WORKTREE_PATH checkout HEAD -- FILE_PATH — absolute, cwd-independent, so it does not depend on where the shell happens to be — run under trap … EXIT INT TERM, and git diff HEAD --stat confirmed empty afterwards (the trap firing is not the trap restoring).

One declared local narrowing

check-readme-exports is red locally, at exit 1, for a reason unrelated to this diff: 46 self-imports across packages/plugin-ai, packages/plugin-gantt and packages/plugin-map report "its type entry ./dist/index.d.ts is not on disk -- run pnpm build first". Its CI job builds every package first (pnpm exec turbo run build --filter='./packages/*'); I built only the 21 the snippet gate derives, and left the rest alone (a sibling agent is live on packages/plugin-gantt). The gate reads packages/**/README.md and package export surfaces — 0 references to content/ in its output — so it cannot see this diff. Real / wrong-path / fabricated counts are 332 / 0 / 0. CI judges this one.


Generated by Claude Code

…geHeaderProps key list
`content/docs/layout/page-header.mdx` named 8 of the contract's keys. Walking
`PageHeaderProps`' zod shape in the installed @objectstack/spec 17.2.0 gives 11
DECLARED keys — 10 live plus the `icon` ADR-0087 D2 tombstone (`optional -> never`),
which this page already excludes correctly. The two the list omitted, `maxVisible` and
`mobileMaxVisible`, are live and authorable.
Derived from the shape's declared keys, not from a `safeParse` output: an optional key
with no default never appears in a parse result, so a parse of `{title, subtitle}` yields
only 6 keys and would have deleted `actions` and `aria` while never surfacing these two.
The added paragraph states that both keys belong to the canonical `page:header` renderer
in `@object-ui/components` (which reads them) rather than to `@object-ui/layout`'s
`PageHeader` (which does not) — the same component-vs-contract split this page already
draws for `icon`.
@yinlianghui
yinlianghui marked this pull request as ready for review August 25, 2026 05:19
@yinlianghui
yinlianghui added this pull request to the merge queueAug 25, 2026
Merged via the queue into main with commit efa6307Aug 25, 2026
25 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-6083-pageheader-docs-keys branch August 25, 2026 05:42
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(layout): page-header.mdx's PageHeaderProps key list omits two live keys (maxVisible, mobileMaxVisible)

2 participants

@yinlianghui@claude