Slice 4: graceful project handoff (pending switch + ack)

Add pending-switch coordination to internal/activity (RequestSwitch/PendingSwitch/AckSwitch/CancelSwitch); AckSwitch atomically sets the active project and records switch-completed with Claude's summary. New MCP tools get_pending_switch and ack_switch (request is user-only via HTTP). HTTP GET/POST/DELETE /api/switch. New <handoff-bar> component (ask/waiting/cancel/completed) over SSE; activity-feed reflects switch-completed. Test covers request->ack->clear. Verified live: request/waiting/cancel over SSE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-20 08:27:22 -04:00
parent 65daedc902
commit 2b77e15b36
13 changed files with 449 additions and 10 deletions
+10 -8
View File
@@ -395,18 +395,20 @@ obeys the safety rules (§1.4).
When the user switches project/task in the app, it is a **request**, not an
instant yank. The cooperative protocol:
1. **User** picks a new project/task in the app → the app records a
**pendingswitch request** (target + optional note) and the UI shows
"waiting for Claude to reach a good stopping point."
1. **User** asks (in the app) for Claude to switch to a project →
`POST /api/switch` records a **pendingswitch request** (target + optional
note) and `<handoff-bar>` shows "waiting for Claude to reach a good stopping
point." (The request is a **useronly** action — there is no MCP tool to raise
it; Claude fulfils requests, it doesn't create them.)
2. **Claude** sees the pending request (it checks at its natural turnboundary
checkpoints via `get_pending_switch`). It **finishes to a safe stopping
point and preserves work** — never abandons uncommitted changes to switch;
it completes the inflight step and commits/stashes as appropriate — then
performs the switch (`set_active_project`, moving its working context to the
new repo) and calls **`ack_switch`** with a short summary of where it left
the previous project.
3. **App** marks the request fulfilled and **notifies the user** over SSE
("Claude switched to *ProjectB*; *ProjectA* left at: …"). The user proceeds.
calls **`ack_switch`** with a short summary of where it left the previous
project. `ack_switch` **atomically** makes the requested target the active
project and clears the request.
3. **App** records `switch-completed` and **notifies the user** over SSE
("Claude switched to *ProjectB* — *left at: …*"). The user proceeds.
**Rule:** the switch is Claudecompleted at a checkpoint, not appforced. Losing
or interrupting uncommitted work to satisfy a switch is a §1.4class violation.