Menu

#31 fix(auth): break E2E refresh-rotation cascade + trace artifact rule + throttle-limit unmasking

open
nobody
None
5 days ago
2026-08-29
Anonymous
No

Originally created by: AshishGTH

Summary

The E2E refresh-rotation cascade documented in CLAUDE.md as
"overridden three times in one session" is fixed at the source, and
the merge gate that let those overrides land without cost now
self-audits. See CLAUDE.md's new
"E2E refresh-rotation cascade — fixed at the source" Decisions entry
for the full account; short version:

Root fix (auth cascade)apps/web/src/lib/auth.tsx and
apps/portal/src/lib/auth.tsx's AuthProvider mount effect now
consults a per-app sessionStorage cooldown (_authCacheStaff,
_authCachePortal) before firing /auth/refresh; if a successful
refresh landed within 5s, it hydrates state.user from the cached
JWT PAYLOAD (never the raw token — Phase 1's rule protects the raw
bearer, not the claims) and skips the refresh call entirely. The
cascade never forms because the aborted calls never fire.
api()'s 401-retry gate is relaxed to fire without a prior
in-memory accessToken so a cooldown-skipped mount's first API
call still works. REFRESH_REUSE_GRACE_SECONDS default bumped
30→60 as defence in depth.

CI trace-artifact rule.github/workflows/ci.yml's
e2e-playwright job now asserts apps/e2e/test-results/ is
non-empty on failure and uploads the traces as a
playwright-traces artifact. Bypassing the merge gate without
reading the trace now has a structural cost. The rule paid for
itself on its first real run — one trace pull diagnosed what nine
prior CI runs of pattern-matching could not.

Downstream unmasking (four commits chased three residual
mechanisms once the cascade stopped masking them)

  • DEFAULT_THROTTLE_LIMIT env override for the harness's webServer
    (playwright.config.ts + app.module.ts): four of five originally-
    failing specs (ticket-reply, successful-to-booking,
    role-permission-edit, user-role-edit) hit 429s on /auth/refresh,
    a shared per-IP default-throttle bucket exhausted under 4-way
    Playwright parallelism from one runner IP.
  • user-deactivate-reactivate DOM race fix: mutation returned but
    the row's re-render after invalidateQueries(['users'])'s
    refetch didn't fit in Playwright's default 5s toBeVisible
    timeout; fixed by waiting for the refetch response explicitly
    before asserting on the DOM (matches CLAUDE.md's own
    booking-wizard-option-count pattern).
  • DEFAULT_THROTTLE_LIMIT for the api test suite too
    (apps/api/vitest.config.ts's test.env): same throttle
    mechanism blocked six tests across three integration-test files
    once the E2E job started passing.

HousekeepingAGENTS.md deleted: it was a manual mirror of
CLAUDE.md that had drifted four entries behind (including the
entry my new Decisions supersedes). Deleting is safer than syncing
— the drift is proven to recur, nothing in the repo consumes
AGENTS.md, and a stale file at a conventional path is worse than
none.

CI proof

Three consecutive green full-suite runs on one commit (9bbae76),
attempts 1/2/3 of run
33292860530,
all five jobs success on every attempt:

Job Attempt 1 Attempt 2 Attempt 3
Lint, typecheck, unit tests, build 2m38s 2m39s 2m34s
Integration tests (Postgres + Redis) 4m33s 4m45s 3m42s
E2E (Playwright + built frontend) 2m56s 3m3s 3m2s
Native install 3m12s 2m31s 3m0s
Native upgrade 3m50s 3m10s 2m41s

Cascade fix confirmed by trace evidence, not just boolean-green:
team-scope and rapid-reload-session (the two most login-heavy
specs, exactly the shape the cascade would fail first) ran green
on every attempt. Zero traces across the three attempts show a
browser parked on /login mid-test.

Test plan

  • [x] Local pnpm typecheck clean across apps/web, apps/portal, apps/api
  • [x] Local pnpm lint clean same
  • [x] Local pnpm test clean for apps/web (8/8) and apps/portal (8/8) including the new _authCache regression suites
  • [x] Local pnpm build clean for both frontends
  • [x] CI three-consecutive-green bar met on commit 9bbae76
  • [x] Fourth full-suite green on docs-amendment commit 76bc4ec (all 5 jobs)

Known remaining

  • cheque-bounce.spec.ts:70:72 observed once (attempt 1, retry-recovered) — a spec this PR did not touch, not in the original failing subset. Logged in docs/todo.md with the exact assertion, exact run/commit for pulling the trace, and an explicit warning against widening retries: 1 to hide it further.

🤖 Generated with Claude Code

Discussion


Log in to post a comment.