← all branches

feat/subscription-gating

needs attentionviewing older commit
1fac5ad · incrementalPR #234reviewed 2026-07-07 02:50 UTC0H · 5M · 4L · 2I
The branch
Purpose
Gate the Batu Energy platform to bills + credentials only during soft launch. PR-207 surfaces (Sites, Assets, Tarifas, metrics) ship mounted but stay unreachable until each is ready.
Goal
Single SoftLaunchGuard + SSOT (lib/soft-launch.ts) drives both sidebar nav treatment and route-level blocking so they can never drift. Admin bypass (isPlatformAdmin) keeps internal ops working.
Sub-goals
  • SG-1: SSOT + sidebar grey-out (Sites/Tarifas as Pronto, Assets hidden, Bills/Credentials live)
  • SG-2: Central SoftLaunchGuard in (dashboard)/layout.tsx; gated → redirect to /bills/contratos
  • SG-3: Post-login landing safety — confirmed non-issue
  • SG-4: i18n locale config alignment (defaultLocale es + localePrefix always)
  • Subscription gating: feature-access decisions + require-active-subscription middleware
The changes (whole branch)
What
Merge of main into feature branch: SoftLaunchGuard integrated into layout.tsx, 17 new @batu/api exports registered, SNOOZE_REQUIRES_FUTURE_DATE error code added, spec-pointer comments cleaned up, large i18n additions for portfolio + period picker.
Why
Keep branch current with main (PR-207 electricity-data-api port) and add remaining export registrations the new domain code requires.
Areas
domains/core/src/billing+2370services/utility/bills/cfe+1462packages/api+1053apps/platform/src/components+610apps/platform/src/api+743apps/platform/src/app+92docs/development+2090
Blast
26 files, +841/−10 across branch. Core: billing decisions + subscription middleware. Platform: layout guard + API exports. No DB migrations, no CDK, no apps/web.
soft-launch gate hardcoded — reveal by code edit i18n parity drift (7 keys) guard component behavior untested
CI checks· statusCheckRollup not accessible via PATCodeRabbit· no .coderabbit.yaml present

Findings · 10

correctness2

medium

SoftLaunchGuard renders null one frame before redirect — blank flash on hard-nav to gated path

apps/platform/src/components/SoftLaunchGuard.tsx:13

isGated() true → returns null, defers router.replace() to useEffect. On hard-refresh to /sites the dashboard chrome appears with blank main for one commit cycle.

info

No isPlatformAdmin loading race confirmed

apps/platform/src/app/[locale]/(dashboard)/layout.tsx:91

Layout short-circuits while isLoading; guard only mounts after useAuthUser resolves. isPlatformAdmin is settled.

security1

info

Client-side gate is acceptable — API enforces auth independently

apps/platform/src/app/[locale]/(dashboard)/layout.tsx

SoftLaunchGuard is a UX reachability gate, not a security boundary. No sensitive data exposed in gated SSR.

conventions4

medium

@batu/metrics-domain devDependency in packages/api inverts canonical type flow

packages/api/package.json

Canonical direction: domains → packages/api → apps. Domain devDep in API layer inverts this. Move AssertEqual check to domain's own type-check.ts.

medium

SNOOZE_REQUIRES_FUTURE_DATE placed under Metrics section with inline sub-comment

packages/api/src/responses/codes.ts:29

Nested '// Triage' sub-comment inside Metrics 400-block fragments the section structure.

low

i18n drift: 6 dashboard.stats.* keys in en.json missing from es.json

apps/platform/src/messages/en.json

activeRpus, newThisMonth, generationToday, vsYesterday, monthlySavings, vsPreviousMonth absent from es.json.

low

i18n drift: bills.descargasPage.filters.statusQueued in es.json missing from en.json

apps/platform/src/messages/es.json

'En cola' present in es.json, no counterpart in en.json.

tests2

medium

SoftLaunchGuard component has no behavior test (redirect + null-render)

apps/platform/src/components/SoftLaunchGuard.tsx

Pure-function tests solid; scope.md calls out 'guard unit/behaviour tests pass'. RTL test needed.

low

DashboardSidebar navState integration untested as a component

apps/platform/src/components/DashboardSidebar.tsx

Scope.md states 'Sidebar test asserts the visible/disabled partition'. navState pure logic tested; component rendering not covered.

improvement1

medium

useEffect dependency should be [gated, router.replace], not [gated, router]

apps/platform/src/components/SoftLaunchGuard.tsx:22

Only router.replace() used inside effect; full router object is an exhaustive-deps lint issue.

History · 7 commits

  1. c807731needs attentionincremental0H · 6M · 8L2026-07-30 17:24
  2. 372882bneeds attentionincremental0H · 1M · 2L2026-07-21 17:31
  3. 1fac5adneeds attentionincremental0H · 5M · 4L2026-07-07 02:50current
  4. 99bf169safeincremental0H · 0M · 1L2026-07-04 04:01
  5. 6305b3cneeds attentionincremental0H · 1M · 4L2026-07-04 03:53
  6. f00e3caneeds attentionincremental0H · 7M · 16L2026-07-04 03:15
  7. 1ae74bdneeds attentionfull6H · 9M · 8L2026-07-04 02:36