Skip to content

fix(metadata-protocol): the packaged app base carries contributed navigation, so Setup entries translate - #30553

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-m01xpo
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-m01xpo

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread

domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF

Fixes #30504

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:服务端给翻译器的「打包基准」应用,现在带上了各插件往这个应用里贡献的菜单项(用的是读取时本来就在用的同一个合并函数)。中文控制台的设置侧栏 45 个子菜单从全英文变成按翻译目录显示中文;account 应用里贡献进去的「连接智能体」同样修好。
  • 为什么改:这些子菜单都是插件贡献的,基准里没有它们,翻译器就把它们当成定制包自己写的菜单,跳过翻译目录,于是全显示英文,而目录里其实有译文(如 nav_users = 用户)。
  • 风险与回滚:只改一个服务端读取函数,接口返回的结构不变,只是标签值变成译文;定制包(.custom)自己新写的菜单仍然只读定制包自己的译文(ADR-0146 D2 不变)。回滚就是 revert。
  • 先看:packages/metadata-protocol/src/protocol-discovery-and-bases.ts 的 getPackagedAppBase;packages/objectql/src/protocol-custom-override-bare-slot-envelope.test.ts 末尾新增的 [#30504] 用例。
  • 两分钟验证:pnpm --filter @objectstack/objectql exec vitest run src/protocol-custom-override-bare-slot-envelope.test.ts -t 30504,应看到 4 个用例通过;或用中文打开控制台的设置,侧栏子菜单显示「用户」「权限集分配」等中文。
  • 需要你做的:无,席位落地。

What changes

getPackagedAppBase(name) (the body in protocol-discovery-and-bases.ts; protocol.ts and meta-item-read-gate.ts are untouched) now answers the app's packaged declaration with the code layer's navigation contributions folded in, through the one fold every served app read already applies, SchemaRegistry.applyNavContributions (ADR-0029 D7). No second copy of the fold. When the registry has no such method (a double) the declaration alone is answered, and a throw from the fold answers the declaration alone, which is the base main answered.

Why this is the fix: the served app is declaration + contributions (protocol-meta-item-reads.ts folds them on read), while the base handed to translateApp was the declaration alone. For a node id the known base lacks, packagedNavNodes answers {}, valueOverridesPackagedBase reads the label as changed, and a changed label reads only the pair's translations (ADR-0146 D2). Outside a pair that bundle is empty, so every contributed entry kept its source text. With the contributions in the base, a contributed node is judged against its own packaged label and the catalog applies.

ADR-0146 D2 is preserved: a node a .custom override authors is in no contribution, so it is still absent from the base and still reads only the pair's translations, else its own text. Shape B (packagedNavNodes answering "no base knowledge" for an unknown id) was not taken, as the dispatch ruled: it would let the base catalog relabel an override-authored node.

Tests

  • New top-level describe [#30504] in packages/objectql/src/protocol-custom-override-bare-slot-envelope.test.ts (the metadata-protocol: page, app, dataset, doc and book serve a published override only from the registry bare slot; build the row-served path, then take them out of it #29613/Declared overrides: a mapping override publishes but is never served, doc and mapping overrides cannot be restored, and an override's label can be hidden by translation #30062 real-boot harness that already pins the packaged app base), with and without a metadata service:
    • the packaged base carries the contributed entry, and the registry's own item is not mutated by the fold;
    • the base's own app, translated against that base, reads the contributed entry as 用户 in zh-CN and Users in en;
    • on the pair's declared override, the override-authored nav_custom keeps Custom entry with no pair translation (the catalog's 基包译文 is not taken) and reads the pair's 自定义入口 when the pair ships one, while the contributed nav_users reads 用户 in both.
  • Red on main: the test committed first (97886306), @objectstack/metadata-protocol built without the fix: 4 failed / 87 skipped (expected {} to deeply equal { nav_users: 'Users' }). With the fix and a rebuild: the file 91/91 passed.
  • After merging origin/main and rebuilding the objectql closure: this file plus registry-nav-contribution-group-semantics.test.ts and registry-nav-contribution-disabled-package.test.ts, 112/112 passed.
  • pnpm --filter @objectstack/metadata-protocol exec vitest run: 316 files passed / 4 skipped, 22425 tests passed / 26 skipped (before the main merge; the merge brought only two metadata-protocol test files).
  • pnpm --filter @objectstack/metadata-protocol --filter @objectstack/objectql typecheck: lock VERDICT command-exit 0.

Acceptance notes

  • Measured whether other apps are hit the same way: every code-level contribution in the tree targets setup, plus one into account (packages/mcp/src/connect-ui.ts, nav_connect_agent), the same code path, fixed here (live: served 连接智能体). Studio carries no code-level contributions, so it is not on this path.
  • protocol.ts's public docblock for getPackagedAppBase still reads "answered the same way" as the page base; it is left untouched to stay off seat 5's facade series, and the new behaviour is documented on the body in protocol-discovery-and-bases.ts. A one-line docblock follow-up there can ride a later protocol.ts PR.
  • nav_webhooks reads Webhooks in zh-CN: that is the catalog's own zh-CN value, not a miss.

Verification

Live: showcase stack, GET /api/v1/meta/app/setup, signed in as the seeded admin

examples/app-showcase, objectstack dev --seed-admin --fresh on this branch's build (fix included):

  • Accept-Language: zh-CN: 9/9 group headings and 45/45 leaf entries in the catalog's zh-CN text (44 contain CJK; the 45th is nav_webhooks = Webhooks, the catalog's value). First leaves: nav_system_overview=系统概览 | nav_packaged_actions=打包动作 | nav_marketplace_browse=浏览应用市场 | nav_marketplace_installed=已安装应用 | nav_users=用户 | nav_organization=当前组织 | nav_business_units=业务单元 | nav_teams=团队. On main the ui seat's dev measured 0/45 (os-dev-report 6660644606).
  • Accept-Language: en: 0/9 and 0/45 CJK, labels unchanged (Users, Organization, …).
  • GET /api/v1/meta/app/account in zh-CN: nav_connect_agent=连接智能体.
Gates (local subset, derived by dispatch-gates --commands on this diff)

58 derived families, all :: exit 0; --ran reconciles 58 run, 0 NOT-MEASURED. Two first ran as exit 3, PREREQUISITE NOT MET, and were re-run once their prerequisite was met: check-plugin-teardown-shape.mjs --self-test (shallow clone, after fetching its pinned fixture commit 621a4876) and check:dual-build-cjs-loads (seven packages had no dist/; 116 entry points across 71 packages load). Record: project files server-seat4/30504/. pnpm lint: no findings. Taken on head 5eef4217.


Generated by Claude Code

@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 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; 103 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 — 10 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 86ae2cac6ef7c859dd3d0f9305ca68e62666b790 → packageMentionDocs.

Which tree this was computed on

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

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

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

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 5, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF

维护者速读

  1. 改了什么:服务端交给翻译器的「打包基准」应用现在带上各插件往该应用贡献的菜单项(复用读取时本来就用的同一个合并函数 applyNavContributions)。中文控制台设置侧栏 45 个子菜单从全英文变为按翻译目录显示中文;account 应用里贡献的「连接智能体」同样修好。
  2. 为什么改:这些子菜单都是插件贡献的,基准里没有它们,翻译器把它们当成定制包自写的菜单,跳过翻译目录,于是全是英文,而目录里有译文(如 nav_users = 用户)。
  3. 风险:只改一个服务端读取函数,接口结构不变,只是标签值变成译文;定制包(.custom)自写的菜单仍只读定制包自己的译文(ADR-0146 D2 不变);合并函数先克隆,不改打包基底;合并失败时退回原基准,不会 5xx。回滚即 revert。
  4. 先看:packages/metadata-protocol/src/protocol-discovery-and-bases.ts 的 getPackagedAppBase;packages/objectql/src/protocol-custom-override-bare-slot-envelope.test.ts 末尾 [#30504] 用例。
  5. 两分钟验证:pnpm --filter @objectstack/objectql exec vitest run src/protocol-custom-override-bare-slot-envelope.test.ts -t 30504,4 个用例通过。
  6. 需要你做的:无,席位落地。非 BREAKING,不属 Tier H,不超 3000 行。

席位意见:ACCEPT(#30504)。中文(及其他语言)控制台的设置侧栏子菜单现在按翻译显示,不再整片英文;英文界面不变,定制包自己写的菜单仍只用定制包的译文。

席位核对

Head-sha: 5eef42172758da663c1d22226f21cfa36b7d3a9f · read 2026-11-05T15:59Z

Item Reading
Shape Draft → main via relay pr_create; Fixes #30504, Clause-②: no / Metadata-protocol: none at line start. 3 files, +124/−4. ⛔ packages/spec/**, ui/**, protocol.ts, meta-item-read-gate.ts untouched (no overlap with seat 5's #30459).
Ruling Triage 6659963754 (p2); ui seat release 6660688296 with the measured mechanism; the seat's pick, shape A (fold code-layer contributions into the packaged app base).
Seat diff read getPackagedAppBase returns registry.applyNavContributions(declared); the fold structuredClones the app before pushing items, so the packaged base is never mutated; a disabled package's contributions are skipped as on every served read; a throw falls back to the declaration. A .custom-authored node is in no contribution, so it stays absent from the base and keeps reading only the pair's translations (D2), pinned.
Evidence Dev report 6662028822: new tests 4/4 red without the fix, 91/91 green with it; metadata-protocol 22,425 green; live showcase zh-CN Setup 45/45 leaves translated (0/45 on main), en unchanged.
Gates (check-runs on this head) 48 completed: 35 success, 13 skipped, 0 failed (re-read at landing).
Semver @objectstack/metadata-protocol: patch, Clause-②: no, Metadata-protocol: none (served shape unchanged, label values only).
Landing Not governed (check-governed-merges --pr 30553: 0 of 3 paths, re-read 2026-11-05T15:59Z), ≤ 3000 lines, not BREAKING, so per pm-dispatch the seat lands it: pr_ready + automerge_enable.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 5, 2026 16:00
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 5, 2026 16:00
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 4bf6e8f Nov 5, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-m01xpo branch November 5, 2026 16:30
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 size/m tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Setup sidebar: the People & Organization and Access Control entries read English on a zh-CN console

1 participant