feat(desktop): plumb WorkspaceApplyResult through frontend boundary (Phase 3e)

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<WorkspaceApplyResult> instead of
  Promise<void>; 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 <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
This commit is contained in:
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7
2026-08-03 00:05:11 -04:00
co-authored by Will Pfleger
parent 00a3381902
commit 91859ea806
2 changed files with 45 additions and 3 deletions
@@ -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
+15 -2
View File
@@ -1142,14 +1142,27 @@ export async function cancelPairing(): Promise<void> {
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<void> {
await invokeTauri("apply_workspace", {
): Promise<WorkspaceApplyResult> {
return invokeTauri<WorkspaceApplyResult>("apply_workspace", {
relayUrl,
nsec: nsec ?? null,
token: token ?? null,