fix(spec): PageHeaderProps.title is optional — matches the platform's own synthesized header - #7756
Conversation
…d header (#7702) PageHeaderProps.title was required, but the platform's own synthesizer (objectui buildDefaultHeader) emits every seeded page:header with no title at all; the renderer falls through to the record-derived heading. PageHeaderProps.safeParse rejected the platform's own canonical output. Maintainer ruling 2026-08-11 (A/B accepted, sentinel option C rejected): title becomes optional, and its describe states the sanctioned spelling — title omitted => the renderer derives the heading from the record. - title: I18nLabelSchema.optional(), describe updated - pins: parses without title (synthesizer's real emission shape), a present title still validates as I18nLabelSchema, describe carries the sanctioned sentence - packages/lint: validateComponentProps no longer flags the synthesized page:header as component-props-invalid - regenerated content/docs/references/ui/component.mdx (check:generated) - changeset: @objectstack/spec minor (accepted-input surface widens) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JY2Q5Xto1u8YHADgrZDTnk
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 7 release-owned page(s) also reference the affected code. These are read-only:
|
…eheader-title-optional
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 31503813959 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 31504932974 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Fixes #7702
Problem
PageHeaderProps.title(packages/spec/src/ui/component.zod.ts) was declared required, but the platform's own synthesizer — objectui'sbuildDefaultHeader— emits every seededpage:headerwith notitleat all:{ type: 'page:header', recordChrome, ...(actions?) }.PageHeaderRenderer(containers.tsx:1013) readsschema?.title ?? schema?.properties?.titleand, finding neither, falls through to the record chip's own record-derived heading — a static authored title would be wrong on every record but one.Consequence:
PageHeaderProps.safeParse(node.properties)rejected the platform's own canonical output withtitle: Invalid input. Invisible on the write path today (PageComponent.propertiesis an opaquez.record, so the strict D3a validator accepts the node regardless), but a standing contradiction that surfaces the moment any props-level validation runs against a header node —validateComponentProps(#5068) is exactly that consumer, and it flagged the synthesized header before this fix.Ruling
Maintainer ruling recorded 2026-08-11 (「接受你的建议,开始加速处理」, accepting the spec lane's A/B recommendation on #7702; option C — a sentinel value — rejected):
Change
PageHeaderProps.title:I18nLabelSchema.describe(...)→I18nLabelSchema.optional().describe(...), with a docstring recording the ruling and a describe stating the sanctioned spelling (title omitted ⇒ renderer derives the heading from the record).packages/spec/src/ui/component.test.ts: replaced the old "should reject header without title" pin (which asserted the now-overturned behavior) with three pins — (a) parses withouttitleusing the synthesizer's real emission shape ({ recordChrome: true }, not a minimal stub), (b) parses with a fully empty object, (c) a presenttitlestill validates asI18nLabelSchema(rejects non-string/non-map, accepts a string).packages/lint/src/validate-component-props.test.ts: added a positive pin against the actual named consumer —validateComponentPropsno longer reportscomponent-props-invalidfor the synthesized title-less header.content/docs/references/ui/component.mdx: regenerated (gen:docs) — the reference table now showstitleas optional with the sanctioned-spelling description..changeset/pageheader-title-optional.md:@objectstack/specminor — this widens the accepted-input surface (every payload that validated before still validates identically; the only newly-accepted shape istitleomitted).Reverse verification
Isolated the fix (
git checkout origin/main -- packages/spec/src/ui/component.zod.ts, keeping the new tests) and re-ran both suites:packages/spec/src/ui/component.test.ts→ 2 failed on the new pins, with exactly the issue's error:title: Invalid input ... expected string, received undefined.packages/lint/src/validate-component-props.test.ts→ 1 failed:component-props-invalidonproperties.titlefor the synthesized header, confirmingvalidateComponentPropswas the consumer surfacing the contradiction, as the issue described.Restored the fix from a patch file (
git apply --include=...) — never a second hand-revert — and re-ran; both suites green again (162/162 and 27/27 respectively).Tests
pnpm --filter '@objectstack/spec' exec vitest run src/ui/component.test.ts src/ui/page.test.ts --maxWorkers=2→ 237 passedpnpm --filter '@objectstack/spec' test -- --maxWorkers=2(full package suite) → 377 files / 9885 tests passedpnpm --filter '@objectstack/lint' test -- --maxWorkers=2→ 70 files / 1905 passed, 4 skipped (pre-existing)pnpm --filter '@objectstack/spec' typecheck→ cleanpnpm --filter '@objectstack/lint' typecheck→ cleanpnpm --filter '@objectstack/spec' check:authorable-surface→ green (base-anchor lag is informational, per AGENTS.md guidance — not an error)pnpm --filter '@objectstack/spec' check:generated→ 1 stale artifact found (content/docs/references/**), regenerated withgen:docs, re-ran → all 13 greennode scripts/check-adr-anchors.mjs --self-test && node scripts/check-adr-anchors.mjs→ OKnode scripts/check-spec-parsed-alias.mjs --self-test && node scripts/check-spec-parsed-alias.mjs→ OKnode scripts/git-merge-regen.mjs --self-test && node scripts/check-regen-pending.mjs --self-test→ OKnode scripts/check-nul-bytes.mjs→ OKScope note
No downstream in-repo consumer imports
PageHeaderPropsdirectly outsidepackages/specandpackages/lint(checked via repo-wide grep) — this is a pure widening (required → optional) so nothing that validated before this change can start failing after it.Generated by Claude Code