Repository navigation
fix: persist active workspace and redirect on session/workspace mismatch - #238
Conversation
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
|
@igorcesarcode is attempting to deploy a commit to the Compozy Team on Vercel. A member of the Team first needs to authorize it. |
|
Caution Review failedPull request was closed or merged during review WalkthroughPersists and hydrates the active workspace selection, gates active workspace exposure until hydration, adds workspace-resolution queries for sessions and a new useSessionById hook, and enforces session->workspace consistency via useSessionWorkspaceGuard integrated into session routes and covered by tests. ChangesWorkspace Validation and Persistence
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (4)
web/src/hooks/routes/use-session-workspace-guard.tsweb/src/routes/_app/agents.$name.sessions.$id.tsxweb/src/systems/workspace/hooks/use-active-workspace-store.tsweb/src/systems/workspace/hooks/use-active-workspace.ts
Merge the two useEffect blocks into one so the mismatch check fires immediately when sessionWorkspaceId arrives after mount, not only on subsequent activeWorkspaceId changes.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
pedronauck
left a comment
There was a problem hiding this comment.
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_iddoes exist on the session response (optional), so the guard is wired — butuseSession(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_idisundefined, 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.tsxonly wrapsTopbarSlotProviderand mocksuseSession— noQueryClient/workspace mock — so pullinguseActiveWorkspace→useWorkspacesinto 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 zustandpersistrehydratesselectedWorkspaceId, causing flicker or an unintended redirect on first paint. Fix: gate auto-select onpersist.hasHydrated()/onFinishHydration. - [codex R-2] Persisted
localStoragestore (agh:active-workspace) needs an explicit test reset — setup clearssessionStorage, notlocalStorage→ cross-test state leakage. Fix: clearlocalStoragein 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 thesessionWorkspaceIddependency. 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.
…249) ## Summary `main` (commit `8cc8ff39`) is red, blocking the **v0.0.6** release PR #248. All three failing release-PR checks trace back to `main`, not the release branch: - **Verify** — `internal/daemon/coordinator_runtime.go` `bootstrapRun` exceeded the gocyclo threshold (CC **21 > 20**). Extracted `reconcileCreatedCoordinator` and `wakeCoordinatorIfNeeded` — a behavior-preserving split that drops complexity to ~10. - **Integration** — `TestHarnessContextIntegrationStartupAndPromptShareResolverPolicy` counted `<agh-situation-context>` (the **open** tag), which the bundled tools guide (`tools-and-skills.md`) documents in prose, so the count was 2. It now counts the closing `</agh-situation-context>` tag → measures **rendered sections** (exactly 1). The product already renders the section once; the assertion was fragile (red since v0.0.5). - **E2E Nightly** — the sandbox *"real session execution"* test direct-navigates to a session created in a temp workspace while the **global** workspace is active. #238's `useSessionWorkspaceGuard` redirects away on a workspace/session mismatch, so the composer never rendered and `locator.fill` hit the 180s timeout. The test now activates the session's workspace via the switcher first — the persisted-active-workspace precondition #238 assumes for direct navigation. ## Verification - `make verify` — **pass** (full monorepo gate). - golangci-lint **v2.12.2** `./...` (CI's pinned version) — **0 issues**. - `go test -race ./internal/daemon/...` — **875 unit pass**; **987 integration pass** (`-tags integration`). - `bun-lint` — 0 warnings / 0 errors; web typecheck — pass. - The E2E fix is verified by deterministic root-cause analysis + typecheck/lint; the release PR's **E2E Nightly** lane (the lane that failed) is its end-to-end gate. A faithful single-test local run is blocked by the nightly coverage-gate plus the daemon-served harness env. ## Release impact Merging this refreshes `release/v0.0.6` (#248) to include the fix so its **Verify** and **Integration** lanes go green. Version stays **v0.0.6** — CI's pinned git-cliff `2.7.0` treats `feat:` as a patch bump on `0.x` (same as how v0.0.5 was cut). > Note: v0.0.5's own release PR (#237) merged with **Integration + Verify failing**. The heavy lanes (Integration, E2E Nightly) only run on release PRs, so these regressions slipped past per-feature CI. Worth tightening the merge gate separately. AGH Impact Audit: - Native tools: no impact — no `agh__*` IDs, toolsets, descriptors, input/output schemas, schema digests, risk flags, or capability gates changed; this is an internal daemon refactor plus two test files. - Extensibility and hooks: no impact — no extension manifests/resources, hook taxonomy or dispatch call sites, skill/capability declarations, bundles, registries, bridge SDKs, MCP sidecars, or `config.toml` lifecycle changed. `bootstrapRun`'s dispatch call sites, locking, and ordering are preserved by the extraction. - Workspace data isolation: no impact on runtime data ownership — the coordinator refactor preserves `workspace_id`-scoped bootstrap/reconcile/dispatch exactly; the e2e change only makes the test honor the existing per-workspace session-route guard from #238. - Official AGH skill: no impact — `skills/agh/SKILL.md` and `skills/agh/references/*.md` are unchanged. The `tools-and-skills.md` `<agh-situation-context>` mention is correct documentation and intentionally left as-is; the test was corrected instead. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Improved internal coordinator session initialization and management code organization for better maintainability and clarity. * **Tests** * Updated integration tests with refined assertions to better validate rendered output correctness. * Enhanced end-to-end tests with explicit verification steps for workspace-to-session binding workflows. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Summary
selectedWorkspaceIdin 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.selectedWorkspaceIdis null, the first available workspace is automatically selected and persisted, avoiding the case where the fallbackquery.data[0]changes silently across refreshes.useSessionWorkspaceGuardhook detects when the active workspace changes to one that does not own the current session and navigates to/agents/$nameinstantly — without waiting for TanStack Query's multi-second retry exhaustion on the 404.Root cause
useActiveWorkspaceStorehad no persistence —selectedWorkspaceIdreset tonullon every page load.useActiveWorkspacesilently fell back toquery.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
/agents/$nameimmediately (< 500 ms), not after a 7+ second retry timeoutmake web-lintpasses (zero oxlint warnings)bunx turbo run typecheck --filter=./webpassesSummary by CodeRabbit
New Features
Bug Fixes
Tests