Skip to content

fix(spec): a page label does not fill the untitled header of a record page - #22598

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-22536-record-page-header-title
Oct 10, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-22536-record-page-header-title

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #22536
Clause-②: no

On a record page, a page:header with no title is the sanctioned spelling for "the renderer derives the heading from the record" (PageHeaderProps.title, the maintainer ruling recorded in component.zod.ts). Studio's record-page seed writes exactly that header. translatePage filled the absent title with the pack's pages.NAME.label, so the record's name was replaced by the page's static label. This happened in every locale whose pack carries the label, including the en skeleton that os i18n extract writes. Nothing warned.

The rule (triage ruling A, comment 6092658777)

resolvePageHeaderTitle (packages/spec/src/system/i18n-resolver.ts) no longer lets the bundle label fill an untitled root page:header when all three of these hold (headerDerivesFromRecord):

  • the page's type === 'record', read as served: a document with no type is not a record page;
  • the page's object is a non-empty string;
  • the header's properties.recordChrome !== false.

Everywhere else the label still fills an absent title, as before. That includes the existing #22508 control fixture, a page with no type and no object, whose pin name now says "non-record page". An authored title, including one that restates the page label, follows the existing order unchanged. The docblock's order list carries the new condition in its step 3.

Where the predicate comes from. objectui is read at the .objectui-sha pin 47b1f0bb71:

  • PageHeaderRenderer draws the record-derived heading only when a record context is mounted and the header keeps its record chrome (containers.tsx:2331-:2332, :1573-:1574).
  • Two hosts mount that context: the record route, for the page usePageAssignment picks (usePageAssignment.ts:139-:140: type === 'record' and object naming the route's object), and the Studio preview (PagePreview.tsx:186-:187, :311).
  • The one host this rule cannot see is a type: 'modal' action that renders its target page inside the invoking record's context (useActionModal.tsx, RecordDetailView.tsx:3064). Such a page keeps today's fill. Triage accepted this gap, and its in-repo population is 0.

Docs: content/docs/ui/translations.mdx states the exception beside the label-fallback sentence.

Measured at the door

These are one-shot runs and were never committed. GET /api/v1/meta/:type/:name goes through RestServer, using the meta-plural-i18n.test.ts harness shape, on both the cached and uncached branches. The page is the Studio-seed shape: account_record, type: 'record', object: 'account', and a root page:header with properties {recordChrome: true}.

pack, locale before (main d6c3791) after (cdfc9ef)
zh-CN pack with only pages.account_record.label (客户记录页), zh-CN header gains title: 客户记录页 header {recordChrome: true}, no title
en extract skeleton (label: Account Record), en header gains title: Account Record header {recordChrome: true}, no title
no en pack, en untouched untouched
control: type: 'app' page, untitled header with recordChrome: false, zh-CN gains title: 落地页 gains title: 落地页 (unchanged)

The resolver, its tests, component.zod.ts and page.zod.ts are byte-unchanged between d6c3791 and this branch's merge base.

Pins (packages/spec/src/system/i18n-resolver.test.ts)

Each pin runs at a region root and at a slot root.

  • The Studio-seed record header stays untitled with the zh-CN label-only pack and with the en extract skeleton, and the page's own label still translates.
  • A bundle subtitle still applies beside the untitled header.
  • The pin also goes through translateMetadataDocument, the function the REST page doors call.
  • One control per condition, where the label DOES fill: type absent, object absent or '', and recordChrome: false. A further control covers an object-bound page of another type (list, app, home, utility).
  • An authored title on a record page follows the existing order: a template is kept, a title that restates the label takes the label translation, and a pack title replaces it.
  • The existing #22508 control is kept as written: a header with no title on a non-record page shows the bundle label.

Ablation. This ran at cdfc9ef with the fix committed, using node scripts/ablation-replace.mjs. It turned if (own === undefined) return derivesFromRecord ? undefined : bundleLabel; into if (own === undefined) return bundleLabel;. The anchor went from 1 to 0 hits, and the blob moved from c0682db53c44 to 69817db5e283. Result: Tests 9 failed | 349 passed (358). The 9 failures are exactly the record-page pins (4 per root plus the translateMetadataDocument pin), and every control stayed green. The file was restored with blob c0682db53c44 equal to HEAD, and git diff HEAD is empty.

Verification

All runs went through scripts/pm/os-verify-lock.sh with slot issue-22536. Each result is the VERDICT command-exit line of its run.

  • @objectstack/spec:
    • i18n-resolver.test.ts plus translation.test.ts at d841d26 (after the main merge): 505 passed.
    • pnpm test at cdfc9ef: 638 files, 19037 passed, 1 todo.
    • pnpm typecheck at cdfc9ef: green. The test-typecheck ledger held at 52 files, 246 errors and 135 signatures.
    • build && check:generated at d841d26: all 15 generated artifacts up to date.
  • The only runtime caller of the translators is @objectstack/rest (meta-item-read-gate.ts), via translateMetadataDocument. Its consumer runs, all at cdfc9ef:
    • pnpm test: 269 files, 4979 passed, 326 skipped.
    • pnpm typecheck: green, with 0 test-layer errors.
    • pnpm test:repo: 191 passed, 1 skipped.
  • @objectstack/cli unit tier, test/platform-page-i18n-parity.test.ts and test/i18n-extract.test.ts: 51 passed. These call translatePage on the shipped platform pages.
  • Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at d841d26 derived 107 commands. 106 were run and all exited 0. check:skill-examples first exited 3 (PREREQUISITE NOT MET: client-react was unbuilt) and exited 0 after turbo run build --filter='@objectstack/client-react...'. pnpm check:dual-build-cjs-loads: NOT MEASURED, because the dispatch rules out a whole-workspace build. --ran reconciliation: 107 derived, 106 run, 1 unrun (that one). Artifact rosters block: 48 of 51 run, all exit 0. The 3 PR-context guards (check-closing-target-claim, check-partof-closing-keyword, check-single-claim-paths) are run against this PR after it opens, and their results are reported on the card.

Acceptance notes

  • Seam: PageSchema's parse default against type read as served. This is reported, not fixed here. PageSchema defaults an absent type to 'record' (page.zod.ts:1177).
    • Stored path: closed. A page saved without type is stored with type: 'record', and a stored row without it is served with it (withDeclaredPageTypeDefault). Its 9 existing pins pass here.
    • Registry path: open. Measured through the door, a page registered in the SchemaRegistry without type and without a parse is served with no type. In zh-CN its untitled header gains the label (title: 打包无类型页). The same page as a stored row is served type: "record", and its header stays untitled.
    • No real producer was found that registers a typeless page without parsing. defineStack applies the default, and the plugin pages in this tree declare type. Reach is therefore not established.
  • recordChrome spelling. Only properties.recordChrome is read. Measured: PageSchema refuses a bare top-level recordChrome on a component (unrecognized_keys at regions.0.components.0), so a valid served document never carries it. Reading that spelling would be consumer tolerance for a key the contract refuses.
  • Unchanged on purpose. A pack's pages.NAME.title and subtitle still apply to an untitled record-page header (step 1). Triage ruled on the label alone.
  • Population. showcase_task_detail is the one in-repo record page with an untitled root header. No in-repo pack carries its label today, so nothing in-repo renders differently. The plugin-approvals record page that landed on main during this work is kind: 'slotted' with no header slot, so the server sees no root header on it.

Generated by Claude Code

… page

On a `type: 'record'` page bound to an `object`, a `page:header` that
authors no title and keeps its record chrome derives its heading from
the record. `translatePage` no longer fills that absent title with the
pack's `pages.NAME.label`; everywhere else the label still fills, and an
authored title follows the existing order.

Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 3 documentable anchor(s).

⛔ 1 release-owned page(s) name something this change touched. These are read-only:

  • content/docs/releases/v17/17-6.mdx (via translatePage (symbol, a top-level function))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 139 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 86f53a4b8d818706b367b3b89de0e33d92395f34 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 9e413215bac8a5abf058e6791cfa51cc8e821803 — the merge of head d841d2686fd54047b5ce72724c97726a029f7c9a into base 86f53a4b8d818706b367b3b89de0e33d92395f34, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 9e413215bac8a5abf058e6791cfa51cc8e821803 && git checkout 9e413215bac8a5abf058e6791cfa51cc8e821803
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 86f53a4b8d818706b367b3b89de0e33d92395f34 d841d2686fd54047b5ce72724c97726a029f7c9a && git checkout -B drift-repro 86f53a4b8d818706b367b3b89de0e33d92395f34 && git merge --no-ff d841d2686fd54047b5ce72724c97726a029f7c9a

node scripts/docs-audit/affected-docs.mjs --json 86f53a4b8d818706b367b3b89de0e33d92395f34

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 86f53a4b8d818706b367b3b89de0e33d92395f34 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: d841d2686fd54047b5ce72724c97726a029f7c9a
Local-runs: none

Read-only review of PR #22598 for card #22536, at the branch head above (the head the brief named; the branch has not moved). Inputs: the card body and all nine comments (triage 6089133193, the unlock, the deferral, the claim 6092445731, both dev reports 6092591933 and 6093422367, the retriage ask 6092610387 and triage's answer 6092658777, the seat's ACCEPT 6093434692), the PR body and file list, the net diff of the head against its merge-base with origin/main (99801d831f; two commits on the branch, the fix cdfc9efa7a and one merge of main), the check-runs on the head, and objectui at the .objectui-sha pin 47b1f0bb71, read only. The diff is 4 files, +214 / -5: packages/spec/src/system/i18n-resolver.ts, its test, content/docs/ui/translations.mdx, and .changeset/22536-record-page-header-title.md. No governed path.

① Derived judgments

  1. The one arm that moved in resolvePageHeaderTitle, and nothing else — RIGHT. Before: if (own === undefined || sameAuthoredText(own, authoredPageLabel)) return bundleLabel; return undefined;. After: if (own === undefined) return derivesFromRecord ? undefined : bundleLabel; if (sameAuthoredText(own, authoredPageLabel)) return bundleLabel; return undefined;. The four cells of the authored-title axis: no authored title on a non-record header still takes the bundle label; no authored title on a record-deriving header now stays absent (the only cell that changed); an authored title restating the page label still takes the label's translation, the new flag not consulted; an authored title saying anything else is still kept. Step 1 (a bundle pages.NAME.title wins) and the early return when the bundle carries no label are byte-unchanged. authoredLabelText is untouched, so title: '' and an all-empty locale map still count as untitled, the same as before.

  2. headerDerivesFromRecord is exactly triage's predicate A (6092658777) — RIGHT. page.type === 'record' read literally off the served document (no PageSchema default applied), typeof page.object === 'string' with a non-zero page.object.length, and header.properties?.recordChrome !== false. Checked against the pin: containers.tsx:2331–:2332 draws the record heading only when ctx.data && ctx.objectSchema and !disableRecordChrome; usePageAssignment.ts:139–:140 rejects p.type !== 'record' and p.object !== objectName; PagePreview.tsx:186–:187 keys isRecordPage on type === 'record' and recordObject on object, and :311 mounts the context only when recordObject is truthy (so object: '' is unmounted there too, matching the non-empty-string conjunct). D (any object-bound page) is not taken, as triage amended. A page:header with no properties bag at all on a record page reads as deriving (undefined !== false), the same as the renderer's schema?.properties?.recordChrome === false being false.

  3. Only properties.recordChrome is read — RIGHT. The renderer reads both schema.recordChrome and schema.properties.recordChrome (containers.tsx:1573–:1574), but PageComponentSchema (page.zod.ts:308) is a strictObject declaring type, id, label, properties and nothing named recordChrome, so a parsed document never carries the bare spelling; reading it would tolerate a refused key. The dev's measured unrecognized_keys matches the schema as it stands.

  4. The translatePage call site writes no phantom key — RIGHT. The header rebuild spreads title only when headerTitle !== undefined and returns next untouched when both title and subtitle resolve to undefined, so a record-page header with a bundle subtitle is served {recordChrome: true, subtitle} with no title key, and the pin that asserts 'title' in properties is false holds on the no-subtitle path. The region root and the slot root reach the one call with nested: false, so both pin sets exercise the same resolution.

  5. Public surface unchanged; accept set unchanged — RIGHT. translatePage and translateMetadataDocument keep their exported signatures (system/index.ts re-exports the module; api-surface/system.json lists translatePage (function) and is not in the diff). resolvePageHeaderTitle and headerDerivesFromRecord are module-private, so the added parameter is internal. PageLike already declares object?: string and an open index signature, and PageComponentLike already declares properties, so no type widened. component.zod.ts and page.zod.ts are untouched: nothing accepts more or less. The dev reports check:generated at 15 of 15 current at this head, and the Spec property liveness check-run on the head is green. What changes is one served value: an untitled record-page header no longer gains properties.title from the pack's label, in every locale whose pack carries it. That is the ruled meaning of an omitted title (PageHeaderProps.title, the PageHeaderProps.title is required, but the platform's own default page:header deliberately has no title — validateComponentProps would flag every synthesized header #7702 ruling in component.zod.ts:590–:604) restored, not a surface change.

  6. The i18n(pages): a locale bundle with a page label but no title silently replaces the authored page:header title, including a dynamic '{name}', with the static label #22508 control is amended as triage directed, not overturned — RIGHT. The fixture page account_detail_page carries no type and no object, so it is a non-record page as served; the pin keeps its assertions and gains only the words "on a non-record page", which is the amendment triage 6089133193 announced. Option C, which would have flipped that control, is not taken.

  7. The docs sentence states A's condition and touches no release page — RIGHT. translations.mdx adds the exception beside the label-fallback sentence in the same callout, as triage 6092658777 asked. The docs-drift bot named content/docs/releases/v17/17-6.mdx as naming translatePage; it is release-owned and the diff correctly leaves it alone.

  8. The pins and the ablation reconcile — RIGHT. Per root: two seeded-header pins (zh-CN label-only pack, en extract skeleton), one recordChrome-at-default pin ({} and {title: ''}), one subtitle-beside-untitled pin, equals 4; times two roots plus the translateMetadataDocument pin equals the 9 the ablation turned red. One control per conjunct (type absent; object absent or ''; recordChrome: false) plus the other-type control (list, app, home, utility), and the three authored-title arms on a record page, stayed green under the ablation, which is what "nothing wider moved" needs.

  9. The modal-host gap is the accepted one — RIGHT. useActionModal.tsx:197–:210 renders any page fetched by name as modal content, and {modalElement} sits at RecordDetailView.tsx:3064 inside the RecordContextProvider opened at :2849, so a non-record page shown there derives its heading from the host record while keeping the label fill. Triage accepted that gap by name; the headerDerivesFromRecord docblock records it.

② Semver level

  • Packages this diff publishes in: @objectstack/spec only (packages/spec/src/system/i18n-resolver.ts; the test file does not ship). content/docs does not publish; the changeset is the entry itself.
  • Changeset: .changeset/22536-record-page-header-title.md declares '@objectstack/spec': patch, carries Clause-②: no with no (widening) / (narrowing) arm, and describes the three-conjunct rule, the before measurement and what stays unchanged. A bug fix in a released package takes patch, never skip-changeset, and no skip-changeset label is on the PR. Level: patch — RIGHT.
  • Clause-②: no — judged, and right. No accept set changes (no zod file in the diff), no export is added, removed or re-signed, no type widens. The one behavioural change narrows a served fill on one class of input to the meaning the spec already ruled for it. The PR body carries the same Clause-②: no on its second line. The Check Changeset check-run on the head is green.

③ Boundary flags

Every dev flag from 6093422367 (deviations, open_questions, out_of_scope_findings) and the PR's Acceptance notes, answered here:

  • open_questions is empty in the final report. The first report's one question (which predicate) was answered by triage 6092658777 as A, with type read as served; the code is that answer. Answered.
  • Deviation: only properties.recordChrome read. Verified against PageComponentSchema (① item 3). Accepted.
  • Finding: PageSchema defaults type to 'record' at parse (page.zod.ts:1177) while an unparsed registry page is served typeless and keeps the label fill. Triage said record it, do not fix it here; the dev measured it, found no real producer (defineStack applies the default; in-tree plugin pages declare type), and the seat kept it in the Acceptance notes without a card for want of reach. The resolver and the hosts agree on a typeless document (usePageAssignment reads type literally too), so spec and renderer do not diverge on it; the seam is the registry read path, outside this card. Disposition stands. Answered, not escalated.
  • Finding: a pack's pages.NAME.title and subtitle still apply to an untitled record-page header. That is step 1 of the order i18n(pages): a locale bundle with a page label but no title silently replaces the authored page:header title, including a dynamic '{name}', with the static label #22508 ruled: a pack title is the translator authoring a title in that locale on purpose, which is not the label-for-title swap the card is about. The ruling covered the label alone. Answered, not escalated.
  • Consumer suites ran at cdfc9efa7a, before the main merge. The merge brought four commits none of which touch the translator files; after it the dev re-ran the resolver and translation tests, the spec build with check:generated, and the gate union. The check-runs on this head are the verdict for everything else; see the CI line below.
  • dual-build-cjs-loads not measured locally, as the dispatch ruled out a whole-workspace build; Build Core on the head is green.
  • Commit trailers use AGENTS.md's model-free pair. Not contract-bearing.
  • Reviewer finding, not blocking: packages/spec/liveness/translation.json, pages entry, still says the label stands in "only where the header authors none or restates the page label". After this PR that clause has the record-page exception. The entry's status, anchors and evidence targets are unchanged and the Spec property liveness check-run is green, so this is ledger prose drift, not a liveness change; the seat can fold the one-clause amendment into the next touch of that ledger. Left with the seat.
  • Governed surfaces: none of the four paths is governed. The review is owed by the landing rule's path limb (packages/spec/src/**, non-test), as the seat's ACCEPT corrected; this record is that review.
  • CI on the head, read at 2026-10-10T03:52Z: 22 success, 5 skipped, 0 failed, 10 still in progress: Test Core 1/6 through 6/6, Temporal Conformance (live PG + MySQL), Type Check · workspace, Type Check · consumer gates, Lint & Repo Gates. Green already: Build Core, Build Docs, Check Changeset, Check PR Size, Type Check · source gates, Type Check · debt ledger, Dogfood Verify CLI, Dogfood Regression Gate 1/3, 2/3 and 3/3, Check Documentation Links, Spec property liveness, Governed Surface Queue Guard, the three PR-context guards, Flag docs affected by code changes, Auto Label, filter. Nothing has failed. This verdict is on the diff and the completed checks; the landing still waits for every required context to finish green, and a red among the ten would stand over this record. GitHub reports the PR mergeable: true with mergeable_state: blocked (a draft awaiting checks); no conflict against main is reported.

Implemented-by: claude/issue-22536-record-page-header-title
Reviewed-by: session_01KNKBCRDJCu5tGy3TEbvtrF

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Landing pre-checks at d841d2686f, by the owning seat

domain:spec seat 3 (#18883) · zhuangjianguo · session session_01KNKBCRDJCu5tGy3TEbvtrF · 2026-10-10T04:11Z · holder of claim 6092445731.

  • The review: contract review PASS 6093489616 names this head. It finds exactly one arm of the header-title order moved, and that arm is exactly triage's predicate A. Nothing it records blocks. Its one note (the pages prose in packages/spec/liveness/translation.json still describes the old fallback order) is a one-clause amendment for the next ledger touch; the liveness check is green.
  • CI: 34 success, 5 skipped, with every required context green. check-expected-skips --pr 22598 reads "OK — 5 skipped check-run(s), every one in the roster", and no other conclusion is on the head.
  • Governed: check-governed-merges --pr objectstack-ai/objectstack#22598 reads NOT governed; 219 changed lines.
  • Closing keywords: the body carries Fixes #22536 alone, and no commit message carries one.
  • main drift since the merge base 99801d831f: main moved none of the PR's 4 paths. GitHub reports mergeable: true, mergeable_state: clean.

needs:contract-review comes off; pr_ready and automerge_enable follow.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 10, 2026 04:12
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 10, 2026 04:12
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 10, 2026
Merged via the queue into main with commit d748ae8 Oct 10, 2026
44 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-22536-record-page-header-title branch October 10, 2026 04:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:system size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

i18n(pages): a locale bundle's page label fills an untitled page:header on a record page, replacing the record-derived heading in that locale only

2 participants