Skip to content

Commit b1c1eba

Browse files
committed
chore(platform-objects): drop four dead apps.setup.navigation translation keys (#6660)
`nav_approval_processes`, `nav_verifications`, `nav_device_codes` and `nav_metadata` each kept a Setup nav label long after the nav item that declared them was removed. No composition renders them; a translated key with no declaring nav item is dead weight that reads as coverage — the same shape `app-nav-translation-parity.test.ts` already refuses for Studio. 14 key/label pairs across the four hand-written locale files (zh-CN never carried `nav_verifications` / `nav_device_codes`), plus a tombstone test that pins the four ids so they cannot drift back without their nav item. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W6bLax4KMrSfnE1ydFU8Dw
1 parent 61282f9 commit b1c1eba

6 files changed

Lines changed: 139 additions & 14 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
"@objectstack/platform-objects": patch
3+
---
4+
5+
chore(platform-objects): drop four dead `apps.setup.navigation` translation keys (#6660)
6+
7+
Four ids kept a Setup nav label in the hand-written locale bundles long after
8+
the nav item that declared them was removed. No composition renders them, so
9+
nothing was broken — but a translated key with no declaring nav item is the
10+
shape `app-nav-translation-parity.test.ts` already refuses for Studio: it reads
11+
as coverage. `nav_workflows` outlived its Studio menu entry in all four locales
12+
the same way, and nothing said so until that reverse assertion was written.
13+
14+
Removed, with the reason each one is gone:
15+
16+
| id | why it has no nav item |
17+
| --- | --- |
18+
| `nav_approval_processes` | the approval process engine was retired in favour of the approval flow node (#1408, ADR-0019 P4/P5) |
19+
| `nav_verifications` | `sys_verification` omits `list` from `apiMethods` |
20+
| `nav_device_codes` | `sys_device_code` likewise — both hold sensitive, ephemeral secrets, so a browse entry could only ever render "failed to load" (#2266) |
21+
| `nav_metadata` | moved to Studio as `nav_metadata_directory` when the Studio app was split out |
22+
23+
14 key/label pairs in total, not 16: `zh-CN` never carried `nav_verifications`
24+
or `nav_device_codes`.
25+
26+
Each id was checked **individually** against a repo-wide grep for a declaring
27+
`id: '<key>'` — zero hits each, against a control probe (`nav_webhooks`) that
28+
returns five. That is deliberately not the same claim as a runtime diff: from a
29+
single booted composition a dead key and a conditionally-contributed one are
30+
indistinguishable (`plugin-auth` contributes `nav_sso_providers` only when an
31+
external IdP is wired), which is why `pnpm check:app-nav-i18n` still refuses the
32+
reverse direction and why this change removes exactly four named ids rather than
33+
"everything the merged app did not declare".
34+
35+
A tombstone test pins the four so they cannot drift back in without their nav
36+
item. Re-adding `nav_verifications` / `nav_device_codes` remains a security
37+
decision — it means enabling `list` on the object first.

packages/platform-objects/src/apps/translations/en.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ export const en: TranslationData = {
8686
nav_connect_agent: { label: 'Connect an Agent' },
8787

8888
// Approvals
89-
nav_approval_processes: { label: 'Processes' },
9089
nav_approval_requests: { label: 'Requests' },
9190
nav_approval_actions: { label: 'Action History' },
9291
nav_approval_delegations: { label: 'Delegations (OOO)' },
@@ -121,13 +120,18 @@ export const en: TranslationData = {
121120
nav_datasources: { label: 'Datasources' },
122121

123122
// Advanced
123+
//
124+
// No `nav_verifications` / `nav_device_codes` here on purpose:
125+
// `sys_verification` and `sys_device_code` omit `list` from their
126+
// `apiMethods`, so `setup-nav.contributions.ts` deliberately declares
127+
// no browse entry for them (#2266). Nor `nav_metadata` — that entry
128+
// moved to Studio as `nav_metadata_directory`. Re-adding a label here
129+
// means re-adding the nav item first; the tombstone in
130+
// `setup-nav-dead-key-tombstone.test.ts` states the whole rule (#6660).
124131
nav_oauth_apps: { label: 'OAuth Applications' },
125132
nav_jwks: { label: 'Signing Keys (JWKS)' },
126-
nav_verifications: { label: 'Verifications' },
127-
nav_device_codes: { label: 'Device Codes' },
128133
nav_accounts: { label: 'Identity Links' },
129134
nav_user_preferences: { label: 'User Preferences' },
130-
nav_metadata: { label: 'All Metadata' },
131135
},
132136
},
133137
studio: {

packages/platform-objects/src/apps/translations/es-ES.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ export const esES: TranslationData = {
6363
nav_api_keys: { label: 'Claves API' },
6464
nav_connect_agent: { label: 'Conectar un agente' },
6565

66-
nav_approval_processes: { label: 'Procesos' },
6766
nav_approval_requests: { label: 'Solicitudes' },
6867
nav_approval_actions: { label: 'Historial de Acciones' },
6968
nav_approval_delegations: { label: 'Delegaciones (ausencia)' },
@@ -94,11 +93,8 @@ export const esES: TranslationData = {
9493

9594
nav_oauth_apps: { label: 'Aplicaciones OAuth' },
9695
nav_jwks: { label: 'Claves de Firma (JWKS)' },
97-
nav_verifications: { label: 'Verificaciones' },
98-
nav_device_codes: { label: 'Códigos de Dispositivo' },
9996
nav_accounts: { label: 'Enlaces de Identidad' },
10097
nav_user_preferences: { label: 'Preferencias de Usuario' },
101-
nav_metadata: { label: 'Todos los Metadatos' },
10298
},
10399
},
104100
studio: {

packages/platform-objects/src/apps/translations/ja-JP.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ export const jaJP: TranslationData = {
6363
nav_api_keys: { label: 'API キー' },
6464
nav_connect_agent: { label: 'エージェントを接続' },
6565

66-
nav_approval_processes: { label: 'プロセス' },
6766
nav_approval_requests: { label: 'リクエスト' },
6867
nav_approval_actions: { label: 'アクション履歴' },
6968
nav_approval_delegations: { label: '委任 (不在時)' },
@@ -94,11 +93,8 @@ export const jaJP: TranslationData = {
9493

9594
nav_oauth_apps: { label: 'OAuth アプリケーション' },
9695
nav_jwks: { label: '署名キー (JWKS)' },
97-
nav_verifications: { label: '検証' },
98-
nav_device_codes: { label: 'デバイスコード' },
9996
nav_accounts: { label: 'ID 連携' },
10097
nav_user_preferences: { label: 'ユーザー設定' },
101-
nav_metadata: { label: 'すべてのメタデータ' },
10298
},
10399
},
104100
studio: {
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
//
3+
// Tombstone for four dead `apps.setup.navigation` translation keys (#6660).
4+
//
5+
// ---------------------------------------------------------------------------
6+
// Why a hard-coded id list instead of the general reverse direction
7+
// ---------------------------------------------------------------------------
8+
// `app-nav-translation-parity.test.ts` asserts the reverse direction for Studio
9+
// ("a translation for an id the app no longer declares is dead weight that
10+
// reads as coverage") by walking `STUDIO_APP.navigation`. Setup cannot be
11+
// walked that way: it is a shell of empty group anchors (ADR-0029 D7) and every
12+
// entry arrives at RUNTIME, so this file has nothing to diff against — which is
13+
// exactly why that file's header says a Setup case there "has to boot
14+
// something", and why `pnpm check:app-nav-i18n` (which does boot) still refuses
15+
// the reverse direction: from one composition a dead key and a
16+
// conditionally-contributed key are indistinguishable (`nav_sso_providers` is
17+
// contributed only when an external IdP is wired). Making that gate
18+
// union-aware is tracked as #6659.
19+
//
20+
// This file makes no general claim. It pins exactly four ids that were checked
21+
// ONE BY ONE against a repo-wide grep — `id: '<key>'` returned zero hits for
22+
// each of them on `61282f906`, against a control probe (`nav_webhooks`) that
23+
// returned five — and each of which has a recorded reason to be gone:
24+
//
25+
// nav_approval_processes the process engine was retired in favour of the
26+
// approval flow node (#1408, ADR-0019 P4/P5)
27+
// nav_verifications `sys_verification` omits `list` from `apiMethods`
28+
// nav_device_codes `sys_device_code` likewise — both are sensitive,
29+
// ephemeral secrets, so a browse entry could only
30+
// ever render "failed to load" (#2266, and the
31+
// comment that records it in
32+
// `setup-nav.contributions.ts`)
33+
// nav_metadata moved to Studio as `nav_metadata_directory` when
34+
// the Studio app was split out (482eb67cc)
35+
//
36+
// ---------------------------------------------------------------------------
37+
// What to do when this test goes red
38+
// ---------------------------------------------------------------------------
39+
// It goes red on exactly one event: one of the four ids comes back. That is not
40+
// automatically wrong — re-adding `nav_verifications` or `nav_device_codes` is a
41+
// deliberate security decision (it requires enabling `list` on the object
42+
// first), and `nav_approval_processes` could return with a new owner. The rule
43+
// is the ORDER: the declaring nav item comes back first, the label second, and
44+
// the id's line is deleted from `DEAD_SETUP_NAV_IDS` in that same commit. A
45+
// label with no declaring nav item is what this tombstone exists to refuse.
46+
47+
import { describe, it, expect } from 'vitest';
48+
import { SETUP_NAV_CONTRIBUTIONS } from '../setup-nav.contributions.js';
49+
import { en } from './en.js';
50+
import { zhCN } from './zh-CN.js';
51+
import { jaJP } from './ja-JP.js';
52+
import { esES } from './es-ES.js';
53+
54+
const LOCALES = { en, 'zh-CN': zhCN, 'ja-JP': jaJP, 'es-ES': esES } as const;
55+
56+
/** Removed Setup nav ids. Delete a line here only together with its nav item. */
57+
const DEAD_SETUP_NAV_IDS = [
58+
'nav_approval_processes',
59+
'nav_device_codes',
60+
'nav_metadata',
61+
'nav_verifications',
62+
] as const;
63+
64+
describe('removed Setup nav ids stay removed (#6660)', () => {
65+
for (const [locale, data] of Object.entries(LOCALES)) {
66+
it(`${locale} carries no label for a removed Setup nav id`, () => {
67+
const nav = (data.apps?.setup?.navigation ?? {}) as Record<string, { label?: string }>;
68+
expect(
69+
DEAD_SETUP_NAV_IDS.filter((id) => id in nav),
70+
'apps.setup.navigation keys with no declaring nav item — see this file header',
71+
).toEqual([]);
72+
});
73+
}
74+
75+
// The other half of the same fact, on the one Setup contributor this package
76+
// owns. Keeping it here means a re-added nav item cannot quietly restore a
77+
// label without this ledger being read: both assertions go red together.
78+
it('SETUP_NAV_CONTRIBUTIONS declares none of them', () => {
79+
const declared = new Set<string>();
80+
const walk = (items: unknown[]): void => {
81+
for (const raw of items) {
82+
const item = raw as { id?: string; children?: unknown[] };
83+
if (item?.id) declared.add(item.id);
84+
if (Array.isArray(item?.children)) walk(item.children);
85+
}
86+
};
87+
for (const contribution of SETUP_NAV_CONTRIBUTIONS) walk(contribution.items);
88+
89+
// Control: the walk really reads this array, so an empty `declared` cannot
90+
// pass the assertion below by vacuity.
91+
expect(declared.has('nav_users'), 'nav_users is contributed here').toBe(true);
92+
expect(DEAD_SETUP_NAV_IDS.filter((id) => declared.has(id))).toEqual([]);
93+
});
94+
});

packages/platform-objects/src/apps/translations/zh-CN.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ export const zhCN: TranslationData = {
6666
nav_api_keys: { label: 'API 密钥' },
6767
nav_connect_agent: { label: '连接智能体' },
6868

69-
nav_approval_processes: { label: '审批流程' },
7069
nav_approval_requests: { label: '审批申请' },
7170
nav_approval_actions: { label: '审批历史' },
7271
// `审批委派` matches sys_approval_delegation's object label; `(外出)`
@@ -104,7 +103,6 @@ export const zhCN: TranslationData = {
104103
nav_jwks: { label: '签名密钥 (JWKS)' },
105104
nav_accounts: { label: '身份链接' },
106105
nav_user_preferences: { label: '用户偏好' },
107-
nav_metadata: { label: '全部元数据' },
108106
},
109107
},
110108
studio: {

0 commit comments

Comments
 (0)