From 08118ec6b8cde714a8bcd58a66dd1e5ef03a1e2e Mon Sep 17 00:00:00 2001
From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Date: Mon, 19 Oct 2026 17:45:00 -0700
Subject: [PATCH] fix(linear): show a saved Linear credential that cannot be
decrypted (#28867)
* fix(linear): show a saved Linear credential that cannot be decrypted
The new-workspace Linear search treated a decrypt failure as an empty result, so a connected workspace still said to start typing. Record the existing decrypt message and offer Settings.
* fix(linear): open Linear settings without an Active Server read
The settings helper read activeRuntimeEnvironmentId, which the focus-read check counts as the Active Server setting. Resolve the source row's host id instead, which is the same host the helper already used.
* fix(linear): carry a Linear read's credential error to the composer
The composer showed "Start typing to search Linear issues." when the saved Linear credential could not be decrypted. The previous attempt wrote the read failure into the host-wide Linear status, which describes the Active Server and every workspace on it, while composer reads run on the repo's host and selected workspace. That caused search loops, blocked healthy hosts and workspaces, and a latch that stopped retrying.
Now Linear search and list reads reject with the plain decrypt message instead of resolving empty, the composer's URL lookup asks issue reads to do the same, and the composer keeps that error per request: it clears when the next read starts and shows in the Linear and Smart tabs (and the attach-a-link search) with a Settings link that opens the Linear card on the read's host. Nothing writes read failures into the shared status any more. The Tasks page keeps showing no rows plus the error, as before.
* fix(linear): keep attach actions and a steady error on Linear read failures
- Type the composer test's repo fixture so CI's typecheck passes.
- Attach a link: a Linear read error no longer hides the status row. The Jira-URL mode switch and the Linear-URL Retry stay; only the duplicate sentence is dropped when the empty list already shows it.
- Composer: the status refresh after a first credential failure re-runs the same read. Keep the error on screen through that re-read instead of flashing a loading skeleton.
* fix(linear): keep the attach status row in place; share the integrations Settings path
- Attach a link: keep the status row mounted (empty) when the result list already shows the Linear read error, so the Reference type button doesn't jump across the row.
- Composer: Jira and Linear Settings buttons now share one path that picks the source's host with getTaskSourceRuntimeSettings, so the Linear one no longer re-implements it. No behavior change.
* fix(linear): make the Linear Settings action a list row; keep Jira URLs on the Linear tab quiet
- Composer: the credential error and its Settings action are one list row (red sentence plus "Settings"). It fits the capped results popover and is reached with the arrow keys like every other row; Enter on it opens Settings. It is never highlighted by default, so a plain Enter still moves to the agent picker.
- Attach a link: on the Linear tab, a pasted Jira URL shows only the existing "Select Smart or Jira" status, as before; the Linear search of the URL text no longer adds a red Linear error beside it.
* fix(linear): show the Linear read error as one plain line with no Settings action
The Settings action inside the results list needed its own highlight and Enter rules and still could not fit the capped popover. The list now shows one compact red line, in the Linear and Smart tabs and in attach-a-link: "Orca can't read your saved Linear login. Reconnect Linear in Settings to search issues." It names the fix in the same words as the neighbouring not-connected hint. The highlight resolver, the input's Enter handling, the composer's Settings handlers and the prop plumbing are back to main.
* fix(linear): show an unreadable Linear login as a Reconnect Linear empty state on the Linear tab only
The composer's Smart tab no longer mentions Linear's login; the Linear tab, Edit workspace links and the Tasks page show a calm title and sentence where results would be, matching the not-connected state.
* fix(linear): drive the Tasks Reconnect card from the selected scope's read; plain words in workspace notices
Host-wide status flags any broken workspace, so a healthy selected workspace with no results no longer shows Reconnect Linear. Projects and Views notices say the unreadable login in plain words.
* fix(linear): keep the Tasks Reconnect card up while the same list re-reads
The list effect cleared its error on every re-run, so a refresh flashed the raw status bar until the read failed again. Clear it on success instead.
* refactor(linear): drop the unreachable typed-row padding from the Linear tab's Reconnect state
The Linear tab never has a typed-text row, so the state always uses the roomy padding.
* fix(linear): derive the Tasks Reconnect card from the plain list's read error in the board
Avoids widening the selection model's existing type assertion; project and view tabs report errors inside their results.
* fix(linear): say the unreadable Linear login in one short line
---
.../linear-project-view-surfaces.tsx | 9 +-
...ourceResultList.linear-read-error.test.tsx | 119 ++++++++++++++++++
.../SmartWorkspaceSourceResultList.tsx | 21 +++-
.../use-smart-workspace-secondary-searches.ts | 37 +++++-
.../use-work-item-source-state.ts | 3 +
.../sidebar/WorkspaceAttachmentSearch.tsx | 5 +-
.../WorkspaceAttachmentSourceStatus.tsx | 13 +-
.../task-page/linear/IssueBoard.tsx | 26 +++-
.../use-task-page-linear-list-effects.ts | 12 +-
src/renderer/src/i18n/locales/en.json | 7 +-
src/renderer/src/i18n/locales/es.json | 7 +-
src/renderer/src/i18n/locales/fr.json | 7 +-
src/renderer/src/i18n/locales/ja.json | 7 +-
src/renderer/src/i18n/locales/ko.json | 7 +-
src/renderer/src/i18n/locales/zh.json | 7 +-
.../src/lib/linear-issue-url-lookup.ts | 22 +++-
.../linear-credential-error-recovery.test.ts | 33 ++---
.../linear/linear-issue-detail-actions.ts | 16 ++-
.../linear/linear-issue-list-actions.ts | 26 +++-
.../slices/linear/linear-slice-contract.ts | 2 +
20 files changed, 331 insertions(+), 55 deletions(-)
create mode 100644 src/renderer/src/components/new-workspace/SmartWorkspaceSourceResultList.linear-read-error.test.tsx
diff --git a/src/renderer/src/components/linear-project-view-surfaces.tsx b/src/renderer/src/components/linear-project-view-surfaces.tsx
index 11525ed142d..159caf943af 100644
--- a/src/renderer/src/components/linear-project-view-surfaces.tsx
+++ b/src/renderer/src/components/linear-project-view-surfaces.tsx
@@ -10,6 +10,7 @@ import type {
LinearProjectSummary
} from '../../../shared/linear/project-types'
import type { LinearWorkspaceError } from '../../../shared/linear/workspace-types'
+import { isIntegrationCredentialDecryptionError } from '../../../shared/integration-credential-errors'
import {
LinearCustomViewTableContent,
type LinearCustomViewTableContentProps
@@ -59,7 +60,13 @@ export function LinearCollectionNotice({
{errors.map((error) => (
- {error.workspaceName ?? error.workspaceId}: {error.message}
+ {error.workspaceName ?? error.workspaceId}:{' '}
+ {isIntegrationCredentialDecryptionError(error.message)
+ ? translate(
+ 'auto.components.linear.project.view.surfaces.linearLoginUnreadable',
+ 'Unable to read saved Linear login. Reconnect it in Settings.'
+ )
+ : error.message}
))}
+ {translate(
+ 'auto.components.new.workspace.SmartWorkspaceNameField.linearLoginUnreadableBody',
+ 'Unable to read saved Linear login. Reconnect it in Settings.'
+ )}
+