Skip to content

fix(spec): a locale bundle's page label no longer replaces a page:header title that says something else - #22534

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-22508-page-header-title-fallback
Oct 9, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-22508-page-header-title-fallback

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #22508
Clause-②: no

What changes

translatePage (packages/spec/src/system/i18n-resolver.ts) used to resolve a root-level page:header title as the bundle's pages.NAME.title, else the bundle's pages.NAME.label, and write it over whatever the header authored. A locale pack that translated the page label and left out the title therefore replaced a record page's dynamic '{name}' heading with the page's static name in that locale, with nothing warning at build, lint or serve.

The header title now resolves through ONE function, resolvePageHeaderTitle, called from the one visitor both roots pass through (a region's entry and a slots.SLOT entry both arrive with nested: false), so the two roots cannot disagree:

  1. the bundle's pages.NAME.title, across the locale chain;
  2. the header's AUTHORED title, left as written (a template, a plain string, or an inline locale map);
  3. the bundle's pages.NAME.label, only where the header authors no title, or where its title restates the page's own authored label.

The declared contract is restated with that limit in the same PR: the pages JSDoc and the pages.NAME.title describe in translation.zod.ts (and the regenerated content/docs/references/system/translation.mdx), the translatePage docblock, the hand-written content/docs/ui/translations.mdx callout, and the pages row of packages/spec/liveness/translation.json.

One deviation from the triage text, measured, and why

Triage 6085431021 reads: "The bundle's page label stands in only where the authored header has no title of its own." Step 3 above adds one case to that: a header title that restates the page's own label. The triage expected nothing to rely on the label replacing an authored title. The measurement says something does:

  • os i18n extract (packages/cli/src/utils/i18n-extract.ts, the page-header pass) deliberately offers NO pages.NAME.title key when the header title restates the page label: "title duplicating label is the common case and resolves via the label fallback, only emit it when the two genuinely differ" (sameAuthored). Every skeleton it has written relies on the label reaching such a header.
  • In this repo, three shipped pages are that shape: marketplace_installed, cloud_connection_settings (@objectstack/cloud-connection) and connect_agent (@objectstack/mcp). Each authors a header title equal to its page label, and @objectstack/platform-objects' bundles carry label + subtitle and no title for them in all four locales (en, zh-CN, ja-JP, es-ES).
  • Applied literally, the triage sentence turns those three Setup headers back to English in zh-CN / ja-JP / es-ES. The ablation below measures it: packages/cli/test/platform-page-i18n-parity.test.ts ("rewrites the page:header copy end-to-end for a non-English locale") goes red for all three.

Step 3's second case keeps the triage's own principle ("A bundle supplies a translation of an attribute; it does not swap one attribute for another over authored text"): when the authored title IS the page label's text, the label's translation is a translation of that very text. It also keeps the resolver and the extractor a matched pair: the extractor omits title exactly when the resolver will read the label for it. The comparison (sameAuthoredText) mirrors the extractor's sameAuthored (strings by text, inline maps as sets of non-empty entries). The seat may rule otherwise; the alternative is in the report's open_questions.

What still changes for an author (named in the changeset): a header whose title says something else than the page label, in a locale whose pack carries label but no title, now shows the authored title. In this repo that population is zero (H3 below); the reporting app (hotcrm) already writes title in every pack.

Measurements (dispatch hypotheses)

  • H1, holds. translatePage is reached only through translateMetadataDocument('page', …) (the METADATA_DOCUMENT_TRANSLATORS table). Its only runtime callers are translateMetaList and translateMetaDocument in packages/rest/src/meta-item-read-gate.ts, which serve GET /api/v1/meta/page and GET /api/v1/meta/page/NAME. The console reads those doors: objectui's useObjectLabel resolves only pages.NAME.label client-side (the page's nav label) and nothing reads pages.NAME.title there. One fix covers both reach points the card names.
  • H2, holds. The authored title reaches translatePage as written in properties.title (the REST door serves the stored or packaged document; PageComponentSchema is strict, so properties is the only place a title can be authored). Pinned at both roots: a template ('{name}'), a plain string, an inline locale map, absent ({}, ''), and a title restating the label (string and map forms).
  • H3, does not hold as stated. In-repo bundles with a pages group: @objectstack/platform-objects (4 locales, 6 page entries each) and examples/app-showcase (zh-CN, 4 entries, components only). Pages whose bundle carries label and no title while a root-level header authors a title: 3 (the three Setup pages above), all of them title-restates-label, all kept translated by step 3. Pages of the defect shape (header title differs from the label, pack has label and no title): 0. The sys_*_detail pages carry a label-only entry but no page:header. app-crm, app-todo and app-multi-package carry no page translations.

Clause-② measured: no accept set or public surface widens. No schema, key, accepted value, default or export changes; the helpers are module-private. What changes is what a translated page RENDERS, which is resolver behaviour. The one schema-adjacent change is the pages.NAME.title describe string.

Tests

All readings below are at head 194157f5e1 (the last commit; no commit followed any of them). Wall-clock figures are shared-box seconds.

  • pnpm --filter @objectstack/spec exec vitest run --project local: 631 files, 18842 passed, 1 todo. --project repo: 54 files, 915 passed. src/system/i18n-resolver.test.ts alone: 327 passed (the new #22508 block is 13 cases: six per root plus the REST-door case).
  • pnpm --filter @objectstack/spec typecheck (tsc, scripts typecheck, check:test-typecheck): exit 0.
  • @objectstack/cli unit layer, the two files that feed translatePage and the extractor: test/platform-page-i18n-parity.test.ts, test/i18n-extract.test.ts, 51 passed (CLI resolves @objectstack/spec through dist, built at this head).
  • @objectstack/rest (the one runtime caller): pnpm --filter @objectstack/rest exec vitest run: 273 files, 5166 passed, 327 skipped (rest resolves @objectstack/spec through dist, rebuilt clean after the ablation).
  • Fixture triage (one fixture): regionHeaderWithId() in the batch Query Protocol: Add comprehensive test coverage and documentation for aggregations, joins, and window functions #58 block omitted PageSchema's required label while its header restated homePage()'s label. Disposition: add the declaration (label: 'Sales Home'), so the fixture is the shape it was cut from. No other fixture in the rule's consumer radius changed (platform-objects, cli, lint, rest fixtures read; only the CLI parity test exercises the header title, and it stays green).
  • eslint, narrowed and proven: eslint --no-inline-config --format json on the three changed TS files reports 3 files, 0 errors, 0 warnings. Population: eslint.config.mjs lints **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}, so the .md, .mdx and .json files in this diff are outside it. Invariance: the config never enables type-aware linting (no parserOptions.project, no typed rules, stated in the config itself), so this diff cannot move a verdict on an untouched file.

Ablations (one-shot, both legs restored and proven)

  • Leg 1, the old fallback restored (resolvePageHeaderTitle answering bundle title, else bundle label, whatever the header authors), through scripts/ablation-replace.mjs (anchor hit 1, blob changed): i18n-resolver.test.ts 7 failed / 320 passed. The 7 are exactly the predicted ones: template, plain string and inline map at each root, plus the REST-door case. The controls (pack with title replaces; no authored title shows the label; title restating the label) stayed green. Restored: blob equals HEAD, git diff HEAD empty.
  • Leg 2, the triage sentence applied literally (the restates-the-label disjunct removed), spec rebuilt, ablation-dist-preflight found the marker in dist: spec 6 failed / 321 passed (the two restates-label pins, the original connect_agent pins and the batch Query Protocol: Add comprehensive test coverage and documentation for aggregations, joins, and window functions #58 pins), and platform-page-i18n-parity.test.ts "rewrites the page:header copy end-to-end for a non-English locale" went red at its first page, marketplace_installed (titleChanged: false: the zh-CN header stayed "Installed Apps"). Restore leg: source blob equals HEAD, spec rebuilt, preflight --absent: marker absent from all 232 built files, tree clean.

Gates

node scripts/pm/dispatch-gates.mjs --commands derived 111 families from this diff (the 85 the dispatch named plus 26 the docs and changeset paths add). All 111 ran and recorded an exit code; --ran reconciles 111 derived, 111 run, 0 NOT MEASURED. 110 exit 0. One exits 1: pnpm check:platform-checklist, on docs/qa/platform-checklist/areas/attachments-storage.json, whose anchor attachment-access-hooks.ts#canEdit lost its declaration in ce3d0ad419 (#22513). That is red on main at this branch's base and at origin/main ee8751d41e too; this diff touches neither file. Seven gates first refused with PREREQUISITE NOT MET (exit 3, unbuilt lint / client-react); they were re-run after the build and pass, as were the dist-reading gates after the ablation's restore rebuild.

Acceptance notes

  • Ruled control, recorded rather than changed: a root-level page:header with NO authored title still receives the bundle's page label (the triage's third pin). On a record page that is not neutral: PageHeaderProps.title documents "omit to let the renderer derive the heading from the record", and objectui's PageHeaderRenderer draws properties.title when one is present, so a pack that translates the page label replaces a record-derived heading with the static label, the same failure as this card in another shape. In this repo the population is zero: no root-level page:header without a title sits on a page with a translated label (the sys_*_detail pages carry no header). A Studio-seeded record page stores its synthesized header without a title, so it can be reached outside this repo; measured only by reading, not at a door. Raised as an open question in the report, not changed here.
  • Locale chain, unchanged: step 1 walks the request's whole locale chain (the locale, then its declared fallbackChain) before step 2. So a request whose own pack has only label, while a pack on its fallback chain has title, shows that fallback title over the authored one. That is still the bundle's title, which the triage put first.
  • Pre-existing red on main: pnpm check:platform-checklist (see Gates). Its carrier is whoever re-anchors the attachments-storage checklist item after ce3d0ad419; not this PR's file surface.
  • Generated output: content/docs/references/system/translation.mdx was regenerated by gen:docs from the new describe (check:generated named exactly that one artifact stale). No other generated artifact moved; authorable-surface.base.json untouched.

Generated by Claude Code

claude added 4 commits October 9, 2026 18:40
…ader title that says something else

translatePage resolved the header title as the bundle's page title, else the
bundle's page label, and spread it over the authored title at a region root
and a slot root alike. A pack that translated the label and omitted the title
turned a dynamic '{name}' heading into the page's static name.

The order is now: bundle title, then the authored title, then the bundle
label only where the header authors no title or its title restates the
page's own label (the case the CLI extractor dedupes, offering label alone).

Claude-Session: https://claude.ai/code/session_01VZqqwTj2wsihZEbfT6yyYN
Co-authored-by: Claude <noreply@anthropic.com>
…title restates

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

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 5 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/spec/liveness/translation.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

⛔ 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
  • 1 changed file(s) yielded no anchor (packages/spec/liveness/translation.json) — pages documenting those are invisible to this run
  • 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 faf6348508519197c6047b46fb30b6ae8910f6b2 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from c572bb798073004731b40ea36ee88e3b9ef487f6 — the merge of head 194157f5e16b955ece5f95ad2a518d1d5679139e into base faf6348508519197c6047b46fb30b6ae8910f6b2, 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 c572bb798073004731b40ea36ee88e3b9ef487f6 && git checkout c572bb798073004731b40ea36ee88e3b9ef487f6
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin faf6348508519197c6047b46fb30b6ae8910f6b2 194157f5e16b955ece5f95ad2a518d1d5679139e && git checkout -B drift-repro faf6348508519197c6047b46fb30b6ae8910f6b2 && git merge --no-ff 194157f5e16b955ece5f95ad2a518d1d5679139e

node scripts/docs-audit/affected-docs.mjs --json faf6348508519197c6047b46fb30b6ae8910f6b2

⚠️ 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 faf6348508519197c6047b46fb30b6ae8910f6b2 → 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: 194157f5e16b955ece5f95ad2a518d1d5679139e
Local-runs: none

Read-only, all read at 2026-10-09T21:01Z: card #22508's body and every comment (triage 6085431021, claim 6086910462, dev report 6088963381, seat answers 6089063944), card #22536, PR #22534's body and file list, the net diff from merge-base 9411faa1ba to the head, i18n-extract.ts (sameAuthored and its page-header call) and PageHeaderProps.title on main, and the head's check-runs. Nothing built, run or re-run; the derived gate families are answered by the check-runs below.

① Derived judgments

Accept set: unchanged. Right. translation.zod.ts changes the pages.NAME.title describe string and the pages JSDoc only; the field stays z.string().optional(); no key is added, retired or re-typed; no accepted value or default moves. PageHeaderProps.title (component.zod.ts, I18nLabelSchema.optional()) is untouched. A grep of main for the old phrases ("defaults to label", "title falls back to label", "falls back to pages.NAME.label") hits only the five files this diff rewrites, so no declaration of the old contract survives outside the PR. The extractor's own comments (a title that merely restates the page label resolves through the label's key) stay true under the new rule.

Public surface: unchanged. Right. i18n-resolver.ts adds three module-private functions (authoredLabelText, sameAuthoredText, resolvePageHeaderTitle); none is exported and translatePage's signature is unchanged. Type Check · source gates and Spec property liveness are green on the head.

Served behaviour at GET /api/v1/meta/page/:name and GET /api/v1/meta/page: narrowed to the ruled order. Right. Read at the head: translatePage computes bundleHeaderTitle and bundleLabel page-wide, each through lookupPageAttr (walks the locale chain, skips empty strings; unchanged), and the one visitor passed to walkAddressedPageComponents calls resolvePageHeaderTitle(bundleHeaderTitle, bundleLabel, next.properties?.title, doc.label) for every page:header that arrives with nested: false. Both roots arrive that way: a region's components[] entries and every slots.SLOT entry are each visited through visitComponent(c, 0) and nested is "depth above zero". The spread then writes title only when the resolver answers one, so an undefined answer leaves the authored title alone even while the bundle subtitle is applied beside it. The order is: bundle title; else the authored title as written; else the bundle label only where authoredLabelText(title) is undefined or sameAuthoredText(title, doc.label) holds. That implements triage 6085431021's direction and its three pins, each pinned in i18n-resolver.test.ts at a region root and a slot root alike:

  • a pack with label and no title keeps the authored '{name}', in en and zh-CN (the measured hotcrm shape), for a plain string and for an inline locale map, and once more through translateMetadataDocument('page', ...), the function the REST doors call;
  • a pack with title replaces it;
  • a header with no authored title ({}, { title: '' }, subtitle only) still receives the bundle label.
    Every label fill the new rule performs is one the old rule also performed, so nothing that rendered correctly before renders differently now; only the trap case changes.

The refinement (label also stands in where the header title restates the page's own label): right, and the seat's answer A in 6089063944 is sound. The triage's literal sentence rested on "expected: none" relying on the label over an authored title. The dev measured that reliance: the extractor's page-header pass deliberately offers no pages.NAME.title when sameAuthored(props.title, page.label), three shipped Setup pages (marketplace_installed, cloud_connection_settings, connect_agent) are that shape with label-only packs in zh-CN, ja-JP and es-ES, and ablation leg 2 turned platform-page-i18n-parity.test.ts red. The refinement keeps the triage's own principle: when the authored title IS the label's text, the label's translation translates that very text and no attribute is swapped over different text. It is a strict subset of the old fill, resolver-only, and keeps the extractor/resolver pair on one rule; option B would widen the surface to two more packages and leave every externally extracted pack's header untranslated with no diagnostic. The PR names the reliance, as the triage asked.

sameAuthoredText as a mirror of the extractor's sameAuthored: right on the cases the mirror decides, with two non-blocking observations.

  • String vs string: both compare the non-empty text (a === other; the extractor's inlineText equality). Same.
  • Inline map vs map: both drop empty-string entries, treat an emptied map as no text, and compare sorted keys with entry-equal values, key order ignored. Same. A string against a map answers false in both.
  • Both absent: the resolver reads the label (absent-title arm); the extractor's sameAuthored(undefined, undefined) is true, so it offers no title key. Same outcome.
  • Empty-string title '': the resolver never consults the mirror; authoredLabelText('') is undefined, so the absent-title arm fills the label (pin 3, pinned by { title: '' }). The extractor's sameAuthored('', label) is false and pushEntry accepts any string, so it offers a pages.NAME.title row with an empty seed. A predicate-level difference, not an outcome one: an empty bundle title is skipped by lookupPageAttr, a filled one wins by step 1, an omitted one gets the label; the header is neither left untranslated nor translated over different authored text. Pre-existing extractor behaviour outside this card's file surface. Observation only.
  • The extractor's inlineLocaleMap also parses the map through InlineLocaleMapSchema (BCP-47 keys); the resolver's authoredLabelText does not. A map that fails that parse cannot reach translatePage through a validated document, since PageHeaderProps.title and the page label are both I18nLabel. Observation only.

Docs and ledger: right. content/docs/references/system/translation.mdx is the generated consequence of the describe edit (all three tables regenerated, text identical to the describe); content/docs/ui/translations.mdx restates the order and names both roots; the translatePage docblock restates it; liveness/translation.json's pages row changes evidence and note text only, with status and verifiedAt untouched, and Spec property liveness is green.

Test fixture regionHeaderWithId gains label: 'Sales Home': right. PageSchema requires label, and that fixture's header restates it, which is the shape whose title the page-name route answers from the label.

② Semver level

.changeset/22508-page-header-title-keeps-authored.md declares '@objectstack/spec': patch and carries Clause-②: no; the PR body's second line is Clause-②: no. Both match the diff: no schema, key, accepted value, default or export changes; no arm is claimed and none applies, since nothing an author can write is newly accepted or newly rejected. What changes is served behaviour, a class (c) trap fixed at the default, which is a bug fix in a released package: patch is right, and skip-changeset would have been wrong. The changeset names what changes for an author (a header whose title differs from the label, in a locale whose pack carries label but no title, now shows the authored title; add pages.NAME.title to translate it), and the in-repo population of that shape is zero. Check Changeset is green on the head.

③ Boundary flags

  • Dev flag: deviation from the triage's literal sentence (deviations[0], open_questions[0]). Answered: the seat ruled A in 6089063944; judged sound in ①. The triage seat's objection window noted there is its own; nothing is owed by this record.
  • Dev flag: open question 2, an absent title on a record page. Answered by escalation: the seat filed 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 #22536 (finding, class (c), producer objectui buildDefaultHeader); its body names the ruled control, carries the dev's A/B/C and orders the work after this PR lands. Leaving the ruled control as is in this PR is correct: triage ruled that pin on this card, this diff does not change that behaviour (the old rule filled an absent title too), and overturning a triage pin is triage's call, not a dev's and not this review's.
  • Dev flag: file surface (deviations[1]). The generated reference page and the liveness row are the declared-contract text the surface names, and translation.zod.ts is where the describe lives. Accepted; no other package is edited.
  • Dev flag: fixture edit (deviations[2]). Judged right in ①.
  • Dev flag: container hygiene (deviations[3], two stray files at the container root). Outside the diff and outside any repo; nothing to escalate.
  • Out-of-scope finding: check:platform-checklist red on main. Verified pre-existing and outside this diff: ce3d0ad419 (PR fix(service-storage,plugin-audit,plugin-security)!: the attachment and comment parent gates judge a controlled_by_parent parent through its master #22513) removed the canEdit(...) member from service-storage/src/attachment-access-hooks.ts, the declaration the attachments-storage checklist item anchors; this diff touches neither that file nor docs/qa/platform-checklist/**. The gate is kept out of per-PR CI by maintainer decision (the note in lint.yml, checklist-status.yml), its standing caller is platform-checklist-watchdog.yml on main, and zero of the head's 32 check-runs is a checklist run. The seat's reading that the watchdog owns it stands; no card is owed by this PR.
  • Locale chain, unchanged (PR acceptance note). A fallback-chain pack's title still beats the authored title. That is the bundle's own title, which the triage put first; not a flag.
  • Governed surfaces. The file list touches none (docs/adr/**, docs/NORTH-STAR.md, .claude/**, skills/**, AGENTS.md, CLAUDE.md); head repo equals base repo, so this is not a fork PR. Governed Surface Queue Guard is green.
  • Check-runs on the head, read once at 2026-10-09T21:01Z and not awaited: 32 total; 20 completed, of which 18 success and 2 skipped (Console Pin Gate, Packed-tarball smoke (opt-in)); 12 in_progress (Test Core 1/6 to 6/6, Dogfood Regression Gate 2/3 and 3/3, Temporal Conformance, Lint & Repo Gates, Type Check · workspace); 0 failed. The in-progress runs are not judged green here. Completed green: Build Core, Build Docs, Dogfood Verify CLI, Dogfood Regression Gate 1/3, Type Check source, debt-ledger and consumer gates, Check Changeset, Check PR Size, Spec property liveness, Governed Surface Queue Guard, the claim and single-writer checks, Check Documentation Links, Auto Label and the flag-docs check. Landing waits on the remainder converging, which is the owning seat's read, not this record's.

Implemented-by: claude/issue-22508-page-header-title-fallback
Reviewed-by: session_01VZqqwTj2wsihZEbfT6yyYN

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 9, 2026 21:13
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 9, 2026 21:13
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 9, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 9, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Standing down on a failure that is not this PR's. domain:spec seat 1 (#6017) · session session_01VZqqwTj2wsihZEbfT6yyYN · 2026-10-09T21:20Z.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 37992289961 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

  • ⚠️ 本次没有可用的聚合签名(日志里没有能解析出测试文件名的 FAIL 行)—— 这不是「没有同签名的其他 PR」,是这一轮没测到。跨 PR 聚合本次不可用,请手工比对其他 PR 的同类评论。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 12 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 9, 2026
Merged via the queue into main with commit 0e00d36 Oct 9, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-22508-page-header-title-fallback branch October 9, 2026 23:09
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

2 participants