Skip to content

fix: persist active workspace and redirect on session/workspace mismatch#238

Open
igorcesarcode wants to merge 2 commits into
compozy:mainfrom
igorcesarcode:fix/web-workspace-session-routing
Open

fix: persist active workspace and redirect on session/workspace mismatch#238
igorcesarcode wants to merge 2 commits into
compozy:mainfrom
igorcesarcode:fix/web-workspace-session-routing

Conversation

@igorcesarcode
Copy link
Copy Markdown

@igorcesarcode igorcesarcode commented May 29, 2026

Summary

  • Persists selectedWorkspaceId in localStorage via Zustand persist middleware so that direct navigation to a session URL (e.g. after a page refresh or sharing a link) resolves the correct workspace instead of falling back to the first one in the list.
  • Auto-sets workspace on first load: when selectedWorkspaceId is null, the first available workspace is automatically selected and persisted, avoiding the case where the fallback query.data[0] changes silently across refreshes.
  • Redirects immediately on workspace/session mismatch: a new useSessionWorkspaceGuard hook detects when the active workspace changes to one that does not own the current session and navigates to /agents/$name instantly — without waiting for TanStack Query's multi-second retry exhaustion on the 404.

Root cause

useActiveWorkspaceStore had no persistence — selectedWorkspaceId reset to null on every page load. useActiveWorkspace silently fell back to query.data[0], which could be a different workspace. When navigating directly to a session URL, the wrong workspace was used to fetch the session, so the transcript query was either fetching from the wrong workspace (empty result) or was disabled entirely.

Test plan

  • Navigate directly to a session URL after a page refresh — transcript should load correctly with the persisted workspace
  • Switch workspace while on a session that belongs to a different workspace — should redirect to /agents/$name immediately (< 500 ms), not after a 7+ second retry timeout
  • make web-lint passes (zero oxlint warnings)
  • bunx turbo run typecheck --filter=./web passes

Summary by CodeRabbit

  • New Features

    • Workspace selection is now persisted across browser sessions for faster access.
    • Session workspace validation ensures sessions belong to the correct workspace, with automatic redirection when needed.
  • Bug Fixes

    • Enhanced workspace selection logic to properly handle invalid selections and automatically default to available workspaces.

Review Change Stack

Persists the selected workspace ID in localStorage so that direct
navigation to a session URL (e.g. after a page refresh) resolves
the correct workspace without falling back to the first available one.

- Add Zustand persist middleware to use-active-workspace-store with
  key "agh:active-workspace" and partialize on selectedWorkspaceId
- Auto-set selectedWorkspaceId to the first workspace when null so
  the selection is immediately persisted on first load
- Add useSessionWorkspaceGuard hook that detects workspace/session
  mismatch and navigates immediately to /agents/$name when the active
  workspace changes to one that does not own the current session,
  avoiding a multi-second wait for TanStack Query retry exhaustion
@vercel
Copy link
Copy Markdown

vercel Bot commented May 29, 2026

@igorcesarcode is attempting to deploy a commit to the Compozy Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

Warning

Review limit reached

@igorcesarcode, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 24 minutes and 50 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c8f43ae8-45ff-445b-8011-ee36969081c1

📥 Commits

Reviewing files that changed from the base of the PR and between c7feafb and e3eea8e.

📒 Files selected for processing (1)
  • web/src/hooks/routes/use-session-workspace-guard.ts

Walkthrough

This PR adds workspace validation and persistence to enforce that sessions remain bound to their assigned workspaces. It persists the active workspace selection in local storage, refines workspace selection logic to handle edge cases, introduces a session workspace guard hook, and integrates that guard into the SessionPage component.

Changes

Workspace Validation and Persistence

Layer / File(s) Summary
Workspace Persistence and Selection Logic
web/src/systems/workspace/hooks/use-active-workspace-store.ts, web/src/systems/workspace/hooks/use-active-workspace.ts
Zustand store now persists selectedWorkspaceId to local storage under "agh:active-workspace". The workspace selection effect refactored to return early when no workspaces exist, auto-select the first workspace when no selection is set, and clear the selection when the current ID is no longer present in the workspace list.
Session Workspace Guard Hook
web/src/hooks/routes/use-session-workspace-guard.ts
New useSessionWorkspaceGuard hook tracks a session's assigned workspace ID in a ref and triggers a navigation redirect to the agent page when the active workspace diverges from the session workspace.
Guard Integration in SessionPage
web/src/routes/_app/agents.$name.sessions.$id.tsx
SessionPage imports and invokes useSessionWorkspaceGuard with the session's workspace ID and agent name, enforcing workspace validation at page render time.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: persisting active workspace selection and adding a redirect guard for workspace/session mismatches.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@web/src/hooks/routes/use-session-workspace-guard.ts`:
- Around line 25-33: The guard effect only depends on activeWorkspaceId so it
misses the initial load when sessionWorkspaceRef.current (or sessionWorkspaceId)
is populated after mount; update the hook so the validation runs immediately by
adding sessionWorkspaceId (or sessionWorkspaceRef.current) to the dependency
array of the second useEffect (or merge the two useEffect blocks into one) and
perform the same mismatch check (compare sessionWorkspaceRef.current /
sessionWorkspaceId to activeWorkspaceId) and call navigate({ to:
"/agents/$name", params: { name: agentName }, replace: true }) when they differ.
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 878020f8-4908-4e8f-9447-4a60ef103b93

📥 Commits

Reviewing files that changed from the base of the PR and between cd4b155 and c7feafb.

📒 Files selected for processing (4)
  • web/src/hooks/routes/use-session-workspace-guard.ts
  • web/src/routes/_app/agents.$name.sessions.$id.tsx
  • web/src/systems/workspace/hooks/use-active-workspace-store.ts
  • web/src/systems/workspace/hooks/use-active-workspace.ts

Comment thread web/src/hooks/routes/use-session-workspace-guard.ts Outdated
Merge the two useEffect blocks into one so the mismatch check fires
immediately when sessionWorkspaceId arrives after mount, not only on
subsequent activeWorkspaceId changes.
@igorcesarcode
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Member

@pedronauck pedronauck left a comment

Choose a reason for hiding this comment

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

Automated review — self + Codex (gpt-5.5, xhigh)

Independent automated review of PR #238 (web-only: persist active workspace + redirect on session/workspace mismatch). Authoritative change set is 4 files: use-session-workspace-guard.ts [new], agents.$name.sessions.$id.tsx, use-active-workspace-store.ts, use-active-workspace.ts.

  • Self verdict: REWORK (1 blocker, 2 risks, 1 nit)
  • Codex verdict: FIX_BEFORE_SHIP (2 blockers, 2 risks, 1 nit)
  • Combined: not shippable as-is.

This PR already carries a human CHANGES_REQUESTED, so it is not an auto-merge candidate regardless.

Blockers

  • [codex B-1 / self] The headline redirect fails on the exact reload / direct-nav path it targets. workspace_id does exist on the session response (optional), so the guard is wired — but useSession(id) fetches via the active workspace (GET /api/workspaces/{activeWorkspaceId}/sessions/{id}), and the backend is strictly workspace-scoped (requireSessionInWorkspace → 404 on mismatch). On initial load with a mismatched persisted workspace, the session never loads, session?.workspace_id is undefined, the guard ref stays empty, and the page falls through to the generic "Session not found" toast instead of redirecting. The redirect only works for the in-app workspace-switch case (truthful-UI gap). Fix: fetch the session by id independent of the active workspace (or resolve its workspace first), then redirect; add a test proving the reload/direct-nav mismatch redirects.
  • [codex B-2 / self] No tests, and the route now pulls real workspace TanStack Query state into a suite that can't provide it. The canonical -agents.$name.sessions.$id.test.tsx only wraps TopbarSlotProvider and mocks useSession — no QueryClient/workspace mock — so pulling useActiveWorkspaceuseWorkspaces into the route likely breaks that suite and leaves both new invariants (persistence + redirect) uncovered. Fix: add the QueryClient/workspace test scaffolding and cover the persisted store and the redirect.

Risks

  • [self / codex R-1] Hydration race in use-active-workspace.ts:28-37. Auto-select-first-workspace can run before zustand persist rehydrates selectedWorkspaceId, causing flicker or an unintended redirect on first paint. Fix: gate auto-select on persist.hasHydrated() / onFinishHydration.
  • [codex R-2] Persisted localStorage store (agh:active-workspace) needs an explicit test reset — setup clears sessionStorage, not localStorage → cross-test state leakage. Fix: clear localStorage in test setup.

Nits

  • [codex N-1 / self] Render-phase ref mutation in use-session-workspace-guard.ts:19-26; the effect compares the mutable ref rather than the sessionWorkspaceId dependency. Fix: compute the derived value in the effect keyed on the id.

Codex ran once via compozy exec --ide codex --model gpt-5.5 --reasoning-effort xhigh (exit 0, findings validated). No source files modified.

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.

2 participants