Skip to content

fix(plugin-security): translate the built-in positions under zh-CN, ja-JP and es-ES - #26606

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-8fpct3
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-8fpct3

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Fixes #26562

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:平台内置的七个岗位(平台管理员、组织所有者、组织管理员、组织成员、组织外部成员、全体成员、访客)在中文、日文、西班牙文界面下,名称和说明都按当前语言显示。
  • 为什么改:设置 → 岗位在中文界面下,应用自己的岗位已是中文,平台内置的七个仍是英文,其中包括每条授权都要参照的「全体成员」「访客」。内置权限集早已翻译,岗位漏掉了。
  • 风险与回滚:低。只新增翻译包里的 positions 组,英文、岗位本身的定义与授权都不变;回滚即 revert 本 PR。
  • 先看:packages/plugins/plugin-security/src/translations/builtin-positions.ts(三种语言的译文);translations/index.ts(三行接入);translations/builtin-positions.test.ts。
  • 两分钟验证:pnpm --filter @objectstack/plugin-security exec vitest run src/translations/builtin-positions.test.ts,预期 29/29 通过;或中文界面打开 设置 → 岗位,内置七项显示中文。
  • 需要你做的:无,席位落地。

What changes

  • New packages/plugins/plugin-security/src/translations/builtin-positions.ts: hand-authored positions.NAME.label / .description for the seven built-in positions (platform_admin, org_owner, org_admin, org_member, org_external, everyone, guest) in zh-CN, ja-JP and es-ES, the locales the built-in permission-set group (permission-sets.ts) covers. Hand-authored for the reason that file gives: this package's extract runs objects-only, so os i18n extract writes no positions group. English needs no entry (an untranslated locale serves the declaration's own text). Terms follow this bundle's objects: 岗位 / ポジション / puesto.
  • translations/index.ts: each translated locale of SecurityTranslations gains positions beside permissions, outside withSourceFallback's inputs exactly like the permission-set group (enSource carries neither, so nothing is judged stale).
  • New translations/builtin-positions.test.ts: per locale, the group translates exactly the declared built-ins, no two labels repeat, each label and description is a non-English string naming no machine id, and translateMetadataDocument('position', declaration, SecurityTranslations, { locale }) serves that locale's label and description with nothing else of the position changed.
  • Changeset: @objectstack/plugin-security patch.

No packages/spec/** change.

Why

Card #26562 (triage 6437681727, p2): under zh-CN, Setup → Positions (/_console/apps/setup/metadata/position) listed the app's positions in Chinese and the platform's seven in English.

Reader measured before writing the bundle: the page reads GET /api/v1/meta/position, whose list read (translateMetaList in packages/rest/src/meta-item-read-gate.ts) runs translateMetadataDocument('position', …) with the i18n service's merged bundle; that dispatches to translatePosition (packages/spec/src/system/i18n-resolver.ts), which reads positions.NAME.label / .description. SecurityPlugin loads SecurityTranslations into that service at kernel:ready. So the reader existed and the bundle was missing, as the card says. The built-ins reach the door as registry declarations (builtin-positions.ts, registerBuiltinPositions), keyed by name, which is what the group keys on.

Tests

Red first on main (a14f3e61): the new test, before the bundle was added: Tests 27 failed | 2 passed (29) (every per-locale case; the two population checks pass). On the fix: Tests 29 passed (29).

Package suite and typecheck at the fix

pnpm --filter @objectstack/plugin-security build && … typecheck && … test under the verify lock: VERDICT command-exit 0; Test Files 230 passed (230), Tests 4991 passed | 45 skipped (5036).
Lint: eslint packages/plugins/plugin-security --no-inline-config exit 0.

Acceptance notes

  • everyone reads 全体成员 / 全メンバー / Todos, since the description says every authenticated member holds it; guest reads 访客 / ゲスト / Invitado.
  • zh-CN org_admin (组织管理员) shares its text with the organization_admin permission set's label. They are on different pages and name the same people, so it is left as is.
  • A live probe was not run: the served overlay is proven in-process through the same translateMetadataDocument the REST door calls.

Verification

Head 352780c3 (the gate loop started on 65852aec, the same tree before a commit-message reword; git diff 65852aec 352780c3 is empty).

node scripts/pm/dispatch-gates.mjs --ran FILE --repo objectstack-ai/objectstack: 61 derived famil(ies) accounted for — 61 run, 0 NOT-MEASURED.

Three families first answered exit 3 (prerequisite, nothing measured) and were re-run after their prerequisite, each exit 0: check-plugin-teardown-shape.mjs --self-test after git fetch --unshallow origin main (48 cases pass); check:i18n and check:dual-build-cjs-loads after a full turbo run build (VERDICT command-exit 0): check-i18n-bundles: OK (10 package(s) — all bundles in sync, no undeclared authoring keys).

test:repo not run locally (the diff touches no repo-project test); CI Test Core runs it. No dogfood battery: no ! in the title and no (narrowing) arm.

Every derived family and its exit code
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/check-tenant-audit-census.mjs :: exit 0
node scripts/check-tenant-audit-census.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:i18n :: exit 0
pnpm check:i18n-stale-fill :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0

…scriptions

Setup -> Positions under zh-CN listed the seven built-in positions in
English: the plugin's bundle had no positions group. Add hand-authored
zh-CN, ja-JP and es-ES entries beside the permission sets, and a test of
the served overlay per locale.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEkQ5nLEq1iuL8pxDHXN76
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-security, touching 9 documentable anchor(s).

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

  • content/docs/kernel/runtime-services/sharing-service.mdx (via platform_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/permissions/attachments-access.mdx (via org_member (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/permissions/authentication.mdx (via platform_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/permissions/authorization.mdx (via platform_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/permissions/index.mdx (via org_member (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/permissions/permission-metadata.mdx (via org_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions), org_member (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions), org_owner (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions), platform_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/permissions/permission-sets.mdx (via org_member (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/permissions/permissions-matrix.mdx (via org_member (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/permissions/positions.mdx (via org_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions), org_member (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions), org_owner (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions), platform_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/permissions/sharing-rules.mdx (via org_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions), org_member (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions), org_owner (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions), platform_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/ui/audience-based-interfaces.mdx (via org_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))

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

  • content/docs/releases/v14.mdx (via platform_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/releases/v16.mdx (via org_member (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/releases/v17/17-4.mdx (via org_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions), platform_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/releases/v17/17-5.mdx (via org_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions), org_member (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions), org_owner (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions), platform_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/releases/v18/18-0.mdx (via org_owner (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions), platform_admin (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))
  • content/docs/releases/v18/18-1.mdx (via org_external (symbol, a field of const object esESBuiltinPositions; a field of const object jaJPBuiltinPositions; a field of const object zhCNBuiltinPositions))

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
  • 3 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 55 of 207 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 — 17 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 500bc9d5c59dc1755ab2bb0c4fc60fa2c8e447e2 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 500bc9d5c59dc1755ab2bb0c4fc60fa2c8e447e2

⚠️ 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 500bc9d5c59dc1755ab2bb0c4fc60fa2c8e447e2 → 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

席位核对 · ACCEPT · domain:server seat 1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC · 2026-10-27T10:29Z

Head 352780c30a, report 6438947641 (on #26562), claim 6438027009. No contract review is owed: there is no packages/spec/** diff, no governed path, no narrowing and no Clause-②: yes.

item reading
Shape Draft, base main, Fixes #26562. 4 files in plugin-security/src/translations/: the new hand-authored builtin-positions.ts, its test, the index.ts wiring, and a patch changeset. Clause-②: no and Metadata-protocol: none appear in both the body and the changeset.
Fix Triage 6437681727. The dev measured the reader first: GET /api/v1/meta/position runs translateMetaList, then translateMetadataDocument('position'), then translatePosition, which reads positions.<name>.label / .description from the bundle SecurityPlugin loads. So the bundle was missing, not the reader. The seven built-in positions now carry zh-CN, ja-JP and es-ES (the permission-set group's locales), wired beside permissions outside withSourceFallback, as the permission sets are. English is unchanged.
Evidence CI green on head 352780c30a (36 success, 11 skipped, 0 failed). Governed check: NOT governed, 211 changed lines. Red on main (27 of 29 failed), green at head (29 of 29). The package suite passed 4991 tests (45 skipped), and eslint and 61 of 61 derived gates exit 0. No ablation: the diff adds data only and the test is red on main.
Landing 「按照 pm-dispatch 规则复核过的都授权」 (Leehom, 2026-10-25T11:35Z): ready and auto-merge through the relay, once every check on this head is green or an expected skip.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 10:30
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 10:30
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit fb91e97 Oct 27, 2026
48 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-8fpct3 branch October 27, 2026 10:47
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

Projects

None yet

2 participants