Found while implementing #5750 (PR closing that card adds pnpm check:app-nav-i18n, a gate that boots the real composition and asserts every merged Setup nav id carries a label in every locale).
The gap
@objectstack/plugin-auth contributes a nav_sso_providers entry into the Setup app's group_access_control:
packages/plugins/plugin-auth/src/auth-plugin.ts:552
id: 'nav_sso_providers', type: 'object', label: 'SSO Providers', objectName: 'sys_sso_provider', ...
No locale file carries a label for it. Measured on origin/main 487a197b5:
$ for f in en zh-CN ja-JP es-ES; do
printf '%-8s %s\n' "$f" "$(grep -c nav_sso_providers packages/platform-objects/src/apps/translations/$f.ts)"
done
en 0
zh-CN 0
ja-JP 0
es-ES 0
Control probe on the same four files with an id that IS translated (nav_positions) returns 1 each, so the zeros are real and not a bad path.
Consequence is the same one #5750 reported for four other ids: a deployment with an external IdP wired shows SSO Providers in English in an otherwise translated Setup menu. It is narrower than #5750's four only because it is gated.
Why the new gate does not catch it
The contribution is conditional — auth-plugin.ts spreads navigationContributions in only when this.authManager.isSsoWired() is true (self-host OS_SSO_ENABLED, or the cloud per-environment planAllowsSso arriving via plugins.sso). check:app-nav-i18n judges ONE composition, and a contribution that is gated off in that composition is not merged, so it is not judged. The gate's header states this bound explicitly rather than implying full coverage.
So this is not a bug in that gate — it is the honest edge of what one runtime composition can answer, and this issue is the concrete instance sitting just past it.
Two ways to close it
- Add the four labels and leave the gate's bound where it is. Cheapest; the entry stays unverified by CI, so the next conditional contribution can repeat this.
- Teach
check:app-nav-i18n to enumerate conditional contributions too — e.g. boot a second composition with the gating conditions forced on, or have contributors declare their conditional nav statically so a gate can read the union rather than one realisation. This is the general fix and is what would make "every declared Setup nav id is translated" a claim CI can actually stand behind.
Option 2 changes what plugins are expected to declare, so it is a maintainer call rather than something to pick in passing. Filed unassigned for triage.
Related: #5750 (the four labels that were visible), and the sibling finding on dead apps.setup.navigation keys.
Found while implementing #5750 (PR closing that card adds
pnpm check:app-nav-i18n, a gate that boots the real composition and asserts every merged Setup nav id carries a label in every locale).The gap
@objectstack/plugin-authcontributes anav_sso_providersentry into the Setup app'sgroup_access_control:No locale file carries a label for it. Measured on
origin/main487a197b5:Control probe on the same four files with an id that IS translated (
nav_positions) returns 1 each, so the zeros are real and not a bad path.Consequence is the same one #5750 reported for four other ids: a deployment with an external IdP wired shows
SSO Providersin English in an otherwise translated Setup menu. It is narrower than #5750's four only because it is gated.Why the new gate does not catch it
The contribution is conditional —
auth-plugin.tsspreadsnavigationContributionsin only whenthis.authManager.isSsoWired()is true (self-hostOS_SSO_ENABLED, or the cloud per-environmentplanAllowsSsoarriving viaplugins.sso).check:app-nav-i18njudges ONE composition, and a contribution that is gated off in that composition is not merged, so it is not judged. The gate's header states this bound explicitly rather than implying full coverage.So this is not a bug in that gate — it is the honest edge of what one runtime composition can answer, and this issue is the concrete instance sitting just past it.
Two ways to close it
check:app-nav-i18nto enumerate conditional contributions too — e.g. boot a second composition with the gating conditions forced on, or have contributors declare their conditional nav statically so a gate can read the union rather than one realisation. This is the general fix and is what would make "every declared Setup nav id is translated" a claim CI can actually stand behind.Option 2 changes what plugins are expected to declare, so it is a maintainer call rather than something to pick in passing. Filed unassigned for triage.
Related: #5750 (the four labels that were visible), and the sibling finding on dead
apps.setup.navigationkeys.