Problem
After a machine restart or Tower crash, builders don't auto-recover — only architects do. Users have to know to run afx workspace recover --apply from the CLI to revive them, and most users don't discover this until their in-flight work goes missing. The command works (issue #829), but discoverability is the real gap.
Design context: the asymmetry between architect auto-recovery and manual builder recovery is intentional (see packages/codev/src/agent-farm/commands/workspace-recover.ts for the safety gates: eligibility filtering, dry-run default, per-protocol revivability). This issue does not change the recovery discipline. It only surfaces the CLI's dry-run result in the VS Code sidebar so users can recover from a click instead of a command they didn't know existed.
Scope
Placement — Workspace view, standalone section
A new collapsible section in the Codev sidebar's WORKSPACE view, sibling to Architects:
WORKSPACE
├─ Architects
│ ├─ MAIN
│ ├─ REVIEWER
│ └─ + Add Architect
├─ Recoverable Builders (3) [Recover All] header context menu
│ ├─ pir-1149 · Claude crash-loop fix · 2h ago [Recover] [Cleanup]
│ ├─ pir-1150 · Sibling resurrect · 2h ago [Recover] [Cleanup]
│ └─ pir-1151 · ... · 2h ago [Recover] [Cleanup]
├─ Spawn Builder
└─ New Shell
Visibility: section is hidden entirely when the recoverable set is empty. Appears when non-empty.
Row shape: <builder-id> · <issue title if resolvable> · <age since last activity> plus per-row Recover and Cleanup actions (context-menu items).
Ineligible builders are NOT displayed. Only builders that would appear in afx workspace recover --apply's eligible set. Ineligible (terminal phase, unsupported protocol, too old) are omitted entirely to keep the section focused.
Header actions:
Recover All — bulk-revive every row in the section. Same underlying operation as per-row, batched.
Refresh model — one-shot check on extension activation only
Rationale: recoverable builders only appear after Tower had to cold-boot from an outage. During normal operation (Tower alive), the recoverable set is empty. Fresh recoverables only accumulate when a machine restart happens, at which point VS Code and the extension are re-launching too.
Implementation:
- Extension activation → single
GET /api/recover-preview fetch → renders section if non-empty
- No SSE subscription for
builders-recoverable-changed (out of scope; unnecessary for the intended failure mode)
- After user takes an action (Recover / Cleanup), the section refreshes via a follow-up preview fetch
- Not re-checked on Tower disconnect/reconnect within a session — reduces noise and matches the "restart-triggered" use case
Out of scope: any auto-refresh mechanism. If Tower crashes mid-session and rebuilds recoverable state without a VS Code restart, the sidebar will not show it until the extension is reactivated. Acceptable trade-off for the common case.
Notification — once per session, on activation
If the initial GET /api/recover-preview returns a non-empty set:
- Show a VS Code information notification: "N builders are stopped from your workspace after a Tower restart. Recover them?"
- Actions:
Recover All, View, Dismiss
View reveals + expands the Workspace view's Recoverable Builders section
Recover All runs the bulk revive
Dismiss closes without another notification this session
- Notification fires at most once per extension activation (a
globalState-tracked session flag)
Backend — Tower HTTP endpoints
Two new endpoints, both scoped to the current workspace:
GET /api/recover-preview?workspace=<path> — returns the recoverable set in JSON, using the same eligibility logic as afx workspace recover dry-run.
- Response shape:
{ recoverable: [{ id, issueId?, issueTitle?, protocol, worktreePath, ageDays }] }
- Ineligible builders excluded server-side
POST /api/recover?workspace=<path> — triggers revival. Body: { ids: string[] } (single or bulk). Response: per-id success/error map.
POST /api/cleanup-builder?workspace=<path> — removes a builder from state without revival. Body: { id: string }.
Refactor: workspace-recover.ts currently tangles eligibility with dry-run rendering + CLI shell-out. Extract the eligibility predicate into a shared module (workspace-recover-eligibility.ts or similar) so the endpoint and CLI both consume it. No behavior change to the CLI itself.
Auth posture: no change. Tower's model is localhost-only. Standard warning: don't enable BRIDGE_MODE=1 — these endpoints do real destructive work and Tower has no application-layer auth.
Prerequisites and coordination
Acceptance
- After a Tower restart with 3 builders in the workspace,
WORKSPACE > Recoverable Builders (3) renders on the next VS Code activation
- Section is hidden entirely when the recoverable set is empty
- Notification fires exactly once on activation with a non-empty set; suppressed on re-activation the same session after Dismiss
Recover All from the header revives every row; per-row Recover revives one
Cleanup removes a builder from state without spawning
- Ineligible builders (terminal phase, unsupported protocol, too old) never appear
- After any action, the section re-fetches its state via the preview endpoint
- CLI
afx workspace recover output is unchanged (uses the same shared eligibility module)
Out of scope
- SSE-based real-time refresh of the section
- Cross-workspace recoverable view (only current workspace)
- Any change to the auto-revive discipline (architects continue to auto-recover, builders remain user-triggered)
- Any change to the CLI's dry-run / --apply / max-age / --include-stale semantics
- Any change to Tower's HTTP auth model
Related
Problem
After a machine restart or Tower crash, builders don't auto-recover — only architects do. Users have to know to run
afx workspace recover --applyfrom the CLI to revive them, and most users don't discover this until their in-flight work goes missing. The command works (issue #829), but discoverability is the real gap.Design context: the asymmetry between architect auto-recovery and manual builder recovery is intentional (see
packages/codev/src/agent-farm/commands/workspace-recover.tsfor the safety gates: eligibility filtering, dry-run default, per-protocol revivability). This issue does not change the recovery discipline. It only surfaces the CLI's dry-run result in the VS Code sidebar so users can recover from a click instead of a command they didn't know existed.Scope
Placement — Workspace view, standalone section
A new collapsible section in the Codev sidebar's
WORKSPACEview, sibling toArchitects:Visibility: section is hidden entirely when the recoverable set is empty. Appears when non-empty.
Row shape:
<builder-id> · <issue title if resolvable> · <age since last activity>plus per-rowRecoverandCleanupactions (context-menu items).Ineligible builders are NOT displayed. Only builders that would appear in
afx workspace recover --apply's eligible set. Ineligible (terminal phase, unsupported protocol, too old) are omitted entirely to keep the section focused.Header actions:
Recover All— bulk-revive every row in the section. Same underlying operation as per-row, batched.Refresh model — one-shot check on extension activation only
Rationale: recoverable builders only appear after Tower had to cold-boot from an outage. During normal operation (Tower alive), the recoverable set is empty. Fresh recoverables only accumulate when a machine restart happens, at which point VS Code and the extension are re-launching too.
Implementation:
GET /api/recover-previewfetch → renders section if non-emptybuilders-recoverable-changed(out of scope; unnecessary for the intended failure mode)Out of scope: any auto-refresh mechanism. If Tower crashes mid-session and rebuilds recoverable state without a VS Code restart, the sidebar will not show it until the extension is reactivated. Acceptable trade-off for the common case.
Notification — once per session, on activation
If the initial
GET /api/recover-previewreturns a non-empty set:Recover All,View,DismissViewreveals + expands the Workspace view's Recoverable Builders sectionRecover Allruns the bulk reviveDismisscloses without another notification this sessionglobalState-tracked session flag)Backend — Tower HTTP endpoints
Two new endpoints, both scoped to the current workspace:
GET /api/recover-preview?workspace=<path>— returns the recoverable set in JSON, using the same eligibility logic asafx workspace recoverdry-run.{ recoverable: [{ id, issueId?, issueTitle?, protocol, worktreePath, ageDays }] }POST /api/recover?workspace=<path>— triggers revival. Body:{ ids: string[] }(single or bulk). Response: per-id success/error map.POST /api/cleanup-builder?workspace=<path>— removes a builder from state without revival. Body:{ id: string }.Refactor:
workspace-recover.tscurrently tangles eligibility with dry-run rendering + CLI shell-out. Extract the eligibility predicate into a shared module (workspace-recover-eligibility.tsor similar) so the endpoint and CLI both consume it. No behavior change to the CLI itself.Auth posture: no change. Tower's model is localhost-only. Standard warning: don't enable
BRIDGE_MODE=1— these endpoints do real destructive work and Tower has no application-layer auth.Prerequisites and coordination
packages/codev/src/agent-farm/servers/) and shared between CLI + extension.packages/vscode/src/views/+packages/vscode/src/hooks/(per the existing view pattern).Acceptance
WORKSPACE > Recoverable Builders (3)renders on the next VS Code activationRecover Allfrom the header revives every row; per-rowRecoverrevives oneCleanupremoves a builder from state without spawningafx workspace recoveroutput is unchanged (uses the same shared eligibility module)Out of scope
Related
afx workspace recoverimplementation