Conversation
|
Thank you for your first contribution! 🎉 🔔 @thomaslombart @thomaspaulmann @peduarte @pernielsentikaer @clemb8 @jfkisafk @filipszczepanski @JoeBoulton @itsmingjie @xmok @0xdhrv @erikjs14 @alexibuild @samuelkraft you might want to have a look. You can use this guide to learn how to check out the Pull Request locally in order to test it. 📋 Quick checkout commandsBRANCH="feat/linear-multi-workspace"
FORK_URL="https://github.com/smorin/extensions.git"
EXTENSION_NAME="linear"
REPO_NAME="extensions"
git clone -n --depth=1 --filter=tree:0 -b $BRANCH $FORK_URL
cd $REPO_NAME
git sparse-checkout set --no-cone "extensions/$EXTENSION_NAME"
git checkout
cd "extensions/$EXTENSION_NAME"
npm install && npm run devWe're currently experiencing a high volume of incoming requests. As a result, the initial review may take up to 15 business days. |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds multi-workspace support for the Linear Raycast extension, enabling multiple concurrent OAuth sessions (including same workspace across different accounts) with consistent workspace switching across UI surfaces, menu bar aggregation, and AI tool routing.
Changes:
- Introduces a workspace registry + per-(workspace, account) OAuth providerId strategy, plus a new Manage Workspaces command.
- Adds workspace switchers (list dropdown + form field), per-workspace persisted UI defaults, and menu bar unread notifications aggregated across workspaces.
- Routes AI tools per call via
workspaceId, addsget-workspaces, and updates tool auth to be non-interactive.
Reviewed changes
Copilot reviewed 99 out of 99 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| extensions/linear/src/unread-notifications.tsx | Aggregates unread notifications across all connected workspaces in the menu bar. |
| extensions/linear/src/tools/update-project-milestone.ts | Adds optional workspaceId routing + non-interactive tool auth. |
| extensions/linear/src/tools/update-issue.ts | Adds optional workspaceId routing + workspace-aware confirmations. |
| extensions/linear/src/tools/update-document.ts | Adds optional workspaceId routing + workspace-aware confirmations. |
| extensions/linear/src/tools/update-comment.ts | Adds optional workspaceId routing + workspace-aware confirmations and attachment uploads. |
| extensions/linear/src/tools/resolveToolWorkspace.ts | Implements per-call workspace routing for AI tools and non-interactive auth wrapper. |
| extensions/linear/src/tools/remove-label.ts | Adds optional workspaceId routing + workspace-aware confirmations. |
| extensions/linear/src/tools/get-workspaces.ts | New tool to list connected workspaces without triggering OAuth. |
| extensions/linear/src/tools/get-teams.ts | Adds optional workspaceId routing for AI tools. |
| extensions/linear/src/tools/get-projects.ts | Adds optional workspaceId routing for AI tools. |
| extensions/linear/src/tools/get-project-updates.ts | Adds optional workspaceId routing for AI tools. |
| extensions/linear/src/tools/get-project-statuses.ts | Adds optional workspaceId routing for AI tools. |
| extensions/linear/src/tools/get-notifications.ts | Adds optional workspaceId routing for AI tools. |
| extensions/linear/src/tools/get-members.ts | Adds optional workspaceId routing for AI tools. |
| extensions/linear/src/tools/get-labels.ts | Adds optional workspaceId routing for AI tools. |
| extensions/linear/src/tools/get-issue-states.ts | Adds optional workspaceId routing for AI tools. |
| extensions/linear/src/tools/get-initiatives.ts | Adds optional workspaceId routing and exposes API helper with client override. |
| extensions/linear/src/tools/get-documents.ts | Adds optional workspaceId routing and exposes API helper with client override. |
| extensions/linear/src/tools/get-document-content.ts | Adds optional workspaceId routing and exposes API helper with client override. |
| extensions/linear/src/tools/get-current-user.ts | Adds optional workspaceId routing for AI tools. |
| extensions/linear/src/tools/full-text-search-issues.ts | Adds optional workspaceId routing for AI tools. |
| extensions/linear/src/tools/formatConfirmation.ts | Makes confirmations workspace-aware via explicit client parameter. |
| extensions/linear/src/tools/filter-issues.ts | Adds optional workspaceId routing for AI tools. |
| extensions/linear/src/tools/create-project-update.ts | Adds optional workspaceId routing + workspace-aware confirmations. |
| extensions/linear/src/tools/create-issue.ts | Adds optional workspaceId routing + workspace-aware confirmations. |
| extensions/linear/src/tools/create-document.ts | Adds optional workspaceId routing + workspace-aware confirmations. |
| extensions/linear/src/tools/create-comment.ts | Adds optional workspaceId routing + workspace-aware confirmations and attachment uploads. |
| extensions/linear/src/tools/add-label.ts | Adds optional workspaceId routing + workspace-aware confirmations. |
| extensions/linear/src/search-issues.tsx | Adds workspace dropdown accessory to list search UI. |
| extensions/linear/src/search-custom-views.tsx | Adds workspace dropdown accessory to custom view search UI. |
| extensions/linear/src/quick-add-comment-to-issue.ts | Adds one-shot workspace argument resolution + subtitle refresh mechanism. |
| extensions/linear/src/notifications.tsx | Adds workspace dropdown accessory and makes menu bar refresh best-effort. |
| extensions/linear/src/manage-workspaces.tsx | New UI to add/switch/re-auth/logout workspaces with staging-token recovery. |
| extensions/linear/src/hooks/useWorkspaceCachedState.ts | New helper to scope persisted UI state by workspace key. |
| extensions/linear/src/hooks/useUsers.ts | Scopes cached users queries by workspace. |
| extensions/linear/src/hooks/useTeams.ts | Scopes cached teams queries by workspace. |
| extensions/linear/src/hooks/useStates.ts | Scopes cached workflow states queries by workspace. |
| extensions/linear/src/hooks/useSearchIssues.ts | Scopes cached issue search by workspace. |
| extensions/linear/src/hooks/useProjects.ts | Scopes cached projects pagination by workspace. |
| extensions/linear/src/hooks/useProjectUpdates.ts | Scopes cached project updates by workspace. |
| extensions/linear/src/hooks/useProjectStatuses.ts | Scopes cached project statuses by workspace. |
| extensions/linear/src/hooks/usePriorities.ts | Scopes cached priority values by workspace. |
| extensions/linear/src/hooks/useNotifications.ts | Scopes cached notifications by workspace. |
| extensions/linear/src/hooks/useMilestones.ts | Scopes cached milestones by workspace. |
| extensions/linear/src/hooks/useMe.ts | Scopes cached viewer by workspace. |
| extensions/linear/src/hooks/useLabels.ts | Scopes cached labels by workspace. |
| extensions/linear/src/hooks/useIssues.ts | Scopes cached issue lists by workspace. |
| extensions/linear/src/hooks/useIssueTemplates.ts | Scopes cached issue templates by workspace. |
| extensions/linear/src/hooks/useIssueDetail.ts | Scopes cached issue detail by workspace. |
| extensions/linear/src/hooks/useIssueComments.ts | Scopes cached issue comments by workspace. |
| extensions/linear/src/hooks/useInitiatives.ts | Scopes cached initiatives by workspace. |
| extensions/linear/src/hooks/useDocuments.ts | Scopes cached documents + content by workspace. |
| extensions/linear/src/hooks/useCycles.ts | Scopes cached cycles queries by workspace. |
| extensions/linear/src/hooks/useCustomViews.ts | Scopes cached custom views + issues by workspace. |
| extensions/linear/src/hooks/useAllWorkspaceNotifications.ts | New hook to fetch notifications per workspace for menu bar aggregation. |
| extensions/linear/src/helpers/workspaceArgument.ts | Implements workspace argument matching + command subtitle updates. |
| extensions/linear/src/helpers/refreshQuickSubtitles.ts | Adds best-effort background launches to refresh quick-command subtitles. |
| extensions/linear/src/helpers/createIssueForMyself.ts | Extracts “create issue for myself” into a reusable helper supporting client overrides. |
| extensions/linear/src/favorites.tsx | Adds workspace dropdown and scopes favorites caching by workspace. |
| extensions/linear/src/created-issues.tsx | Adds workspace dropdown accessory. |
| extensions/linear/src/create-project.tsx | Adds draft-time workspace pinning and loading gating for project creation flow. |
| extensions/linear/src/create-issue.tsx | Adds draft-time workspace pinning and loading gating for issue creation flow. |
| extensions/linear/src/create-issue-for-myself.ts | Adds workspace subtitle refresh + workspace-aware auth wrapper. |
| extensions/linear/src/create-issue-for-myself-in-workspace.ts | New quick command to create an issue explicitly in a named workspace. |
| extensions/linear/src/components/docs/DocumentList.tsx | Adds workspace switch section to entity dropdown + per-workspace persisted entity selection. |
| extensions/linear/src/components/WorkspaceDropdown.tsx | New list/form workspace dropdown components + embedded section for existing dropdowns. |
| extensions/linear/src/components/WorkspaceContext.tsx | New context/provider handling workspace switching and remount boundary. |
| extensions/linear/src/components/View.tsx | Wraps commands in WorkspaceProvider and updates auth handling for multi-workspace. |
| extensions/linear/src/components/ProjectList.tsx | Adds workspace switch section to initiative dropdown + per-workspace persisted initiative. |
| extensions/linear/src/components/ProjectIssues.tsx | Scopes milestone filter persistence by workspace. |
| extensions/linear/src/components/EditIssueForm.tsx | Removes storeValue from milestone dropdown (workspace-scoped persistence now handled elsewhere). |
| extensions/linear/src/components/CreateProjectForm.tsx | Adds workspace form field + per-workspace persisted defaults (status/lead). |
| extensions/linear/src/components/CreateMilestoneForm.tsx | Adds per-workspace persisted project selection and removes storeValue. |
| extensions/linear/src/components/CreateIssueForm.tsx | Adds workspace form field + per-workspace persisted defaults with readiness semantics. |
| extensions/linear/src/assigned-issues.tsx | Embeds workspace switch section into view dropdown. |
| extensions/linear/src/api/workspaces.ts | New workspace registry (LocalStorage) with migration/adoption of existing single-login slot. |
| extensions/linear/src/api/withWorkspaceAuth.ts | New wrapper reusing withAccessToken but bootstrapping workspace auth. |
| extensions/linear/src/api/updateNotification.ts | Adds optional client override for workspace routing. |
| extensions/linear/src/api/updateIssue.ts | Adds optional client override for workspace routing. |
| extensions/linear/src/api/oauth.ts | Adds providerId-aware OAuth services + staging token flow + non-destructive refresh. |
| extensions/linear/src/api/linearClient.ts | Reworks client bootstrap/caching for multi-workspace + active workspace snapshot. |
| extensions/linear/src/api/initiatives.ts | Adds optional client override for workspace routing. |
| extensions/linear/src/api/getTeams.ts | Adds optional client override for workspace routing. |
| extensions/linear/src/api/getProjects.ts | Adds optional client override for workspace routing. |
| extensions/linear/src/api/getNotifications.ts | Adds optional client override for workspace routing. |
| extensions/linear/src/api/getMilestones.ts | Adds optional client override for workspace routing. |
| extensions/linear/src/api/getLabels.ts | Adds optional client override for workspace routing. |
| extensions/linear/src/api/getIssues.ts | Adds optional client override for workspace routing for all issue queries. |
| extensions/linear/src/api/getIssueTemplates.ts | Adds optional client override for workspace routing. |
| extensions/linear/src/api/getCustomViews.ts | Adds optional client override for workspace routing. |
| extensions/linear/src/api/favorites.ts | Adds optional client override for workspace routing. |
| extensions/linear/src/api/documents.ts | Adds optional client override for workspace routing. |
| extensions/linear/src/api/deleteNotification.ts | Adds optional client override for workspace routing. |
| extensions/linear/src/api/createIssue.ts | Adds optional client override for workspace routing (issue + sub-issue). |
| extensions/linear/src/api/attachments.ts | Adds optional client override to keep uploads routed to correct workspace. |
| extensions/linear/src/active-cycle.tsx | Adds embedded workspace switching + per-workspace persisted selected team. |
| extensions/linear/package.json | Adds new commands/tools and updates AI instructions + evals for workspace routing. |
| extensions/linear/README.md | Documents multi-workspace usage and new management/switching surfaces. |
| extensions/linear/CHANGELOG.md | Adds changelog entry describing multi-workspace support. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| export async function refreshNonDestructive( | ||
| service: OAuthService, | ||
| ): Promise<{ status: "ok"; accessToken: string } | { status: "failed" }> { | ||
| const tokens = await service.client.getTokens(); | ||
| if (!tokens?.refreshToken) return { status: "failed" }; | ||
| try { | ||
| const response = await fetch(`${LINEAR_PROXY}/refresh-token`, { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ | ||
| client_id: LINEAR_PROXY_CLIENT_ID, | ||
| refresh_token: tokens.refreshToken, | ||
| grant_type: "refresh_token", | ||
| }), | ||
| }); | ||
| if (!response.ok) return { status: "failed" }; | ||
| const next = (await response.json()) as { access_token: string; refresh_token?: string; expires_in?: number }; | ||
| await service.client.setTokens(next); | ||
| return { status: "ok", accessToken: next.access_token }; | ||
| } catch { | ||
| return { status: "failed" }; | ||
| } | ||
| } |
There was a problem hiding this comment.
Not a bug as filed: OAuth.PKCEClient.setTokens accepts TokenSetOptions | TokenResponse, and TokenResponse is the snake_case shape (access_token, refresh_token, expires_in) documented as directly passable in @raycast/api's type definitions. While here, ca9eb8c makes the refresh path keep the stored refresh token when a response omits one, matching @raycast/utils's own refreshTokens behavior.
| } | ||
| } | ||
|
|
||
| if (!teamId) { |
There was a problem hiding this comment.
Fixed in 3981f6e: an empty team list now throws the intended "No team found" error instead of a TypeError from teams[0].id.
| {rows.map((row) => ( | ||
| <MenuBarExtra.Section | ||
| key={entryKey(row.entry)} | ||
| {...(multi | ||
| ? { | ||
| title: | ||
| row.entry.orgName + | ||
| (rows.filter((r) => r.entry.orgId === row.entry.orgId).length > 1 ? ` (${row.entry.userEmail})` : ""), | ||
| } | ||
| : {})} | ||
| > |
There was a problem hiding this comment.
Fixed in 3981f6e: the per-org entry count is computed once with countBy before rendering.
| async function logOutAll() { | ||
| const confirmed = await confirmAlert({ | ||
| title: "Log Out of All Workspaces?", | ||
| message: | ||
| "Removes every workspace login from Raycast (tokens are deleted locally; each access token expires at Linear on its own within 24 hours).", | ||
| primaryAction: { title: "Log Out of All", style: Alert.ActionStyle.Destructive }, | ||
| }); | ||
| if (!confirmed) return; | ||
| for (const row of rows) { | ||
| const service = getServiceForProviderId(row.entry.providerId, undefined, `Linear — ${row.entry.orgName}`); | ||
| await service.client.removeTokens(); | ||
| await removeWorkspaceEntry({ orgId: row.entry.orgId, userId: row.entry.userId }); | ||
| } | ||
| await showToast({ style: Toast.Style.Success, title: "Logged out of all workspaces" }); | ||
| refreshQuickCommandSubtitles(); // fire-and-forget: workspace membership changed, subtitles listing "others" are now stale | ||
| await reload(); | ||
| } |
There was a problem hiding this comment.
Fixed in 3981f6e: logOutAll now removes each workspace best-effort inside a try/catch, tracks the failing step like logOut, and reports which workspaces failed in a failure toast.
Greptile SummaryThe PR adds multi-workspace authentication, routing, workspace management, aggregate notifications, and workspace-aware AI tools to the Linear extension. The logout cache fix still permits an in-flight menu-bar refresh to restore a removed workspace’s notification data after invalidation.
Confidence Score: 3/5The PR is not yet safe to merge because logout can still leave removed-workspace notification payloads in the unencrypted cache through stale asynchronous writeback. smorin stated that the notification-cache issue was addressed, but a refresh that captured the workspace before logout can complete after the clear and unconditionally persist the same removed workspace’s rows again. Files Needing Attention: extensions/linear/src/hooks/useAllWorkspaceNotifications.ts and extensions/linear/src/manage-workspaces.tsx
|
| Filename | Overview |
|---|---|
| extensions/linear/src/hooks/useAllWorkspaceNotifications.ts | Adds aggregated notification fetching and named caching, but an in-flight request can repopulate removed-workspace rows after logout clears the cache. |
| extensions/linear/src/manage-workspaces.tsx | Adds workspace lifecycle management and cache clearing on logout, but the clear operation does not invalidate notification requests already in progress. |
| extensions/linear/src/api/workspaces.ts | Implements the multi-workspace registry, migration, active-entry selection, and workspace removal primitives. |
| extensions/linear/src/api/linearClient.ts | Adds per-workspace client and token resolution used throughout the extension. |
| extensions/linear/src/tools/resolveToolWorkspace.ts | Routes AI tool calls through the requested connected workspace. |
| extensions/linear/package.json | Registers the workspace-management commands, workspace argument, and workspace-discovery AI tool. |
Prompt To Fix All With AI
### Issue 1
extensions/linear/src/hooks/useAllWorkspaceNotifications.ts:63
**Stale refresh restores logged-out data**
If a menu-bar notification refresh overlaps a workspace logout, the request retains the pre-removal registry and `onData` writes its completed rows after `clearWorkspaceNotificationsCache()` runs, restoring the removed workspace’s private notification payloads to the unencrypted cache until a later successful refresh replaces them.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (3): Last reviewed commit: "fix(linear): preserve interrupted worksp..." | Re-trigger Greptile
|
@0xdhrv thanks for picking it up let me know if you have questions or suggestions |
Looks good so far, the API team is reviewing the utils changes, once it's done I'll will re-review this |
|
@0xdhrv The design doesn't really immediately take advantage of the útil changes. Built so it works without that. Once those are in can do an update to take advantage of the new auth capabilities |
|
@smorin thanks for contributing here. I'd be excited to use it! |
0xdhrv
left a comment
There was a problem hiding this comment.
Hi @smorin 👋
Thanks for the contribution 🔥
Could you look into the suggestions from Greptile?
Also, could you resolve the merge conflict?
I converted this PR into a draft until it's ready for the review, please press the button Ready for review when it's ready and we'll have a look 😊
Adds multi-workspace support to the Linear extension. See PR description for the full feature list and design notes. Builds on raycast/utils#80 via a temporary manual OAuth service construction behind a code-level switch.
…n preservation, docs and comments Claude-Session: https://claude.ai/code/session_01RvxBX9CKpjp6oqGVCW96fz
7ca9d40 to
ca9eb8c
Compare
|
@0xdhrv thanks — both items done: rebased onto main (resolves the conflict, and adapts the multi-workspace routing to the new AI-tool set from #30517: all list/get/save tools now accept |
0xdhrv
left a comment
There was a problem hiding this comment.
Hi 👋
Thanks for your contribution 💪
I have now tested your extension, and I have some feedback ready for you:
When I tried to login into with my other account into other workspace, I got successfully connected message on the webpage, but I don't see it working meaning, I don't see two workspaces in the list for Manage Workspaces command. I suppose this is related to pending publish of utils changes, but want to confirm, if this is the case?
I'm looking forward to testing this extension again 🔥
Feel free to contact me here or at Slack if you have any questions.
Nope, that is a bug of some sort. In this PR, I had a workaround to make it work until the other PR was ready, but built it with a plan to be compatible and switch over once that's ready. I did the merge, and on my end, I have this working on my end I'll send you a video in slack |
pernielsentikaer
left a comment
There was a problem hiding this comment.
Hi @smorin 👋
I also just gave it a spin but I'm not able to connect the second one in my end, I just end up in this window after connecting the second workspace
Could there be some kind of race condition?
I converted this PR into a draft until it's ready for the review, please press the button Ready for review when it's ready and we'll have a look 😊
|
Let me check again, is running locally
Steve Morin | Entrepreneur, Engineering Leader, Startup Advisor
AI TikTok at | ***@***.***
twitter.com/SteveMorin ( http://twitter.com/SteveMorin ) | stevemorin.com ( http://stevemorin.com ) | https://productivegrowth.substack.com/ | https://www.linkedin.com/in/stevebmorin/
Live the dream start a startup. Make the world ... a better place.
…On Mon, Sep 07, 2026 at 12:16 AM, Per Nielsen Tikær < ***@***.*** > wrote:
***@***.**** requested changes on this pull request.
Hi @ smorin ( https://github.com/smorin ) 👋
I also just gave it a spin but I'm not able to connect the second one in
my end, I just end up in this window after connecting the second workspace
1788765340866.png (
https://camo.githubusercontent.com/f8339c26baf91efb97c265e93885abaafe98572ec9dfc2ee5d5bf4b49b592de4/68747470733a2f2f726179636173742e6769746875622e696f2f676875622f696d616765732f313738383736353334303836362e706e67
)
Could there be some kind of race condition?
I converted this PR into a draft until it's ready for the review, please
press the button Ready for review when it's ready and we'll have a look 😊
—
Reply to this email directly, view it on GitHub (
#30314?email_source=notifications&email_token=AAA6OL4MCFAIPJJCZI4IHWD5NZOD7A5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTKMJSHEYDMOBUHA42M4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#pullrequestreview-5129068489
) , or unsubscribe (
https://github.com/notifications/unsubscribe-auth/AAA6OL3DSF5B74Q3EG2UEXT5NZOD7AVCNFSNUABFKJSXA33TNF2G64TZHM2DAOBYGU4DGMZQHNEXG43VMU5TKMJXHAZTGOBSGM42C5QC
).
You are receiving this because you were mentioned. Message ID: <raycast/extensions/pull/30314/review/5129068489
@ github. com>
|
|
@pernielsentikaer messaged you in Slack Community |
| ); | ||
| }, | ||
| [], | ||
| { onData: setCachedRows }, |
There was a problem hiding this comment.
Stale refresh restores logged-out data
If a menu-bar notification refresh overlaps a workspace logout, the request retains the pre-removal registry and onData writes its completed rows after clearWorkspaceNotificationsCache() runs, restoring the removed workspace’s private notification payloads to the unencrypted cache until a later successful refresh replaces them.
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/linear/src/hooks/useAllWorkspaceNotifications.ts
Line: 63
Comment:
**Stale refresh restores logged-out data**
If a menu-bar notification refresh overlaps a workspace logout, the request retains the pre-removal registry and `onData` writes its completed rows after `clearWorkspaceNotificationsCache()` runs, restoring the removed workspace’s private notification payloads to the unencrypted cache until a later successful refresh replaces them.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Description
Adds multi-workspace support to the Linear extension: connect any number of workspaces
(including the same workspace under two accounts), switch via a consistent pattern on
every surface (list dropdowns, a form field, a dedicated Create Issue for Myself in
Workspace quick command plus a one-shot
workspaceargument on Quick Add Comment), anew Manage Workspaces command, an aggregated menu bar, and workspace-routable AI tools
with a new
get-workspacestool. The quick commands surface their target via a liveroot-search subtitle (hidden for single-workspace users).
Single-workspace users see no behavior change: every switcher is invisible until a
second workspace is connected, and an existing login is adopted silently (no re-auth).
The only visible additions are the new Manage Workspaces and Create Issue for Myself
in Workspace commands and an optional
workspaceargument on Quick Add Comment.Closes #24297. Supersedes the PAT-based approach in #25646 — this stays entirely on
Raycast's OAuth proxy and token storage.
Approach
(
linear-ws-<orgId>-<userId>); the existinglinearslot is adopted unchanged.providerId/extraParameterstoOAuthService.linear(); untilit ships in a release, a small factory (
src/api/oauth.ts,AUTH_BACKENDswitch)constructs the service manually with the same proxy + clientId — the copied constants
are temporary by design and deleted when the utils option lands.
prompt=consent(without it Linear silently skips consent foran already-granted app). Linear's consent page has no workspace picker — the grant
follows the active workspace at linear.app, so the add flow instructs the user to
switch there first (verified empirically).
token refresh, so a transient network failure can never log a workspace out.
(revocation is authorization-scoped and eventually consistent, so an unconditional
revoke could affect the primary login).
workspaceId(each tool call runs in a fresh process, soper-call routing is the only reliable design); confirmations resolve the same client
as executions and display the target workspace.
Testing notes & known limitations
macOS only (no Windows hardware available to the author) — flagging for reviewers.
including the same workspace under two accounts): silent adoption on upgrade, add /
switch / re-authenticate / log out flows, per-workspace remembered form defaults,
cross-workspace drafts (draft pins its workspace; the global default is untouched),
menu-bar aggregation with per-workspace re-auth rows, and AI-tool workspace routing.
workspaceIdon AI tools is optional by design (absent → active workspace). If themodel never calls
get-workspacesand the user switches the default between aconfirmation and its execution, routing follows the default; mitigations are the AI
instructions, a Workspace row on every mutating confirmation, and routing evals.
dropdowns require a static list; workspaces are per-user runtime data). It matches
URL key, account email, or a unique name prefix, and never changes the default
workspace; Create Issue for Myself is deliberately pegged to the active workspace,
with the dedicated in-Workspace command for explicit targeting.
create-project-update.tsandget-initiatives.tsexist undeclared intools[]; team switch clears the CreateIssue title via the template-reset effect (upstream behavior, kept for parity).
Manage Workspaces is the primary management surface and labels each login row
(
providerName: "Linear — <org>").a failed staged-add verification matches on error text ("HTTP 401/403") — an SDK-path
auth failure in a rare race is treated as transient (retried) rather than permanent.
Screencast
<attach the Manage Workspaces add/switch screencast>