Skip to content

feat(spec,cli): reports are translatable — reports in the translation bundle - #25826

Merged
objectstack-fleet[bot] merged 13 commits into
mainfrom
claude/project-thread-uzjpca
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 13 commits into
mainfrom
claude/project-thread-uzjpca

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Requested by 田騏 · project thread

Fixes #25788

Clause-②: yes
Metadata-protocol: added
Ruling-ref: none; triage first grade 6397355491 (a protocol addition under the standing order, p1)

Body refreshed 2026-10-26T02:56Z, after the ui/ merge (#25825): the console half now ships in this PR.

What

A translation bundle can now carry reports:

reports: { opps_by_stage: { label: '各阶段商机', description: '按阶段汇总的商机' } },

Before, an app's reports listed in the authoring language on the console's report index and report page under any locale: the bundle had no key for a report (reports was a retired object-first dialect key, so os validate refused it), and os i18n check reported 100 % with zero report keys (HotCRM, ✓ All 1691 translation keys covered).

  • @objectstack/spec (system/translation.zod.ts): a reports group on the shared per-app shape, so the bundle entry (TranslationData), the registered translation item and the platform bundle all accept it. Keyed by the report's name and closed at label / description (strict: name / title are refused with a message pointing at label, and the singular report with a message pointing at reports).
  • @objectstack/spec (system/i18n-resolver.ts): translateReport, registered in METADATA_DOCUMENT_TRANSLATORS. TRANSLATABLE_METADATA_TYPES is derived from that table and @objectstack/rest reads it, so GET /meta/report (list and item) is served in the request's locale. A key the bundle does not carry leaves the authored string, and an untouched document keeps its reference.
  • @objectstack/metadata-protocol + @objectstack/rest: a report is overlay-editable (ADR-0126 Regime O), so like dashboard and view its translator honours the packaged base (ADR-0029 D9.2a): getPackagedReportBase(name) returns the shipped document under any overlay, and PACKAGED_BASE_ACCESSORS hands it to the translator. A label an org re-authored is served as written; the strings it did not touch are still translated.
  • @objectstack/spec (liveness ledger): translation/reports added as live, with drilled label / description children, seeded with its reader.
  • @objectstack/cli: os i18n extract walks reports[] (walkReports), and os i18n check / os lint coverage report a gap in its own bucket (i18n/missing-report). Every key is recorded with pushOptional, so a description nobody wrote is never demanded.
  • @objectstack/lint: translation-target-unknown resolves each reports.NAME key against the stack's reports[] names, sibling packages' declarations included; an unknown name is an error with the nearest declared name. os explain translation-target-unknown names the leg.
  • scripts/fixtures/i18n-walk-parity/every-group.stack.json: authors one report, so check:i18n-walk-parity sees the group walked.
  • packages/runtime meta-list projection census: report / reports joined both axes; the reference-moves blocks pin the zh-CN label on both reads.
  • examples/app-todo (zh-CN, ja-JP), examples/app-showcase (zh-CN): translations for their 5 + 4 reports. The new walk counts them; deleting one todo key grows check:i18n-coverage 106 → 108, so the fix was to translate them, not to raise the ratchet.
  • ui/ (@object-ui/app-shell, @object-ui/i18n): the translation designer's preview draws the reports group as a "Reports" / 「报表」 category beside the others, and the console's translation loader recognises a payload that carries only reports.
  • Docs: protocol/kernel/i18n-standard.mdx (group list, count, leg table) and ui/translations.mdx.

Acceptance notes

Verification

After the ui/ merge, at head 378f52336f (main merged a third time at bbb49e33f1, clean, then the ui/ port da49afeb6c and the sdui re-record 378f52336f). Every result is exit 0.

  • ui vitest over the touched surfaces (TranslationPreview*, adminPreviews.i18n-10862-s2, packages/i18n/src, apps/console/src/loadLanguage.test.tsx), run from ui/: 91 files, 1424 passed, 13 skipped.
    • Ablation: with the ui/ half stashed, the three pinned files fail 19 tests in 2 files, and app-shell tsc reports TS1360 at TranslationPreview.tsx:103 (CATEGORY_DEFS does not satisfy the record of 14 groups). With the port, that error is gone. The stash was popped and git diff was empty afterwards.
  • ui i18n gates: check:i18n-keys, check:i18n-drift, check:i18n-designer-parity, check:i18n-dead-keys, check:spec-symbols and check:icon-record-names passed.
  • check-sdui-manifest: the ui/packages/i18n source edit moved the in-tree registry digest, which reddened Lint · repo & tooling gates on da49afeb6c. scripts/gen-sdui-manifest-node.mjs regenerated it. sdui.manifest.json is byte-identical (sha256 1f6e1ccb925a…, 106 components); only the record's registrySourcesSha256 and generatedAt moved. The gate now passes, locally and in CI.
  • dispatch-gates --commands derived 135 families, the same list at da49afeb6c and 378f52336f. Each ran with its exit code captured before any pipe: 134 at da49afeb6c and the sdui gate again at 378f52336f, all 135 exit 0. --ran reports 135 run, 0 NOT-MEASURED, 0 UNRUN.
  • Local environment note: this clone's incremental install (from before the ui merge) left @types/react unhoisted, so @object-ui/components' declaration build failed locally on three third-party typings in files this PR does not touch. The generator ran after @types/react and @types/react-dom were hoisted locally; no install change is committed. ui/packages/components/ is unchanged from main, where Type Check UI builds it green.

Before the ui/ merge, all at head ac2bcb0ba (main merged twice, the second clean) unless noted. Every result is exit 0.

  • Package suites (spec, runtime, objectql, metadata-protocol and rest test:repo on a79e5bc2; rest test, lint and cli on f6725a90, before the first merge):
    • @objectstack/spec: test 21849 passed, test:repo 1269 passed.
    • @objectstack/runtime: test 7100 passed, test:repo 947 passed.
    • @objectstack/objectql: test 8723 passed.
    • @objectstack/metadata-protocol: test 21490 passed.
    • @objectstack/rest: test 6737 passed, test:repo 280 passed.
    • @objectstack/lint: test 7302 passed.
    • @objectstack/cli: unit project 4848 passed.
  • After the second merge, at ac2bcb0ba: spec src/system (1974), the cli report walk (4), lint validate-translation-references (209), rest explicit-override (76), runtime projection census (878) and the new objectql accessor file (5) re-ran green, and spec tsc --noEmit passed.
  • typecheck passed for spec, cli, lint, rest, metadata-protocol, runtime, example-todo and example-showcase.
  • dispatch-gates --commands at ac2bcb0ba derived 134 families. A full turbo run build came first. Each family ran with its exit code captured before any pipe, and all 134 exited 0. --ran reports 134 run, 0 NOT-MEASURED, 0 UNRUN.
    • check-engine-split-ratio --days 90 first refused (exit 2) because the clone was shallow. After git fetch --shallow-since=2026-07-20 it passed.
  • check:i18n-coverage: OK, 527 baselined, none new. --update moved nothing.
    • Control: deleting one zh-CN report key in app-todo turns it red (106 → 108), then the file was restored.
  • check:i18n, check:i18n-walk-parity (14 declared, 12 walked, 2 exempted), check:generated: green.
  • Ablation. Each step was run, then restored to the HEAD blob, with git diff empty afterwards:
    • Deleting report: translateReport, turns i18n-report.test.ts red (2 of 16 failed).
    • Deleting the walkReports(config, out); call turns i18n-report-walk.test.ts red (4 of 4 failed).
    • Deleting the ['report', 'getPackagedReportBase'] row turns the REST explicit-override file red (9 failed: §1 and every §4 edited-label cell).
  • Independent review (the spec rule): an isolated Fable subagent ran three rounds, each APPROVE_WITH_NITS. Every finding is fixed, filed or recorded. Rounds 1 and 2 (the whole diff, then its fixes) are spec/i18n: a report's label and description have no translation key — os i18n check reports 100 % with zero report keys while the console's Reports index shows every report in English under zh-CN #25788 comment 6399284479. Round 3 (the merge, the ui/ port and the re-record) is spec/i18n: a report's label and description have no translation key — os i18n check reports 100 % with zero report keys while the console's Reports index shows every report in English under zh-CN #25788 comment 6407105388; its one nit is that 378f52336f's message credits app-shell as well as i18n for the digest move, when app-shell is not a registry package.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01BUeRMD4VYCYUfzxTKcZFAF

@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 5 package(s): @objectstack/cli, @objectstack/lint, @objectstack/metadata-protocol, @objectstack/rest, @objectstack/spec, touching 27 documentable anchor(s). ⚠️ 5 changed file(s) yielded no anchor (packages/spec/api-surface/system.json, packages/spec/authorable-surface/system.json, packages/spec/export-origins/system.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/concepts/metadata-lifecycle.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class))
  • content/docs/protocol/kernel/i18n-standard.mdx (via TranslationDataSchema (symbol, a top-level const), globalActions (literal, a string literal in PlatformTranslationDataSchema; a string literal in TranslationDataSchema; a string literal in TranslationItemSchema), metadataForms (literal, a string literal in PER_APP_SETTINGS_PLATFORM_ONLY), settingsCommon (literal, a string literal in PER_APP_SETTINGS_PLATFORM_ONLY))
  • content/docs/ui/translations.mdx (via globalActions (literal, a string literal in PlatformTranslationDataSchema; a string literal in TranslationDataSchema; a string literal in TranslationItemSchema), metadataForms (literal, a string literal in PER_APP_SETTINGS_PLATFORM_ONLY), settingsCommon (literal, a string literal in PER_APP_SETTINGS_PLATFORM_ONLY))

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

  • content/docs/releases/v16.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class), globalActions (literal, a string literal in PlatformTranslationDataSchema; a string literal in TranslationDataSchema; a string literal in TranslationItemSchema))
  • content/docs/releases/v17/17-0.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class))
  • content/docs/releases/v17/17-7.mdx (via globalActions (literal, a string literal in PlatformTranslationDataSchema; a string literal in TranslationDataSchema; a string literal in TranslationItemSchema))

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
  • 5 changed file(s) yielded no anchor (packages/spec/api-surface/system.json, packages/spec/authorable-surface/system.json, packages/spec/export-origins/system.json, …) — pages documenting those are invisible to this run
  • 6 name(s) were too generic to anchor anything (single lowercase words)
  • 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 — 154 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 f00e8d5c85392db7a3a7d4f41a4c3f619550d11d → packageMentionDocs.

Which tree this was computed on

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

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

⚠️ 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 f00e8d5c85392db7a3a7d4f41a4c3f619550d11d → 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: ac2bcb0ba728e477f90fbe1a1ca227a4154d2b29
Local-runs: none
Scope: packages/spec/src/system/i18n-resolver.ts, packages/spec/src/system/translation.zod.ts + packages/core/src/fallbacks/authored-translation-sync.ts (the one runtime reader of LEGACY_OBJECT_FIRST_KEYS), packages/metadata-protocol/src/protocol.ts (getPackagedReportBase, packagedArtifactBase), packages/rest/src/meta-item-read-gate.ts (PACKAGED_BASE_ACCESSORS, packagedObjectBaseOf, translateMetaList), packages/cli/src/utils/i18n-extract.ts (walkReports, pushOptional), packages/cli/src/utils/i18n-coverage.ts (the report bucket), packages/lint/src/validate-translation-references.ts (Universe.reports, namedItemGroups), packages/lint/src/rule-explanations.ts, packages/spec/src/ui/report.zod.ts (ReportSchema.label / .description are I18nLabelSchema), the generated artifacts (api-surface, authorable-surface, export-origins, liveness/translation.json and its state-counts, the strictness-ledger count), docs/adr/0029 D9.2a and docs/adr/0126 section 3, the precedent commit 47af3dd (#25701), .changeset/25788-translation-reports.md, and the tests that pin each claim (spec i18n-report.test.ts, cli i18n-report-walk.test.ts, lint validate-translation-references.test.ts, rest meta-dashboard-view-i18n-explicit-override.test.ts section 4, runtime meta-list-projection-parity.test.ts, objectql protocol-packaged-report-base.test.ts). The rest of the diff (docs prose, the three example bundles, the walk-parity fixture) is the lane seat's ACCEPT at TIER_DEFAULT.

① Derived judgments

The reports group — right. appTranslationDataShape() gains reports: z.record(name, strictObject({ label?, description? })), inserted after datasets and before pages, byte-for-byte the shape of the positions group the precedent added (same strictObject kit, same TRANSLATION_HISTORY, same aliases: { name: 'label', title: 'label' }). Because the three doors (TranslationDataSchema, PlatformTranslationDataSchema, TranslationItemSchema) spread that one shape, all three accept the group with no per-door edit, and each door's alias map gained report: 'reports'. The thirteen-to-fourteen count moved in every prose site that states it (PER_APP_SETTINGS_PLATFORM_ONLY, ITEM_SETTINGS_PLATFORM_ONLY, the three schema doc blocks, i18n-standard.mdx) and the group order in those messages matches the shape order. authorable-surface carries reports on all three types; api-surface and export-origins carry ReportLike and translateReport; the liveness ledger seeds translation/reports LIVE with drilled label / description children and names the reader, the datasets precedent. The face stops at the two strings the report itself authors, which is what the card asks for.

Its readers — right, each verified in the cited code.

  • translateReport (i18n-resolver.ts): registered in METADATA_DOCUMENT_TRANSLATORS; TRANSLATABLE_METADATA_TYPES is derived from that table and @objectstack/rest reads it, so GET /meta/report list and item localize with no rest edit for the dispatch half (the runtime census test moves the reference on both reads). Lookup walks localeChain(opts) through pickData(bundle, code)?.reports?.[name]?.[attr] and accepts only a non-empty string, as lookupDashboardAttr does. Returns the same reference when nothing matched, when the bundle is absent, or when the document has no string name. An inline locale-map label the bundle does not address is left intact (test pins it).
  • walkReports (cli): reads config.reports[], emits reports.NAME.label / .description through pushOptional, so an unwritten description is recorded without an inline and never demanded (authoredEvidence). The coverage bucket report has its COVERAGE_SOURCE, SOURCE_NOUN and SOURCE_SURFACE rows, so a gap reports as i18n/missing-report, not folded into dashboard. The walk-parity fixture authors one report so check:i18n-walk-parity sees the group walked.
  • translation-target-unknown (lint): Universe.reports = namesOf('reports'), which already folds sibling packages' declarations (the concat disposition the module doc cites), and the reports row joined the renamed namedItemGroups loop beside positions and permissions, same error severity, same nearest-name hint. The explanation text names the leg.

Removing reports from LEGACY_OBJECT_FIRST_KEYS — verified, and required rather than optional. The one runtime reader is packages/core/src/fallbacks/authored-translation-sync.ts at the legacyKeys filter: a stored translation row carrying ANY listed key is warned about and continued, so a declared group left on the list would parse clean in a file and never load from a Studio-authored row. The type Record of LegacyObjectFirstKey on TRANSLATION_KEY_GUIDANCE forces the guidance entry out with it. Every other mention of either symbol in the tree is the exported const's name (api-surface, export-list.test.ts, CHANGELOG), not a reader. objectui: git grep of both symbols over the sibling clone at cd70b78 (fifteen commits behind the pinned b41ed7f6, which only added two CATEGORY_DEFS rows) returns no hits, agreeing with the dev's measurement at 699156a. Consequence of the removal, stated correctly in the new doc comment: a pre-#3778 row carrying only a legacy-shaped reports value (measured footprint zero) now passes through applyConversionsToStoredItem and merges; a non-object value under reports.NAME resolves to nothing in lookupReportAttr, so nothing renders from it and nothing regresses.

The Regime O yield (ADR-0029 D9.2a) — verified against both ADRs. ADR-0126 section 3 lists Regime O as exactly view, dashboard, report, translation, email_template, so a report IS overlay-editable and a translator that ignored the packaged base would replace an org's re-authored label with the catalog string, the #20680 class. ADR-0029 D9.2a rules "an explicit override beats a packaged default" by comparison-based provenance, the same predicate the i18n catalog uses (#8284), compared against the packaged owner alone. The diff honours all three clauses: translateReport calls the one shared valueOverridesPackagedBase(base, key, value), never a second copy; getPackagedReportBase goes through packagedArtifactBase, whose body is lookupArtifactItem and nothing wider, so only a _packageId-marked item answers and an overlay hydrated under the plain registry key never does (the objectql test proves the trap is live and the base does not move); PACKAGED_BASE_ACCESSORS gained the report row so translateMetaList and the item twin resolve the protocol once per request and hand packagedObjectBaseOf(p, 'report', item.name) to the translator. The rest section-4 matrix pins the three facts (edit served as written, untouched description still translated, reset restores the shipped label translated) over item and list reads, two callers and two locales. One conservative edge, identical to the dashboard rule and therefore right: a description the packaged report never declared but the served document carries counts as diverged and is left untranslated.

② Semver level

Clause-②: yes and Metadata-protocol: added on the PR body and in the changeset, line-initial, each once. Kind added takes at least minor on every published package the diff moves: .changeset/25788-translation-reports.md declares minor on @objectstack/spec, @objectstack/cli, @objectstack/lint, @objectstack/metadata-protocol and @objectstack/rest, which are exactly the five packages with a non-test source change (packages/runtime and packages/objectql move test files only, publish nothing, owe nothing). No (narrowing) arm, no !, no tombstone owed (nothing authorable is removed: the retired-dialect reports key becomes a declared group, so an author who wrote it is now accepted rather than refused differently). Check Changeset is green on the head. The precedent spelled yes (widening); the arm is optional under the rule, so the bare yes reads the same. Correct.

③ Boundary flags

  1. Deviation from the precedent's package set (metadata-protocol + rest) — accepted. Inside the claim's "runtime meta translation path"; ADR-0126 makes it a requirement, not a widening of scope: positions and permission sets were not Regime O, a report is. One accessor and one table row, each the twin of an existing dashboard row, each pinned by a test.
  2. Deviation from the precedent's list (the LEGACY_OBJECT_FIRST_KEYS removal) — accepted, verified in ① as the only correct resolution of the name collision; the PM assumption on the order is measured and decided.
  3. A joined report's blocks[].label — should be a card, not a blocker. ui/report.zod.ts types a block's label as I18nLabelSchema, so an inline locale map works today, but the bundle has no key and os i18n check cannot see the gap; the showcase example demonstrates the symptom (showcase_task_overview: translated heading over authored block labels). That is filing gate ① with a named landing site (translateReport plus walkReports, the shape this PR just built), the sibling relation spec/i18n: a report's label and description have no translation key — os i18n check reports 100 % with zero report keys while the console's Reports index shows every report in English under zh-CN #25788 had to spec/i18n: a translation bundle has no key for positions or permission sets, so their labels stay in the authoring language #25652. Recommend the seat files it as a small follow-up on the spec lane (dedupe words: joined report blocks label translation key i18n extract). Out of scope here, correctly recorded by the dev.
  4. Sequencing with objectui#13543 — recommend merge-first, as feat(spec,cli): positions and permission sets are translatable — positions and permissions in the translation bundle #25701 did, with the main-red owned in advance. What Console Pin Gate checks: ci.yml console-pin shallow-clones objectui at .objectui-sha, builds @object-ui/console against THIS tree's spec and client, then asserts dist presence, the pin stamp and spec injection. Its if: excludes pull_request and merge_group, so it runs only on push to main and the hourly schedule. It is NOT in the required set: scripts/check-required-contexts.mjs tombstones it (dropped by the finding(console,plugin-dashboard): the bare dashboard key is claimed by two DIFFERENT full types — and the plugin-dashboard:dashboard lazy stub is never cleared, so it stays unsatisfiable forever objectui#9533 ruling) and says that name must stay out of any required set, and the five queue-blocking contexts are Lint and Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard. So a red there blocks NO pull request and NO queue entry mechanically. What it does cost: (a) the release gate reads it before any approval (scheduled-full-run.mjs --release-gate; chore(console): move the objectui pin to b41ed7f68956 so Console Pin Gate builds again #25735 recorded "the release gate refuses a cut while it stays red" with a Version Packages PR open), (b) AGENTS.md's Main-red convention, which binds the spec lane: the lane's next PR fixes it and no lane PR arms the queue meanwhile, and (c) scheduled-full-run-card.yml files or refreshes the hourly card. Why hold does not work cleanly: objectui#13543's rows fail objectui's own tsc at any .objectstack-sha that lacks the group (TS2353 excess property under satisfies, measured on fix(app-shell): the translation preview draws the positions and permissions groups, at an objectstack pin that declares them (objectui#13478) objectui#13485), so the only pin that lets it land first is this branch's head; objectui's bump-objectstack.sh would accept it with a warning ("A WARNING, NOT A GATE"), but this repo squash-merges and reaps merged branches, so objectui would be pinned to a commit that becomes unreachable, and objectui#13543's own acceptance is written merge-first ("move .objectstack-sha to or past that merge"). Precedent timing: feat(spec,cli): positions and permission sets are translatable — positions and permissions in the translation bundle #25701 merged, the schedule run went red six minutes later, objectui#13485 merged about four hours after that, chore(console): move the objectui pin to b41ed7f68956 so Console Pin Gate builds again #25735 moved the pin about two and a half hours later; roughly six and a half hours of main-red, during which the release gate refused a cut. Conditions for the seat on merge-first: objectui#13543 is already filed with the full acceptance (the two production sites and, per the addendum, the two test files), so the "triage files it" step is pre-done; the seat should state on the card that the next full-battery Console Pin Gate red is expected and owned by objectui#13543 plus the .objectui-sha bump, and the spec lane arms no other PR until that bump lands. If a release cut is pending in the next hours, that is the one reason to hold instead.
  5. Checks on the head at review time. Green: Check Changeset, Governed Surface Queue Guard, Lint · ESLint, Lint · PM dispatch-gates, Spec property liveness, the five PR-shape guards, Check PR Size, Check Documentation Links, Dogfood Regression Gate aggregator. Still running: Test Core 1 to 8 of 8, Build Core, Build Docs, the four Type Check jobs and the seven remaining Lint jobs, so the required aggregators Lint and Repo Gates, TypeScript Type Check, Test Core and Build Core had not concluded; the record judges the contract, and the landing still waits for those to read success. Skipped by design (full battery only): Console Pin Gate, Temporal Conformance, the dogfood shards, the ui/ jobs. The PR is a draft with mergeable_state: blocked.

Implemented-by: claude/project-thread-uzjpca
Reviewed-by: session_01K2Tat8JEt4AhLASV4V4QWE

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读

改了什么
报表(report)的名称和描述可以翻译了:翻译包新增 reports 分组(label、description),和 #25701 给岗位、权限集加的分组同一形状。os i18n extract 会抽出每张报表的键,os i18n check 计入覆盖率,运行时读报表元数据时套用翻译;组织在 Studio 里改过的报表名优先于包里的翻译(报表属于可被组织覆盖的元数据)。

为什么改
HotCRM 在中文界面下十张报表全是英文,而 os i18n check 报 100%,因为报表根本没有翻译键。

风险与代价(含回滚)
新增可选分组,已有翻译包不受影响。objectui 的翻译预览对分组是穷举的,必须同步改(已立卡 objectui#13543);本 PR 一合并,main 上定时跑的 Console Pin Gate 会红,直到 objectui 那张卡落地并推进 .objectui-sha。回滚 = 撤销合并。

席位意见
内容可以合并:开发者的 Fable 独立复核两轮、席位的 Fable 契约复核都通过,CI 全绿,不在受治理路径上。合并时机请你定(已在项目线程里问你)。

你要做的
在项目线程里选「先不合」或「现在合并」。

席位核对

Seat domain:spec#4 (#18917) · session_01K2Tat8JEt4AhLASV4V4QWE · head ac2bcb0ba728 · 2026-10-25T16:53Z · verdict ACCEPT (Tier S; landing held for the owner's sequencing choice).

  1. Shape. Draft, base main, Fixes #25788, Clause-②: yes, Metadata-protocol: added; 29 files, +823/−48; check-governed-merges --pr 25826: 0 of 29 paths governed, under the 3000-line threshold.
  2. Independent full review (the owner's rule 「spec skills adr 都需要fable 完整的复核」): two rounds by an isolated Fable reviewer, both approve-with-nits, nits fixed; verdict 6399284479 on the card.
  3. Contract review of record: PASS on this head at CONTRACT_REVIEW_TIER, 6400225090. The reports group matches the positions precedent at all three doors; removing reports from LEGACY_OBJECT_FIRST_KEYS is required (the only runtime reader skips any stored row carrying a listed key); the Regime O yield follows ADR-0126 §3 and ADR-0029 D9.2a through the shared predicate; changeset minor on spec, cli, lint, metadata-protocol and rest.
  4. Gates. Head-pinned check-runs on ac2bcb0ba728: 48 completed, 38 success, 10 skipped, none failed. Dev dispatch-gates --commands 134/134 exit 0, --ran 0 NOT-MEASURED.
  5. Consumer. objectui needs the change (TranslationPreview CATEGORY_DEFS and SPEC_TRANSLATION_GROUPS are exhaustive over the bundle shape): child card objectui#13543 with addendum 6399274054. Merging first turns main's scheduled Console Pin Gate red until objectui#13543 lands and .objectui-sha moves (precedent feat(spec,cli): positions and permission sets are translatable — positions and permissions in the translation bundle #25701: about 6.5 hours, and the release gate refuses a cut meanwhile). The gate is not a required context, so it blocks no PR.
  6. Accepted as is / not filed: a joined report's blocks[].label has no translation key; the dev recorded it out of scope and the contract review would make it a small follow-up card. The seat files no card this shift (the owner closed the round to new work); it is listed in the clock-out brief.
  7. Landing. Held as draft until the owner chooses: merge now (pr_ready + automerge_enable through the relay) or hold until objectui#13543 lands.

…orts group

objectui is now this repository's ui/ workspace, so the consumer half filed
as objectui#13543 lands with the spec change: a reports row in the
translation designer's CATEGORY_DEFS (en/zh heading keys), 'reports' in
SPEC_TRANSLATION_GROUPS, and the two tests that pin both lists.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BUeRMD4VYCYUfzxTKcZFAF
…s edit

The ui half of the reports group edits two registry sources
(app-shell's TranslationPreview and metadata-admin i18n table, and
i18n's spec-translations), so check-sdui-manifest's in-tree digest
moved. Regenerated with scripts/gen-sdui-manifest-node.mjs: the
manifest itself is byte-identical (106 components, sha256 1f6e1ccb925a),
only the record's registrySourcesSha256 and date move.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BUeRMD4VYCYUfzxTKcZFAF
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Test UI (8/16) is red, but not because of this PR. main fails the same way.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 378f52336f10490c6e1e835da8690ef9ed4c6a8c
Local-runs: none
Scope: packages/spec/src/system/translation.zod.ts, packages/spec/src/system/i18n-resolver.ts, packages/metadata-protocol/src/protocol.ts, packages/rest/src/meta-item-read-gate.ts, packages/cli/src/utils/i18n-extract.ts, packages/cli/src/utils/i18n-coverage.ts, packages/lint/src/validate-translation-references.ts, the generated spec artifacts (api-surface, authorable-surface, export-origins, liveness/translation.json and its state-counts) + ui/packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.tsx (CATEGORY_DEFS, TranslationGroup), ui/packages/app-shell/src/views/metadata-admin/i18n.ts (ENGINE_STRINGS_EN, ENGINE_STRINGS_ZH), ui/packages/i18n/src/utils/spec-translations.ts (SPEC_TRANSLATION_GROUPS, isSpecTranslationData, transformSpecTranslations), the two re-pinned tests (TranslationPreview.categories-11765, adminPreviews.i18n-10862-s2), ui/apps/console/src/loadLanguage.test.tsx (read, unchanged), scripts/sdui-manifest.record.json with scripts/gen-sdui-manifest-node.mjs and scripts/check-sdui-manifest.mjs, .changeset/25788-translation-reports.md with .changeset/config.json, scripts/check-closing-target-claim.mjs and closingKeywordTargets in scripts/pm/check-half-states.mjs, and the earlier record 6400225090 on ac2bcb0ba728e477f90fbe1a1ca227a4154d2b29. The rest of the diff (docs prose, the three example bundles, the walk-parity fixture) stays the lane seat's ACCEPT at TIER_DEFAULT.

This is a delta record. The PASS at ac2bcb0ba728e477f90fbe1a1ca227a4154d2b29 (comment 6400225090) judged the spec, runtime, cli and lint half; this record re-judges only what moved between that head and this one, and carries the earlier judgments forward where the hunks are shown unchanged.

① Derived judgments

The spec/runtime half is unchanged, measured rather than assumed. Three commits sit between the two heads: a merge of main (bbb49e33), the ui/ port (da49afeb) and the sdui re-record (378f5233). I took the PR's patch on its 29 original files at the old head (against its merge base then, abebc4e1) and at the new head (against its merge base now, c475b45b), dropped index lines and hunk-header offsets, and diffed the two patches: zero lines differ. So every judgment in ① of the earlier record about appTranslationDataShape(), translateReport, the LEGACY_OBJECT_FIRST_KEYS removal, getPackagedReportBase, the PACKAGED_BASE_ACCESSORS row, walkReports, the coverage bucket and the lint universe holds on this head as written there. What main brought onto those files between the two merge bases is not this PR's diff. The file list grew by exactly six paths, all ui/ or the sdui record, plus two lines and one bullet in the changeset (② below). Net diff: 35 files, +842/−51.

The ui/ port is complete against #25859's acceptance. Every exhaustive copy of the bundle group list under ui/ now carries reports, and I searched for others:

  • CATEGORY_DEFS (TranslationPreview.tsx) is satisfies Record over TranslationGroup = keyof (typeof TranslationDataSchema)['shape'], so without the row app-shell fails type-check (TS1360) and with a row for a key the spec lacks it fails the other way. The row is inserted after datasets and before pages, which is the schema's order (the earlier record verified the shape inserts reports exactly there), flat: false like the sibling nested groups, icon BarChart3. Type Check UI is green on this head, which is the gate the card named.
  • The label key engine.translationPreview.category.reports is in both engine packs, ENGINE_STRINGS_EN ("Reports") and ENGINE_STRINGS_ZH (「报表」). Those two are the only engine packs in i18n.ts (the resolver at its foot returns one or the other), and no built-in locale pack under ui/packages/i18n carries a translationPreview namespace, so "every locale pack" is satisfied by two.
  • SPEC_TRANSLATION_GROUPS (spec-translations.ts) gains 'reports' with the provenance comment the positions/permissions rows carry. Its one reader is isSpecTranslationData, so the behaviour change is exactly the one claimed: a payload carrying only reports now takes the transform branch. transformSpecTranslations needs no row: reports is not in PASSTHROUGH_TOP_LEVEL_KEYS, and neither are positions, permissions, datasets or pages; all of them ride the "forward any top-level namespace we do not explicitly know about" loop onto app, so a client-side reports.NAME.label lookup resolves, as app-shell + i18n: add the reports translation group objectstack #25788 adds — TranslationPreview's CATEGORY_DEFS fails type-check at the next pin, and the console loader does not recognise a reports-only payload #25859 measured.
  • The two hand-pinned tests moved as the app-shell + i18n: add the reports translation group objectstack #25788 adds — TranslationPreview's CATEGORY_DEFS fails type-check at the next pin, and the console loader does not recognise a reports-only payload #25859 addendum (comment 6405963677) required: ONE_ENTRY gains a reports fixture whose row is a 2-key entry (label and description), and the 10862-s2 key list gains the row with the denominator 2/13 (15%) becoming 2/14 (14%), which is the right arithmetic for fourteen groups.
  • ui/apps/console/src/loadLanguage.test.tsx needs no edit: it walks GetTranslationsResponseSchema.shape.translations dynamically, so it turned red only through the SPEC_TRANSLATION_GROUPS gap, which the row closes. TranslationPreview.categories-11765.test.tsx also derives GROUPS from the schema at runtime, and the fixture row is what it needed.
  • Nothing else under ui/ is keyed on the group list. git grep at this head for SPEC_TRANSLATION_GROUPS, CATEGORY_DEFS, TranslationGroup, TranslationDataSchema.shape and translationPreview.category finds only the files above plus the globalActions-11755 test, which names two existing categories and is unaffected. A second sweep for any ui/ source naming datasets, positions and globalActions together returns exactly the five files the port touched. The report surfaces (appReportIndex.ts, ReportView.tsx, AppHeader.tsx, useRecentItemLabel.ts) are byte-identical to main and read the server-translated GET /meta/report, as the earlier record already judged for the serving path.

The sdui digest re-record is the regeneration it claims. The diff on scripts/sdui-manifest.record.json is two fields: the day stamp moved forward one day and inTree.registrySourcesSha256 moved. sha256 of the artefact (1f6e1ccb925a…), components (106), registrySources (922) and registryPackages are unchanged, and sdui.manifest.json is not in the diff, so the artefact is byte-identical and only the source digest moved. That is the expected shape: ui/packages/i18n is one of the 27 registryPackages whose src/ the digest reads, ui/packages/app-shell is not, and the port edits an existing i18n source file rather than adding one (hence 922 stays). Under Local-runs: none the digest is not recomputed here; the gate that recomputes it (scripts/check-sdui-manifest.mjs, run by Lint · repo & tooling gates) reddened on da49afeb and is green on this head, which is the recomputation. The round-3 Fable nit stands as recorded: the commit message credits app-shell as well as i18n for the digest move, app-shell is not a registry package; message only, no tree effect.

② Semver level

Clause-②: yes and Metadata-protocol: added, line-initial, once each in the PR body and in the changeset body. Kind added takes at least minor on every published package the diff moves. The ui/ port moves published source in @object-ui/app-shell and @object-ui/i18n, and after #25745 both are members of the one fixed group in the root .changeset/config.json (the @object-ui/* block sits beside the @objectstack/* packages; neither is in ignore). The changeset now declares minor on exactly the seven packages with a non-test source change: spec, cli, lint, metadata-protocol, rest, app-shell, i18n, plus a bullet for the two ui packages. No entry is owed under ui/.changeset/: #25745 deleted ui/.changeset/config.json, GitHub reads no workflow from ui/.github/, and the files still parked there are objectui's pre-cutover changesets held for the aftercare card #25831 (scripts/ui-import/README.md); a new change writes at the root, where Check Changeset reads it and is green on this head. No (narrowing) arm, no !, no tombstone owed. Correct.

③ Boundary flags

  1. app-shell + i18n: add the reports translation group objectstack #25788 adds — TranslationPreview's CATEGORY_DEFS fails type-check at the next pin, and the console loader does not recognise a reports-only payload #25859 carries no closing keyword in this PR, and that is right. scripts/check-closing-target-claim.mjs admits a closing keyword only while the closed card's own thread carries a Claim: whose Branch: line names this PR's head branch; app-shell + i18n: add the reports translation group objectstack #25788 adds — TranslationPreview's CATEGORY_DEFS fails type-check at the next pin, and the console loader does not recognise a reports-only payload #25859's four comments (the test addendum, triage's grade, the transfer note, the dev's "carried in feat(spec,cli): reports are translatable — reports in the translation bundle #25826" note) hold none, and the gate's header forbids a dispatched executor from posting a second claim to clear it. The body's prose mention of the keyword sits in a code span: closingKeywordTargets strips markdown code on the body surface, GitHub's parser does not fire inside a code span either, The card this PR closes must claim this branch is green on this head after the body refresh, and GitHub lists no PR closing app-shell + i18n: add the reports translation group objectstack #25788 adds — TranslationPreview's CATEGORY_DEFS fails type-check at the next pin, and the console loader does not recognise a reports-only payload #25859. So the seat closes app-shell + i18n: add the reports translation group objectstack #25788 adds — TranslationPreview's CATEGORY_DEFS fails type-check at the next pin, and the console loader does not recognise a reports-only payload #25859 by hand after the merge, as completed, citing the merge commit and feat(spec,cli): reports are translatable — reports in the translation bundle #25826; it is pm:blocked on spec/i18n: a report's label and description have no translation key — os i18n check reports 100 % with zero report keys while the console's Reports index shows every report in English under zh-CN #25788 and nothing else is left on it (the dev's note 6406913991 says the same). Fine to carry forward.
  2. Test UI 8/16 is red on this head, and it is main's red, not this PR's. The one failing test is ui/scripts/__tests__/check-pre-install-import-graph.test.ts ("unresolved: scripts/objectstack-pin.mjs"): release(ui-merge): one release lane for both scopes — @object-ui joins the fixed group, @objectstack/console builds from ui/apps/console, the objectui pin is deleted (#25711) #25745 deleted ui/scripts/objectstack-pin.mjs while ui/.github/workflows/ci.yml still runs it, and the test walks that workflow. The test, the script and the workflow are byte-identical between main and this head; the PR touches none of them; and main's own CI runs at the merge base c475b45b fail on the same job, Test UI 8/16. It is already carded as Queue-flake anchor: scripts/__tests__/check-pre-install-import-graph.test.ts #25863 ("Queue-flake anchor: scripts/tests/check-pre-install-import-graph.test.ts", domain:devx, p1). Test UI is outside the five required contexts, so it blocks no queue entry; the record notes it so nobody reads this PR as the cause.
  3. Checks on this head at review time. Green: Type Check UI, Lint UI, Build & E2E UI, Test UI 1 to 7 and 9 to 16 of 16, Lint & Repo Gates, Type Check · workspace, · debt ledger, · consumer gates, · source gates, every Lint · job including repo & tooling gates, Build Core, Build Docs, Test Core 1 to 3 and 5 to 8 of 8, Governed Surface Queue Guard, Check Changeset, Spec property liveness, the five PR-shape guards. Still running: Test Core 4 of 8, so the Test Core aggregator has not concluded. Red: Test UI 8/16 (flag 2). Skipped by design: Temporal Conformance, the dogfood shards, the opt-in harnesses. The record judges the contract; the landing still waits for Test Core to read success.
  4. Carried forward from the earlier record, unchanged: the joined report's blocks[].label follow-up (③.3 there) is still the seat's to file on the spec lane; ③.4 there (sequencing against a Console Pin Gate red) is superseded, as the dev says, because the gate and .objectui-sha are gone and Type Check UI compiles ui/ against this PR's spec on the PR itself.

Implemented-by: claude/project-thread-uzjpca
Reviewed-by: session_01K2Tat8JEt4AhLASV4V4QWE

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Regen-provenance: 6407328168 · 378f52336f10490c6e1e835da8690ef9ed4c6a8c → 5797bfd06d7c54d928f568fd9e48df74c583a0bd · unexplainedPathsBetween (record-recognisers, base origin/main) → (empty)

The seat merged main into the branch through the relay pr_update_branch (run 40263095006) to pick up #25879, which fixes the Test UI (8/16) red this head inherited from main (#25863). No path of this PR moved; the contract review 6407328168 carries.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读

改了什么
报表(report)的名称和说明进了翻译包,和岗位(positions)用同一套规则:spec 的翻译分组、os i18n 抽取,以及读取时的叠加。原来 objectui 那半张卡(objectui#13543,现 #25859)在 ui 仓库并入后直接改在本 PR 的 ui/ 里,翻译预览也能列出报表了。

为什么改
多语言部署里,报表标题以前只能显示作者写的原文。

风险与代价(含回滚)
纯新增。没有声明报表翻译的应用不受影响。回滚就是撤销合并。

席位意见
建议合并。依据:

  • 开发者的 Fable 完整复核已通过(6399284479、6407105388)。
  • 席位的 Fable 契约复核两次 PASS(6400225090,以及 ui 移植之后的 6407328168)。
  • 这次并入 main 后 PR 文件没动,复核延续(6407423980)。

唯一的红是 Test UI (11/16)。它是 main 上已知的同一个红,不是本 PR 引起的,也不是必需检查。

你要做的
无。不在受治理路径上,席位经合并队列落地。

席位核对

Seat domain:spec#4 (#18917) · session_01K2Tat8JEt4AhLASV4V4QWE · head 5797bfd06d7c · 2026-10-26T03:45Z · verdict ACCEPT (Tier S; the seat lands it through the merge queue). This supersedes seat record 6400446674 (head ac2bcb0ba728), which held the landing for the objectui pin. That pin was retired by #25745, so there is nothing left to sequence.

  1. Shape. Base main, Fixes #25788, Clause-②: yes, Metadata-protocol: added. 35 files, +842/−51, including the ui/ consumer half: TranslationPreview and SPEC_TRANSLATION_GROUPS, ported from objectui#13543 (now app-shell + i18n: add the reports translation group objectstack #25788 adds — TranslationPreview's CATEGORY_DEFS fails type-check at the next pin, and the console loader does not recognise a reports-only payload #25859). check-governed-merges --pr 25826 finds 0 paths governed, under the 3000-line threshold.
  2. Independent full review (the owner's rule 「spec skills adr 都需要fable 完整的复核」): Fable, verdicts 6399284479 and 6407105388 (the latter after the ui port).
  3. Contract review of record: PASS at CONTRACT_REVIEW_TIER. The first is 6400225090; the delta after the ui port is 6407328168 at 378f5233. It carries to this head by Regen-provenance 6407423980: unexplainedPathsBetween returned empty after pr_update_branch.
  4. Gates. Check-runs on 5797bfd06d7c: 62, of which 56 success, 5 skipped and 1 failure. The failure is Test UI (11/16): scripts/__tests__/check-pre-install-import-graph.test.ts, unresolved: scripts/objectstack-pin.mjs.
  5. Accepted as is, carried to the clock-out brief: a joined report's blocks[].label has no translation key.
  6. Landing. pr_ready + automerge_enable through the relay in this pass. After the merge, the seat closes app-shell + i18n: add the reports translation group objectstack #25788 adds — TranslationPreview's CATEGORY_DEFS fails type-check at the next pin, and the console loader does not recognise a reports-only payload #25859 as completed, citing the merge, and confirms the close of spec/i18n: a report's label and description have no translation key — os i18n check reports 100 % with zero report keys while the console's Reports index shows every report in English under zh-CN #25788.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 26, 2026 03:46
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 26, 2026 03:46
@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

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

  • Test Core (1/8) — 失败步骤: Run this shard's tests

    @objectstack/spec:test:      ✓ inverts the card's measured control: the three bogus keys now FAIL parse instead of adding zero warnings  357ms
      ↳ 失败原因: (这条 FAIL 之后 12 行内没有可识别的原因行 —— 点进 job 看)
    @objectstack/spec:test:repo:  FAIL   repo  scripts/strictness-ledger-doc.test.ts > the ledger and the generated counts agree > is checked in current — every shard on disk equals a fresh render, and no
      ↳ 失败原因: @objectstack/spec:test:repo: AssertionError: expected Map{ …(14) } to deeply equal Map{ …(14) }
    @objectstack/spec:test:repo:  FAIL   repo  scripts/strictness-ledger-doc.test.ts > the ledger and the generated counts agree > the read-time totals equal the sums of the shards' own rows
      ↳ 失败原因: @objectstack/spec:test:repo: AssertionError: expected 'untriaged: 1236 object site(s) across…' to be 'untriaged: 1235 object site(s) across…' // Object.is equality
    
  • Test UI (11/16) — 失败步骤: Run the ui tests (shard 11/16)

    × finds no pre-install gate reaching a package 7ms
    FAIL   unit  scripts/__tests__/check-pre-install-import-graph.test.ts > the real tree — every step objectui#6148 measured is still derived > finds no pre-install gate reaching a package
      ↳ 失败原因: AssertionError: a script this repository runs before `pnpm install` needs `node_modules` to load: expected [ Array(1) ] to deeply equal []
    

↳ 失败原因 是判读的关键:超时(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,按失败测试文件聚合):

历史信号:

  • 本 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)

…ain's demoStaff

Merging origin/main (f00e8d5, #25830) stacked its demoStaff schema
on this branch's reports group. The os-regen paths both sides moved
are regenerated from the merged tree: api-surface, authorable-surface
and export-origins carry both DemoStaff and the reports entries, and
the strictness ledger's system/ row moves 381 -> 382.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BUeRMD4VYCYUfzxTKcZFAF
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Regen-provenance: 6407328168 · 5797bfd06d7c54d928f568fd9e48df74c583a0bd → 5e30af5c5ca0cbd13d70f811614e720770f91f2c · unexplainedPathsBetween (record-recognisers, base origin/main) → (empty)

The merge queue dropped this PR (run 40265890690) because strictness-ledger-doc.test.ts found the ledger total stale after #25830 merged (f00e8d5c). The dev merged main with os-regen-merge.sh (d8592395be) and regenerated with the repo's generators (5e30af5c5c). Four generated files now carry both PRs' entries: system api-surface, authorable-surface, export-origins and the ledger's system row (381 → 382). No PR-authored path moved, so the contract review 6407328168 and seat ACCEPT 6407756541 carry to this head. The dev's scoped re-check: check:generated 14/14 current; strictness-ledger-doc.test.ts 24/24.

@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

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

  • Test UI (11/16) — 失败步骤: Run the ui tests (shard 11/16)

    × finds no pre-install gate reaching a package 10ms
    FAIL   unit  scripts/__tests__/check-pre-install-import-graph.test.ts > the real tree — every step objectui#6148 measured is still derived > finds no pre-install gate reaching a package
      ↳ 失败原因: AssertionError: a script this repository runs before `pnpm install` needs `node_modules` to load: expected [ Array(1) ] to deeply equal []
    

↳ 失败原因 是判读的关键:超时(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,按失败测试文件聚合):

历史信号:

  • ⚠️ 本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 13 个失败构建(不含本次)。

分诊清单:

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

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

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/l tests tooling

Projects

None yet

2 participants