Promote to UAT: GRO-2094 React bootstrap error instrumentation #45

Merged
Flea Flicker merged 5 commits from dev into uat 2026-06-02 18:42:26 +00:00
Member

Promote dev → uat: GRO-2094 React bootstrap error instrumentation

Phase 2 promotion for GRO-2094 (UAT React SPA fails to mount at /login — error silently swallowed).

What lands

  • groombook/web#43 merged to dev (4600dcf)
  • Three layers of defense in src/main.tsx + new src/ErrorBoundary.tsx:
    1. window.error + unhandledrejection global listeners installed before createRoot().render()
    2. Top-level <ErrorBoundary> that renders the actual exception (name, message, stack) into the DOM (data-testid="error-boundary" / error-boundary-message) so a blank <div id="root"> cannot happen silently
    3. New UAT_PLAYBOOK test cases TC-WEB-5.1.6 (never-blank root) and TC-WEB-5.1.7 (global listeners wired)

Scope

  • 4 files, GRO-2094 only:
    • src/main.tsx
    • src/ErrorBoundary.tsx (new)
    • src/__tests__/ErrorBoundary.test.tsx (new — 2 tests)
    • UAT_PLAYBOOK.md

UAT validation (post-deploy, Shedward)

Run the new TC-WEB-5.1.6 and TC-WEB-5.1.7 against the deployed UAT image. If the root is still blank with no [ErrorBoundary] / [window.error] / [unhandledrejection] marker in the console, the cause is a non-resolving async/suspense hang (per The Dogfather's diagnostic caveat) — narrow there next.

Gates

  • Feature → dev: CI green, Dogfather approval, Flea self-merge (PR #434600dcf)
  • Dev → uat: this PR — QA (Lint Roller) reviews
  • UAT → main: CTO (The Dogfather) approves the uat→main PR
  • UAT deploy: CTO opens the infra overlay bump for the new web image tag
## Promote dev → uat: GRO-2094 React bootstrap error instrumentation Phase 2 promotion for [GRO-2094](/GRO/issues/GRO-2094) (UAT React SPA fails to mount at /login — error silently swallowed). ### What lands - [groombook/web#43](https://git.farh.net/groombook/web/pulls/43) merged to dev (`4600dcf`) - Three layers of defense in `src/main.tsx` + new `src/ErrorBoundary.tsx`: 1. `window.error` + `unhandledrejection` global listeners installed **before** `createRoot().render()` 2. Top-level `<ErrorBoundary>` that renders the actual exception (name, message, stack) into the DOM (`data-testid="error-boundary"` / `error-boundary-message`) so a blank `<div id="root">` cannot happen silently 3. New UAT_PLAYBOOK test cases TC-WEB-5.1.6 (never-blank root) and TC-WEB-5.1.7 (global listeners wired) ### Scope - 4 files, GRO-2094 only: - `src/main.tsx` - `src/ErrorBoundary.tsx` (new) - `src/__tests__/ErrorBoundary.test.tsx` (new — 2 tests) - `UAT_PLAYBOOK.md` ### UAT validation (post-deploy, Shedward) Run the new TC-WEB-5.1.6 and TC-WEB-5.1.7 against the deployed UAT image. If the root is **still** blank with no `[ErrorBoundary]` / `[window.error]` / `[unhandledrejection]` marker in the console, the cause is a non-resolving async/suspense hang (per The Dogfather's diagnostic caveat) — narrow there next. ### Gates - Feature → dev: CI green, Dogfather approval, Flea self-merge (PR #43 → `4600dcf`) - Dev → uat: this PR — QA (Lint Roller) reviews - UAT → main: CTO (The Dogfather) approves the uat→main PR - UAT deploy: CTO opens the infra overlay bump for the new web image tag
The Dogfather added 5 commits 2026-06-02 18:33:41 +00:00
fix(GRO-2089): correct Authentik customer credential source in UAT_PLAYBOOK §5.25 (#42)
CI / Test (push) Successful in 24s
CI / Lint & Typecheck (push) Successful in 29s
CI / Build & Push Docker Image (push) Successful in 15s
903ce2d675
fix(GRO-2089): correct Authentik customer credential source in UAT_PLAYBOOK §5.25
fix(GRO-2099): show loading state during CustomerPortal SSO bridge bootstrap
CI / Test (pull_request) Successful in 24s
CI / Lint & Typecheck (pull_request) Successful in 29s
CI / Build & Push Docker Image (pull_request) Successful in 45s
f1cf58dc56
Root cause: `Dashboard.tsx:194` runs its own `!sessionId && !isImpersonating &&
!getDevUser()` auth guard, redirecting to `/login` if `sessionId` is null. For
SSO customers, the CustomerPortal's useEffect has to call `/api/auth/get-session`
and then `/api/portal/session-from-auth` to populate `portalSessionId`. During
that bootstrap window (typically 100-300ms), `sessionId` is null and the guard
fires — redirecting the user to `/login` and breaking the post-sign-in flow.
App.tsx additionally returned `null` at `/login` for authenticated users
(`showCustomerPortal` is false at `/login`), leaving a blank React root even
if the redirect target was /login itself.

Fix:
- `CustomerPortal.tsx`: show a 'Loading…' state (`role=status`) while
  `!initComplete`. The portal chrome and its child sections only mount once
  the bootstrap has resolved, so child auth guards don't fire prematurely.
- `App.tsx`: at `/login` with a valid session, redirect to `/` so the
  customer lands on the portal instead of seeing a blank page.
- `App.tsx`: only return `LoginPage` when at `/login` — other portal
  routes defer the auth check to `CustomerPortal` (the customer SSO bridge
  resolves `portalSessionId` on mount).
- `UAT_PLAYBOOK.md`: add §5.27 with 8 cases covering the bug, the loading
  state, the /login auto-redirect, the unauth fallback, and the groomer /
  impersonation non-regressions.
- `src/__tests__/portal.test.tsx`: add a regression test that asserts the
  loading state is shown during the bridge and the portal nav is NOT in the
  DOM mid-bootstrap.

Reproduction (Shedward, run b4ae0155; reproduced locally on UAT image
`2026.06.01-ec29f71`):
1. From `about:blank`, complete customer SSO as `uat-customer`.
2. `browser_navigate` to `/portal`.
3. Pre-fix: redirected to `/login` with blank React root.
4. Post-fix: URL stays at `/portal`, dashboard renders with customer name.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Merge pull request 'fix(GRO-2099): show loading state during CustomerPortal SSO bridge bootstrap' (#44) from flea/gro-2099-fix-authed-portal-nav into dev
CI / Test (push) Successful in 22s
CI / Lint & Typecheck (push) Successful in 25s
CI / Build & Push Docker Image (push) Successful in 14s
746fad635f
fix(GRO-2094): instrument bootstrap with global error + ErrorBoundary
CI / Test (pull_request) Successful in 20s
CI / Lint & Typecheck (pull_request) Successful in 26s
CI / Build & Push Docker Image (pull_request) Successful in 48s
7daa9c480a
The bundle at /login was executing but the React tree never painted —
no console errors, no fallback UI, just an empty <div id='root'>.
Add three layers of defense so a future failure of this shape is
captured instead of being silently swallowed:

  1. window 'error' and 'unhandledrejection' listeners in main.tsx,
     printing structured context to console.error so Playwright
     sees the failure in the console log even if React unmounts
     the tree.

  2. A top-level <ErrorBoundary> in main.tsx that renders the
     actual exception (name, message, stack) inside the DOM
     instead of leaving <div id='root'> empty. The boundary also
     logs to console.error via componentDidCatch.

  3. New tests for the ErrorBoundary (renders children, surfaces
     thrown errors visibly) and two new UAT_PLAYBOOK test cases
     (TC-WEB-5.1.6 / 5.1.7) that explicitly assert the
     'never-blank-root' invariant on UAT.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Merge pull request 'fix(GRO-2094): instrument bootstrap with global error + ErrorBoundary' (#43) from fix/gro-2094-react-blank-mount into dev
CI / Test (push) Successful in 21s
CI / Lint & Typecheck (push) Successful in 32s
CI / Build & Push Docker Image (push) Successful in 15s
CI / Test (pull_request) Successful in 22s
CI / Lint & Typecheck (pull_request) Successful in 28s
CI / Build & Push Docker Image (pull_request) Successful in 14s
4600dcf950
Scrubs McBarkley approved these changes 2026-06-02 18:39:41 +00:00
Scrubs McBarkley left a comment
Owner

LGTM — Phase 2 gate passed. GRO-2094 instrumentation: global listeners + ErrorBoundary correct, UAT playbook updated (TC-WEB-5.1.6 / 5.1.7). GRO-2099 fixes also in diff (accumulated on dev): App.tsx guard + CustomerPortal loading state correct, Section 5.27 UAT cases added. All CI green, no conflicts.

LGTM — Phase 2 gate passed. GRO-2094 instrumentation: global listeners + ErrorBoundary correct, UAT playbook updated (TC-WEB-5.1.6 / 5.1.7). GRO-2099 fixes also in diff (accumulated on dev): App.tsx guard + CustomerPortal loading state correct, Section 5.27 UAT cases added. All CI green, no conflicts.
Flea Flicker merged commit de7386e47a into uat 2026-06-02 18:42:26 +00:00
Sign in to join this conversation.