Skip to content

feat: add shared workspaces view#991

Open
EhabY wants to merge 5 commits into
mainfrom
feat/shared-workspaces-review-fixes
Open

feat: add shared workspaces view#991
EhabY wants to merge 5 commits into
mainfrom
feat/shared-workspaces-review-fixes

Conversation

@EhabY

@EhabY EhabY commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add a Shared Workspaces tree view with search and manual refresh actions.
  • Query shared:true, then filter out workspaces owned by the signed-in user inside WorkspaceProvider using the session snapshot's userId.
  • Own the session in DeploymentManager/SessionStore and expose a lean snapshot (kind, revision, userId) so concurrent auth or session changes do not leak stale users into shared-workspace filtering or render stale fetches.

Testing

  • pnpm format:check
  • pnpm typecheck
  • pnpm lint
  • pnpm test:extension
Implementation plan and decision log
  • Cherry-picked PR feat: shared workspaces support #957 onto current origin/main and resolved the DeploymentManager conflict while preserving current main's recovery and concurrency guards.
  • Verified coder/coder shared-workspace semantics: shared:true matches any workspace with non-empty user/group ACLs, including current-user-owned workspaces that were shared out, so the extension must filter out workspace.owner_id === currentUserId client-side.
  • Avoided shared_with_user:me because server-side me support is not implemented there and direct-user filtering misses group shares.
  • User chose manual refresh only for Shared Workspaces, matching All Workspaces, instead of polling like My Workspaces.
  • Kept WorkspaceProvider free of current-user knowledge by driving per-view behavior from a WORKSPACE_QUERY_CONFIG table and reading the session through a lean WorkspaceSessionState.getSnapshot() projection.
  • SessionStore owns the session and bumps a revision on every sign-in/out; the provider snapshots revision and userId before each fetch and compares afterward to drop stale responses and to filter shared:true results by owner_id.

Generated with Coder Agents on behalf of @EhabY.

@EhabY EhabY self-assigned this Jun 4, 2026
@EhabY EhabY force-pushed the feat/shared-workspaces-review-fixes branch from 77214b6 to 67f3cee Compare June 5, 2026 10:39
@EhabY EhabY force-pushed the feat/shared-workspaces-review-fixes branch from 67f3cee to 5073b43 Compare June 5, 2026 10:52
@EhabY EhabY linked an issue Jun 5, 2026 that may be closed by this pull request
@EhabY EhabY force-pushed the feat/shared-workspaces-review-fixes branch 2 times, most recently from 710aebd to 6072dba Compare June 5, 2026 10:59
@EhabY EhabY requested a review from code-asher June 5, 2026 11:02
@EhabY EhabY force-pushed the feat/shared-workspaces-review-fixes branch from 6072dba to 0c47e82 Compare June 5, 2026 11:05
@EhabY

EhabY commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator Author

/coder-agents-review

@coder-agents-review

coder-agents-review Bot commented Jun 8, 2026

Copy link
Copy Markdown

Chat: Review in progress | View chat
Requested: 2026-06-08 16:52 UTC by @EhabY
Spend: $70.20 / $100.00

deep-review v0.7.1 | Round 3 | 4ae51e1..07fbfa5

Last posted: Round 3, 19 findings (2 P2, 12 P3, 5 Nit), COMMENT. Review

Finding inventory

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 P2 Author fixed (19084ea) deploymentManager.ts:259 Token refresh triggers full workspace tree rebuild R1 Hisoka P2, Mafuuu P2, Pariston P2, Zoro P2, Chopper P2, Meruem P2, Knov P2, Razor P2, Kite P3, Takumi P3 Yes
CRF-2 P2 Author contested; panel closed R2 (8/8 accept) deploymentManager.ts:281 Token verification race window and silent token drop on failure R1 Takumi P2, Kite P2, Razor P2, Mafuuu P3 Yes
CRF-17 P3 Author fixed (07fbfa5) workspacesProvider.test.ts No test for MAX_FETCH_ATTEMPTS exhaustion R2 Bisky Yes
CRF-18 P3 Author fixed (07fbfa5) workspacesProvider.ts:189 Watcher creation revision gap: session change during await persists stale data R2 Mafuuu Yes
CRF-19 P3 Author fixed (07fbfa5) workspacesProvider.ts:189 CRF-8 dispose guard at watcher creation has no test coverage R2 Bisky Yes
CRF-3 P3 Author fixed (19084ea) workspacesProvider.ts:159 Unbounded recursive fetch() with no disposed check R1 Hisoka P3, Mafuuu P3, Pariston P3, Zoro P3, Chopper P3, Meruem P3, Knov P3, Razor P3 Yes
CRF-4 P3 Author fixed (19084ea) workspacesProvider.test.ts:452 No test for dispose() lifecycle R1 Bisky Yes
CRF-5 P3 Author fixed (19084ea) deploymentManager.test.ts:418 No test for auth listener verification failure R1 Kite Yes
CRF-6 P3 Author fixed (19084ea) testHelpers.ts:1220 Mock session starts signed-in, real starts signed-out R1 Bisky Yes
CRF-7 P3 Author fixed (19084ea) workspacesProvider.ts:95 Session change during failed fetch leaves tree stuck R1 Bisky Yes
CRF-8 P3 Author fixed (19084ea) workspacesProvider.ts:178 Metadata watcher leak if dispose() races fetch() R1 Chopper Yes
CRF-9 P3 Author fixed (19084ea) sessionStore.ts:12 Systematic comment bloat across new files R1 Gon Yes
CRF-10 P3 Author fixed (19084ea) (body) Commit messages lack bodies with design rationale R1 Leorio Yes
CRF-11 P3 Author fixed (19084ea) (body) PR description details section references removed APIs R1 Mafu-san Yes
CRF-12 Nit Author fixed (19084ea) deploymentManager.ts:218 clearSideEffects is a vague method name R1 Gon Yes
CRF-13 Nit Author fixed (19084ea) session.ts:3 WorkspaceSessionSnapshot revision field undocumented R1 Leorio Yes
CRF-14 Nit Author fixed (19084ea) testHelpers.ts:1274 Dead getAxiosInstance() in MockWorkspacesClient R1 Meruem Yes
CRF-15 Nit Author fixed (19084ea) workspacesProvider.ts:259 _onDidChangeTreeData EventEmitter not disposed R1 Chopper Yes
CRF-16 Nit Author fixed (19084ea) extension.ts:101 Unrelated blank line removal R1 Kite Yes

Contested and acknowledged

CRF-2 (P2, deploymentManager.ts:281) - Token verification race window and silent token drop on failure

  • Finding: Review proposed applying credentials eagerly before verification to eliminate the race window where the shared client holds an expiring old token during the verify round-trip, and to ensure verification failure does not silently drop the new token.
  • Author defense: Kept verify-before-apply intentionally. Does not want to push an unverified token onto the shared client. Addressed the race via recovery: if the session was suspended during verify for the same deployment, the method now re-signs in after verification succeeds. For the failure path: retains the old token; the next rotation or recovery retries. Added tests for both paths.
  • Panel closure (R2, 8/8): Mafuuu, Hisoka, Pariston, Mafu-san, Takumi, Kite, Meruem, and Razor each independently traced the three-branch recovery in verifyAndUpdateSession and verified the tests cover all interleavings. The verify-before-apply pattern prevents unverified tokens from reaching the live client. The suspension-recovery path handles the main race scenario. The consequence (brief 401 window during verification) is bounded by one HTTP round-trip and self-heals.

Law analysis

Effective LOC: 1094 (+1094 -207). Head SHA: 0c47e82. Verdict: Don't split. Enforcement: Advisory.

Round log

Round 1

Panel. 2 P2, 7 P3, 5 Nit. 2 body-only (not posted inline). Reviewed against bea4059..0c47e82.

Round 2

Churn guard: PROCEED (overrode BLOCKED; CRF-8 and CRF-15 were addressed in code but had no reply because they were folded into the review body). 14 fixed, 1 contested (CRF-2). CRF-2 closed by panel (8/8). 3 new P3 findings. Reviewed against 4ae51e1..19084ea.

Round 3

Churn guard: PROCEED. CRF-17, CRF-18, CRF-19 all addressed (07fbfa5). No open findings remain. Reviewed against 4ae51e1..07fbfa5.

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Kurapika security
Law decomposition
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

@coder-agents-review coder-agents-review 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.

The session-state refactoring is well-designed. The SessionStore + WorkspaceSessionState interface decouples DeploymentManager from WorkspaceProvider cleanly. The revision-based snapshot is the right mechanism for stale-fetch detection, and WORKSPACE_QUERY_CONFIG replaces scattered conditionals with a single source of truth. Test density is strong at 54.4%, and the concurrency tests (session change mid-request, sign-out during pending fetch, hidden-then-visible transitions) verify real race scenarios.

Severity breakdown: 2 P2, 7 P3, 5 Nit.

The two P2s both stem from the auth listener's token-rotation path, which changed from a lightweight setCredentials() swap to a full verifyAndUpdateSession -> setDeployment -> signIn() cascade. On success, this causes tree flicker and 4 extra API calls per rotation (CRF-1). On failure, the new token is silently dropped, leaving the client holding expiring credentials (CRF-2). Both are fixable without changing the overall architecture.

Process notes: Two of the three commits have empty bodies. The feature commit should explain why shared:true includes current-user workspaces and why client-side filtering exists; that is non-obvious and only lives in the PR description currently. The PR description's collapsible "Implementation plan" section references APIs that were removed in subsequent commits (#authedUser, getCurrentUserId(), getAuthStateVersion(), filterWorkspaces callback).

"If #verifyCredentials throws, the catch block logs a warning and returns. Neither the old nor the new token is applied to the client." --Mafuuu, on why verify-before-apply needs a fallback


src/workspace/workspacesProvider.ts:178

P3 [CRF-8] Agent metadata watchers can leak if dispose() races an in-flight fetch() that creates watchers.

dispose() calls clearState() which disposes and clears agentWatchers. If dispose() runs while fetch() is between createAgentMetadataWatcher calls:

  1. dispose() disposes all existing watchers and clears the map.
  2. createAgentMetadataWatcher completes and adds a new watcher to the now-empty map.
  3. setWorkspaces checks this.disposed and returns early.
  4. The new watcher is never disposed; its EventSource connection leaks.

This only affects WorkspaceQuery.Mine (the only query with showMetadata: true) and requires a narrow timing window. Fix: check this.disposed before creating each new watcher inside the loop.

(Chopper)

🤖

src/workspace/workspacesProvider.ts:259

Nit [CRF-15] _onDidChangeTreeData EventEmitter is never disposed in dispose(). The disposed flag prevents new fires, so this is a resource accounting gap rather than a functional bug. Add this._onDidChangeTreeData.dispose() to dispose() for completeness. (Chopper)

🤖

🤖 This review was automatically generated with Coder Agents.

Comment thread src/deployment/deploymentManager.ts
Comment thread src/deployment/deploymentManager.ts
Comment thread src/workspace/workspacesProvider.ts Outdated
Comment thread test/unit/workspace/workspacesProvider.test.ts
Comment thread test/unit/deployment/deploymentManager.test.ts
Comment thread src/workspace/session.ts
Comment thread test/mocks/testHelpers.ts Outdated
Comment thread src/extension.ts
Comment thread src/workspace/workspacesProvider.ts
Comment thread src/deployment/sessionStore.ts
@EhabY EhabY force-pushed the feat/shared-workspaces-review-fixes branch 2 times, most recently from 175c550 to 19084ea Compare June 8, 2026 15:54
@EhabY

EhabY commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator Author

/coder-agents-review

@coder-agents-review coder-agents-review 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.

All 16 R1 findings addressed. CRF-2 (contested) is closed by the panel (8/8 accept). The verify-before-apply approach with suspension recovery is sound; every reviewer independently traced the three-branch recovery in verifyAndUpdateSession and confirmed the tests cover all race interleavings.

The R2 delta is clean: CRF-1 fix (same-user rotation skips revision bump) eliminates the tree rebuild, CRF-3 fix (bounded loop with disposed check) closes the unbounded recursion, and CRF-8 fix (post-await disposed guard on watcher creation) prevents the leak. Comment trimming, naming, and documentation fixes are all retained.

3 new P3 findings from this round, all in the watcher-creation section of fetch() and the retry cap.

"The verify-before-apply pattern prevents unverified tokens from reaching the live client. The suspension-recovery path handles the main race scenario." --Meruem, closing CRF-2

🤖 This review was automatically generated with Coder Agents.

Comment thread src/workspace/workspacesProvider.ts Outdated
Comment thread test/unit/workspace/workspacesProvider.test.ts
Comment thread src/workspace/workspacesProvider.ts Outdated
@EhabY

EhabY commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator Author

/coder-agents-review

@coder-agents-review coder-agents-review 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.

All 19 findings across 3 rounds are resolved. CRF-17 (MAX_FETCH_ATTEMPTS test), CRF-18 (revision re-check after watcher creation), and CRF-19 (dispose-during-watcher-creation test) are all addressed in 07fbfa5. The sessionChangedSince helper shared between the mid-request check and the watcher-creation guard is a clean factoring.

Final state: 571 production lines, 779 test lines (57.7% test density), 1667 tests passing. The session-state architecture is sound, the race conditions are covered, and the shared-workspace filtering works correctly.

"The revision-based snapshot comparison is a well-chosen mechanism for stale-fetch detection." --Knov, R1

🤖 This review was automatically generated with Coder Agents.

@EhabY EhabY force-pushed the feat/shared-workspaces-review-fixes branch from 07fbfa5 to 9c46b7a Compare June 9, 2026 09:11
EhabY and others added 5 commits June 10, 2026 19:08
Add a Shared Workspaces tree view alongside My and All Workspaces, with
search and manual refresh.

The view queries `shared:true`, which on the server matches any workspace
with non-empty user or group ACLs. That set also includes workspaces the
signed-in user owns and shared out, so the extension filters those out
client-side by `owner_id` to leave only workspaces shared with the user.
`shared_with_user:me` is not used: the server does not implement `me` there,
and direct-user filtering would miss group shares.
Replace the WorkspaceProvider filter callback and ad-hoc state-version getter
with a small WorkspaceSessionState abstraction. Providers now read a
point-in-time snapshot (kind, revision, userId) and subscribe to changes,
instead of being handed a filter function and a version getter.

This puts current-user knowledge and stale-fetch detection behind one
interface: DeploymentManager exposes session state in one place, and the
provider compares snapshot revisions to discard responses from a session that
changed mid-request.
- collapse per-query branches into a readonly WORKSPACE_QUERY_CONFIG table
- drop the unreachable re-entry branch in the workspace refresh loop
- remove test-only getCurrentUserId; tests read getSnapshot via a helper
- extract the repeated tree-view wiring in extension.ts into a helper
- drive metadata tests through the real watcher over MockEventStream
- move shared test doubles (session, workspaces client, flush) into testHelpers
- replace describe-scoped state + beforeEach/afterEach with a setup() helper
verifyAndUpdateSession keeps verify-before-apply: it verifies the rotated
token before touching the live client, rotates credentials in place when the
user is unchanged (no revision bump or tree rebuild), and recovers a session
that was suspended mid-verify instead of getting stuck signed out (CRF-1, CRF-2).

Also:
- bound fetch() retries with a per-attempt disposed check, and dispose a
  metadata watcher created while dispose() races the fetch (CRF-3, CRF-8)
- dispose the tree-data EventEmitter on dispose() (CRF-15)
- rename clearSideEffects to clearAuthState (CRF-12)
- document the session snapshot revision contract (CRF-13)
- drop the dead getAxiosInstance mock helper and restore an unrelated blank
  line (CRF-14, CRF-16)
- trim comments that restated the code (CRF-9)
- add tests for dispose lifecycle, verify failure, signed-out startup, and
  stale-fetch recovery (CRF-4 through CRF-7)
- re-check the session revision after the watcher-creation await so a session
  change mid-await cannot overwrite the cleared tree with a stale session's
  workspaces (CRF-18)
- add tests for the MAX_FETCH_ATTEMPTS exhaustion path and the
  dispose-during-watcher-creation guard (CRF-17, CRF-19)
@EhabY EhabY force-pushed the feat/shared-workspaces-review-fixes branch from 9c46b7a to 400b4d2 Compare June 10, 2026 16:18
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.

Add a Shared Workspaces view

1 participant