Skip to content

fix(gui-app): survive auth changes during the router's initial load + hold TanStack router at pre-refresh pins - #1136

Merged
pranshugupta54 merged 4 commits into
mainfrom
fix-router-initial-load-auth
Aug 12, 2026
Merged

pranshugupta54 merged 4 commits into
mainfrom
fix-router-initial-load-auth

Conversation

@pranshugupta54

Copy link
Copy Markdown
Member

What

Two commits, one failure mode: fresh mobile installs of the Capacitor app booted to a permanently blank screen.

  1. Guard (bindAuthInvalidation) — on cold launch, the auth status flips (stored tokens validating) while the router's initial load is still uncommitted. On router-core ≥1.171.16, invalidate() in that window retires the in-flight load and nothing reschedules it: status: "pending", zero committed matches, forever — no error anywhere. The binding now detects the uncommitted window via resolvedLocation (matches is not a commit signal — slow loads publish provisional pending matches after defaultPendingMs) and routes the auth change through router.load() first, invalidating once it settles; recoveries are coalesced to one per window since load() aborts its predecessor transaction. Behavior on the held router version is unchanged (recheck lands a few ms later).
  2. Version hold — the bump that exposed this (chore: refresh bun dependencies #1109: react-router 1.170.18→.25, router-core 1.171.15→.21) crosses router-core's lane-scheduler rewrite, which is still churning upstream (1.171.22 shipped another ~1600-line pass over the same area yesterday). Held back to the last field-proven set: react-router@1.170.18 (exact; pins core 1.171.15 exactly) + router-plugin@1.168.23 (exact; newer plugin requires ^1.170.25 and splits the resolution into two router-core copies). The history-prune-provider adaptation from chore: refresh bun dependencies #1109 is restored to match. The guard stays regardless — it makes the pattern safe on any version, including future re-bumps.

Verification

  • 10 unit tests + 1 integration test mounting a real RouterProvider against the pinned library (slow uncommitted first load past pendingMs → real auth flip → converges; fails without the guard).
  • iOS simulator, Release staging build, cold launches: pre-fix mostly blank; post-fix 12/12 render across three install modes — upgrade-in-place, reinstall with surviving Keychain tokens (the exact reported death mode: auto-sign-in mid-boot), and never-signed-in.
  • Adversarial review by a GPT-5.6-Sol agent: two findings in the first pass (false commit signal via matches; un-coalesced recovery fan-out) — both fixed and re-verified; final pass clean on auth flows, redirect loops, deep links, desktop multi-window, and hydration paths.

Notes

  • Root-caused end-to-end in the simulator (fiber-probe of the stuck router; manual load() revived it).
  • The mobile-app branch carries the same guard commit; it will converge on sync after this merges.

Cc @hdkshingala

On router-core >= 1.171.16, invalidate() during a load retires the active
transaction. When that load is the very first one (no resolution has ever
completed), nothing reschedules it: the router stays pending with no
committed matches and the app renders a permanently blank screen. Mobile
cold launches hit this window on nearly every fresh install, where the
stored-token validation flips auth mid initial load.

bindAuthInvalidation now detects the uncommitted window via
resolvedLocation (matches is not a commit signal: slow loads publish
provisional pending matches after defaultPendingMs) and routes the auth
change through router.load() first, invalidating once the load settles.
Recoveries are coalesced to one per uncommitted window since load() aborts
its predecessor transaction rather than joining it.

Signed-off-by: Pranshu Gupta <76090263+pranshugupta54@users.noreply.github.com>
…ions

The 1.170.18 -> 1.170.25 range (router-core 1.171.15 -> 1.171.21) contains
router-core's lane-scheduler rewrite, which is still churning upstream
(1.171.22 landed another ~1600-line pass over the same area). Its
invalidation-retires-active-work semantics orphaned the router's initial
load on mobile cold launches. The bindAuthInvalidation guard neutralizes
that specific defect, but the rewrite is too unsettled to ship on; hold
the family at the last field-proven set until a routine dependency
refresh can revalidate it:

- @tanstack/react-router 1.170.18 (exact - carries router-core 1.171.15
  as an exact transitive pin)
- @tanstack/router-plugin 1.168.23 (exact - its newer releases require
  react-router ^1.170.25, which would split the resolution into two
  router-core copies)

Also restores the isTransitioning check in history-prune-provider that
was adapted away when the newer router dropped that state field.

Signed-off-by: Pranshu Gupta <76090263+pranshugupta54@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9f74251b-123d-40b8-884e-40bd32c68d46

📥 Commits

Reviewing files that changed from the base of the PR and between 0ca0f22 and 89dfc89.

📒 Files selected for processing (2)
  • clients/gui-app/src/__tests__/router-auth-invalidation.test.ts
  • clients/gui-app/src/router.tsx

Summary by CodeRabbit

  • Bug Fixes

    • Improved authentication handling during initial navigation to prevent stale or incomplete router states.
    • Ensured authentication changes recover correctly after successful or failed loading.
    • Consolidated rapid authentication updates to avoid redundant refreshes.
    • Improved detection of in-progress navigation for more reliable history handling.
  • Tests

    • Added integration and regression coverage for authentication changes during initial loading, recovery, error handling, concurrent updates, and subscription cleanup.

Walkthrough

The router now recovers unresolved initial loads before invalidation, coalesces auth changes, and invalidates after recovery settles. Tests cover unit and real-router flows. Navigation pruning detects transitioning states, and TanStack Router versions are pinned.

Changes

Auth invalidation recovery

Layer / File(s) Summary
Router recovery and navigation-state handling
clients/gui-app/src/router.tsx, clients/gui-app/src/providers/history-prune-provider.tsx
The router contract exposes status, resolved location, load(), and invalidate(). Unresolved initial loads recover through one coalesced load before invalidation. History pruning detects transitioning navigation.
Auth invalidation behavior tests
clients/gui-app/src/__tests__/router-auth-invalidation.test.ts
Tests cover recovery, direct invalidation, coalescing, recovery reuse, rejection handling, unchanged auth, and unsubscribe behavior.
Cold-launch integration validation
clients/gui-app/src/__tests__/router-auth-invalidation.integration.test.tsx
A real TanStack Router test validates auth recovery during an uncommitted initial load and confirms one post-settle invalidation.

TanStack Router version pinning

Layer / File(s) Summary
Exact router package versions
package.json
The TanStack React Router and router plugin catalog entries use exact versions.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AuthStore
  participant bindAuthInvalidation
  participant TanStackRouter
  participant RouterProvider
  AuthStore->>bindAuthInvalidation: auth transition
  bindAuthInvalidation->>TanStackRouter: inspect pending state
  bindAuthInvalidation->>TanStackRouter: call load()
  TanStackRouter-->>RouterProvider: settle initial route
  bindAuthInvalidation->>TanStackRouter: call invalidate()
Loading

Possibly related PRs

Poem

I twitch my nose at routes in flight,
Then load the path before rewrite.
Auth hops merge in one neat stream,
Settled states invalidate clean.
The router rests; I nibble greens.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the auth-invalidation fix and TanStack Router version pinning.
Description check ✅ Passed The description directly explains the blank-screen failure, implementation, version pins, tests, and verification results.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-router-initial-load-auth

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@clients/gui-app/src/__tests__/router-auth-invalidation.integration.test.tsx`:
- Around line 56-67: Replace the local waitUntil helper and fixed timeout in the
router authentication integration test with Testing Library waitFor, waiting on
the relevant router state conditions. Import act and wrap both
useAuthStore.setState calls, including the gate release, so synchronous router
updates and mounted RouterProvider renders are flushed within React’s act
environment; retain the existing assertions and convergence behavior.
- Line 160: Register the test teardown immediately after
bindAuthInvalidation(adapter) creates unsubscribe, using the repository’s
standard automatic cleanup mechanism to invoke unsubscribe regardless of
assertion outcomes. Remove the success-path-only unsubscribe call while
preserving the existing unmount cleanup behavior.

In `@clients/gui-app/src/__tests__/router-auth-invalidation.test.ts`:
- Around line 12-19: Update the version reference in the documentation comment
above bindAuthInvalidation to match the pinned `@tanstack/react-router` version,
1.170.18, or another explicitly verified range. Leave the described router-state
behavior unchanged.
- Around line 33-37: Update the comments in
clients/gui-app/src/__tests__/router-auth-invalidation.test.ts:33-37 to describe
recovery coalescing as per-binding closure state from bindAuthInvalidation, not
module-scoped state. In
clients/gui-app/src/__tests__/router-auth-invalidation.integration.test.tsx:106-119,
remove the claim that state.resolvedLocation is required and the
always-present-key justification, while preserving the adapter and spy
justification.

In `@clients/gui-app/src/providers/history-prune-provider.tsx`:
- Around line 102-109: Bound the retry loop in flush when isRouterLoadInFlight
reports true, adding a termination or abort path after a finite number of
requestAnimationFrame retries while preserving normal pruning once the router
becomes idle. Add coverage for a persistently in-flight router state to verify
retries stop and no permanent animation-frame loop remains.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5ee4039b-e4b0-4794-931a-df38631b2720

📥 Commits

Reviewing files that changed from the base of the PR and between 777ad89 and ab07274.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • clients/gui-app/src/__tests__/router-auth-invalidation.integration.test.tsx
  • clients/gui-app/src/__tests__/router-auth-invalidation.test.ts
  • clients/gui-app/src/providers/history-prune-provider.tsx
  • clients/gui-app/src/router.tsx
  • package.json

Comment thread clients/gui-app/src/__tests__/router-auth-invalidation.integration.test.tsx Outdated
Comment thread clients/gui-app/src/__tests__/router-auth-invalidation.test.ts Outdated
Comment thread clients/gui-app/src/__tests__/router-auth-invalidation.test.ts Outdated
Comment thread clients/gui-app/src/providers/history-prune-provider.tsx
- drive the mounted-router assertions through Testing Library's waitFor
  and wrap store writes in act() instead of hand-rolled timers
- register unsubscribe via onTestFinished so a failed assertion cannot
  leak the auth listener into the unmounted tree
- correct the version cite (behavior observed on 1.170.25; repo pins
  1.170.18) and two stale doc claims (recovery is per-binding closure
  state; resolvedLocation is optional on the interface)

Signed-off-by: Pranshu Gupta <76090263+pranshugupta54@users.noreply.github.com>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 12, 2026
Signed-off-by: Pranshu Gupta <76090263+pranshugupta54@users.noreply.github.com>
@pranshugupta54
pranshugupta54 merged commit ed5388e into main Aug 12, 2026
20 checks passed
@pranshugupta54
pranshugupta54 deleted the fix-router-initial-load-auth branch August 12, 2026 20:59
hdkshingala added a commit that referenced this pull request Aug 13, 2026
## Summary

- Refreshes compatible open-source Bun dependencies and regenerates the
lockfile, including TipTap 3.30.1, Electron 42.9.0, PostHog 1.417.0,
shadcn 4.17.0, and supporting UI/tooling patches.
- Preserves the exact TanStack Router versions restored by #1136 after
the mobile cold-launch blank-screen regression; neither direct
dependency nor the transitive router-core pin changes in this refresh.

## Still held after audit

- `@tanstack/react-router` remains exactly `1.170.18`,
`@tanstack/router-plugin` remains exactly `1.168.23`, and React Router
continues to pin `@tanstack/router-core` at `1.171.15`. They must move
together only after the initial-load scheduler regression is proven
fixed.
- Electron 43, Nx 23, ESLint 10, TypeScript 7, jsdom 30, motion 13, and
undici 8 remain deliberate major-version holds.
- Exact Sentry, Radix, Pierre, Legend, Dagre, and related compatibility
pins remain unchanged.

## Verification

- Bun 1.3.12 install and `bun install --frozen-lockfile` passed with no
lockfile changes.
- `bun outdated --recursive --no-progress` has no remaining eligible OSS
rows; every reported delta is a deliberate hold.
- `git diff --check` passed, with no TanStack Router-family changed
lines in either the manifest or lockfile.
- The commit-time canonical pre-commit hook passed affected build,
compile, lint, format, JSON, secret, and DCO checks.

Signed-off-by: Hardik Shingala <hardik@traycer.ai>
hdkshingala added a commit that referenced this pull request Aug 14, 2026
## Summary

- update `posthog-js` from `^1.417.0` to `^1.417.1`
- update `shadcn` from `^4.17.0` to `^4.18.0`
- refresh the Bun lockfile

## Still held after audit

- keep `@tanstack/react-router` at exact `1.170.18` and
`@tanstack/router-plugin` at exact `1.168.23`; #1136 reverted their
prior update after a permanent blank screen on mobile cold launch
- keep the corresponding reverted router-core/history resolutions
unchanged
- leave out-of-range majors and deliberate exact/vendor pins unchanged

## Verification

- `bun install --frozen-lockfile`
- `bun outdated` audit
- final `bun install` reported no changes
- `git diff --check`
- pre-commit affected build, compile, lint, and format checks

Signed-off-by: Hardik Shingala <hardik@traycer.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant