From 91859ea80693a36bdb896b18ab14ce902dfd5b98 Mon Sep 17 00:00:00 2001 From: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 Date: Mon, 3 Aug 2026 00:05:11 -0400 Subject: [PATCH] feat(desktop): plumb WorkspaceApplyResult through frontend boundary (Phase 3e) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Surfaces the degraded-apply result from the backend all the way to the UI: tauri.ts: - Add WorkspaceApplyResult interface { applied: boolean; degraded: string[] } - applyCommunity() now returns Promise instead of Promise; passes typed result through from apply_workspace command. useCommunityInit.ts: - Consumes applyResult from applyCommunity instead of discarding void. - applied: false (drain-failed) → park on the loading gate so the user can retry via a workspace switch; the specific degradation messages are shown as the error. - applied: true with degraded entries → console.warn (informational; workspace IS active; post-commit step failed gracefully). - Existing catch block still handles genuine Tauri errors (poisoned lock, invalid nsec, etc.) unchanged. Co-authored-by: Will Pfleger Signed-off-by: Will Pfleger --- .../features/communities/useCommunityInit.ts | 31 ++++++++++++++++++- desktop/src/shared/api/tauri.ts | 17 ++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/desktop/src/features/communities/useCommunityInit.ts b/desktop/src/features/communities/useCommunityInit.ts index afa69f913..d5d59a90b 100644 --- a/desktop/src/features/communities/useCommunityInit.ts +++ b/desktop/src/features/communities/useCommunityInit.ts @@ -210,13 +210,42 @@ export function useCommunityInit( // imported key. `loadCommunities()` strips lingering `nsec` fields from // legacy entries; this site refuses to apply one even if present. try { - await applyCommunity( + const applyResult = await applyCommunity( activeCommunity.relayUrl, undefined, activeCommunity.token, activeCommunity.reposDir, getOverrides().agentManagedProfiles === true, ); + + if (!applyResult.applied) { + // Drain failed; old scope is still active. Treat as a fatal apply + // error: park on the loading gate so the user can retry by switching + // workspaces again. + const reason = applyResult.degraded.join("; "); + console.error( + "[useCommunityInit] workspace apply blocked by drain failure:", + reason, + ); + if (!cancelled) { + setResult({ + isReady: false, + needsSetup: false, + appliedKey: null, + error: `Workspace switch failed (agents could not stop): ${reason}`, + }); + } + return; + } + + // Workspace applied. Log any post-commit degradation (informational — + // the workspace IS active; these are best-effort post-commit steps). + if (applyResult.degraded.length > 0) { + console.warn( + "[useCommunityInit] workspace applied with degradation:", + applyResult.degraded, + ); + } } catch (error) { // A bad `repos_dir` no longer reaches here — `apply_workspace` treats // it as non-fatal (relay/keys apply, bad value not persisted, REPOS diff --git a/desktop/src/shared/api/tauri.ts b/desktop/src/shared/api/tauri.ts index 69e2e455e..d25bfc8ee 100644 --- a/desktop/src/shared/api/tauri.ts +++ b/desktop/src/shared/api/tauri.ts @@ -1142,14 +1142,27 @@ export async function cancelPairing(): Promise { await invokeTauri("cancel_pairing"); } +/** + * Result from `apply_workspace` / `applyCommunity`. + * + * - `applied: true` — new scope committed; post-commit failures surface in + * `degraded` (informational — workspace IS active). + * - `applied: false` — drain failed; old scope still active; `degraded` names + * what could not be stopped or restored by compensation. + */ +export interface WorkspaceApplyResult { + applied: boolean; + degraded: string[]; +} + export async function applyCommunity( relayUrl: string, nsec?: string, token?: string, reposDir?: string, agentManagedProfiles?: boolean, -): Promise { - await invokeTauri("apply_workspace", { +): Promise { + return invokeTauri("apply_workspace", { relayUrl, nsec: nsec ?? null, token: token ?? null,