feat/soft-launch
needs attention3709e2e · incrementalPR #263reviewed 2026-07-07 02:18 UTC0H · 3M · 3L · 3I- Purpose
- Gate the platform to bills+credentials-only soft launch: keep PR-207 surfaces (Sites, Assets, Tarifas, metrics) mounted but dark until each is ready, with a single SSOT that prevents nav/route drift.
- Goal
- Bills + Credentials are the only live modules. All other top-level modules are either greyed ('Pronto') or hidden, and their routes redirect to /bills/contratos. Platform admins bypass the gate.
- Sub-goals
- SG-1: SSOT lib/soft-launch.ts with isGated()/navState() + sidebar grey-out/hide + admin bypass
- SG-2: Single <SoftLaunchGuard> in (dashboard)/layout.tsx covers all gated routes centrally
- SG-3: Login default + bare /dashboard redirect to /bills/contratos
- Tangential (BAT-254): Fix i18n locale config self-contradiction — defaultLocale es + localePrefix always to match Supabase proxy
- What
- This incremental commit (3709e2e) folds in the BAT-254 i18n fix: routing.ts defaultLocale en→es + localePrefix as-needed→always; config.ts aligned; openapi/source.ts baseUrlFor simplified; soft-launch.ts and DocsI18nProvider.tsx comments updated. No logic changes to the soft-launch gate itself.
- Why
- The proxy was force-prefixing every request to /es regardless of the next-intl config. The old as-needed+en config was dead — English was never served prefix-less in practice. The fix makes the config match reality and prevents a future redirect loop if es were set as default with as-needed.
- Areas
- apps/platform/src/i18n+15−6apps/platform/src/lib/openapi+2−3apps/platform/src/lib/soft-launch.ts+2−2apps/platform/src/app/[locale]/(docs)+2−2.branch/scope.md+14−0apps/platform/CLAUDE.md+1−1
- Blast
- Config-only. 7 files changed, +36/-14 in this incremental window. The soft-launch gate SG-1/2/3 files (SoftLaunchGuard.tsx, DashboardSidebar.tsx, credentials/layout.tsx, dashboard layout.tsx, soft-launch.ts logic) are not touched — those were done in prior commits. No backend, no migrations, no CDK.
Findings · 9
correctness3
DashboardHeader/Sidebar fallback locale 'en' is stale — now wrong default
apps/platform/src/components/DashboardSidebar.tsx:73
DashboardHeader.tsx and DashboardSidebar.tsx extract the locale via `pathname.split('/')[1] || 'en'` using raw next/navigation usePathname(). Under localePrefix: 'always' the fallback fires only if the locale segment is absent — which the proxy prevents in practice — but if it does fire, navigation lands on /en/... instead of /es/.... Not introduced by this diff but exposed by making 'en' the wrong fallback.
ADR-006 'English-First' header in routing.ts now contradicts config
apps/platform/src/i18n/routing.ts:4
Same issue as in config.ts — the JSDoc block at the top of routing.ts still says 'Per ADR-006: Internationalization with English-First Approach'. No behavioral impact but misleading to future readers.
In-memory docs source cache won't invalidate if locale config changes
apps/platform/src/lib/openapi/source.ts:88
The module-level cache Map is never invalidated. Not a regression from this diff — the new baseUrlFor is simpler and correct. But the cache means a future locale config change would serve stale baseUrls until process restart.
conventions1
File-level ADR-006 header contradicts new defaultLocale in config.ts and routing.ts
apps/platform/src/i18n/config.ts:4
The file header says 'Per ADR-006: Internationalization with English-First Approach' and '- English as source language' with no qualification, but the module now exports defaultLocale: 'es'. A reader skimming the file top-down sees 'English-First' then immediately hits a Spanish default. The ADR itself may specify defaultLocale: 'en' in examples, risking a future developer reverting this as an 'ADR violation'. Should note the distinction: English as translation source language; Spanish as routing default per proxy mechanics (BAT-254).
tests4
No unit test for baseUrlFor — old conditional was silently broken, no guard for regressions
apps/platform/src/lib/openapi/source.ts:84
The old baseUrlFor had a branch: `locale === routing.defaultLocale ? '/developers' : '/${locale}/developers'`. Once defaultLocale changed to 'es', it would have returned the un-prefixed '/developers' for Spanish — silently wrong. No test caught it. The simplification to `/${locale}/developers` is correct, but a two-line unit test (baseUrlFor('es') === '/es/developers', baseUrlFor('en') === '/en/developers') would lock this in.
docs-route-gate tests only exercise /es/developers — /en/developers path untested
apps/platform/src/lib/supabase/__tests__/docs-route-gate.test.ts:35
Both test cases pass /es/developers. With localePrefix: 'always', /en/developers is equally valid. A third case with /en/developers (anonymous → redirect to /en/login) would confirm locale-stripping logic is locale-agnostic.
No proxy test for locale-less /developers — 'always' prefix invariant unverified at middleware
apps/platform/src/lib/supabase/__tests__/docs-route-gate.test.ts:31
Under localePrefix: 'always', a bare /developers should be redirected to /es/developers by the proxy before auth fires. No test exercises this. A test for req('/developers') verifying redirect to /es/developers would protect against proxy reordering.
Three-way defaultLocale invariant (config.ts / routing.ts / proxy.ts) has no machine check
apps/platform/src/i18n/config.ts:17
Three files independently declare the default locale as 'es' with no import relationship. BAT-254 was exactly this drift. A one-line unit test importing all three and asserting equality would make the invariant machine-checked.
improvement1
proxy.ts duplicates locale constants instead of importing from i18n/config.ts
apps/platform/src/lib/supabase/proxy.ts:17
proxy.ts defines its own DEFAULT_LOCALE = 'es' and type SupportedLocale = 'en' | 'es' independently. routing.ts comment now explicitly warns 'This must match that proxy' — that warning exists because there's no compile-time enforcement. Importing defaultLocale and Locale from @/i18n/config would make them consistent by construction.