docs(changelog): v2.6.7-interrupt-guard + reconcile roadmap/review/openspec

CHANGELOG entry for v2.6.7. Plus the session's doc reconciliation: roadmap shipped record synced through v2.6.7 (v2.3 lifecycle marked shipped, relicense AGPL->MIT batch, fork-sweep lift items, claude-agent-sdk SessionStore, ACP package fix); boocode_code_review_v2 (two fork sweeps, relicense decision = 3 AGPL files, jinja gate green); openspec v2-3 reconciled to shipped (v2.5.4-v2.5.13); openspec v2-6 Phase 0/1 + P1.5 shipped, F.1 done, remaining-phase plan + lift sources.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-31 21:31:47 +00:00
parent 372651bcb1
commit 457010391a
9 changed files with 234 additions and 135 deletions

View File

@@ -3,6 +3,8 @@
Reference implementations: `/opt/forks/opencode` (server + SDK),
`/opt/forks/paseo` (warm ACP + opencode server-manager + reasoning dedup).
> **⚠️ Reconciled 2026-05-31 — read the proposal's Reconciliation note first.** §2a and §3 describe the *original* design; four details were revised during implementation (per-session SSE; `(chat_id, agent)` key + `worktrees` table; `session.next.*` events; password deferred) — flagged inline. **Phases 23 and the Phase-1 UX (§2b, §6, §9) are not yet built**; updated lift sources for them are in new **§10**.
## 1. Architecture overview
```
@@ -47,6 +49,8 @@ interface AgentBackend {
### 2a. OpenCodeServerBackend (shared HTTP server)
> **⚠️ Shipped deltas vs the bullets below:** (a) **per-session SSE** — one `event.subscribe({directory})` per live opencode session (P1.5-a, `v2.6.2`), NOT one global `/event` loop; (b) events are **`session.next.*`** (`text.delta`/`reasoning.delta`/`tool.{called,success,failed}`), NOT `message.part.*`; (c) **`OPENCODE_SERVER_PASSWORD` deferred** — server binds loopback unsecured.
- **Spawn once per BooCoder process:** `opencode serve --hostname 127.0.0.1 --port <p>`
with `OPENCODE_SERVER_PASSWORD=<random-at-boot>` (verified: `serve.ts`, `network.ts`;
default port 4096, prints `opencode server listening on http://…`). Use the official
@@ -82,6 +86,8 @@ interface AgentBackend {
## 3. Data model
> **⚠️ Shipped (P1.5-b, `v2.6.3``v2.6.4`):** `agent_sessions` is keyed **`(chat_id, agent)`** (the tab/chat is the agent-context unit; `chat_id` CASCADEs from `chats`), and a first-class **`worktrees`** table (one-per-session, survives session delete via `session_id` `SET NULL`) replaced `session_worktrees`. `tasks.chat_id` threads the tab id to the dispatcher. The SQL below is the original `(session_id, agent)` / `session_worktrees` shape — see `apps/coder/src/schema.sql` for the live DDL.
Agent switching is **free** within a chat (the picker is per-turn, not locked), so
the worktree is shared across agents but each agent keeps its own backend session.
That splits into two tables: one **shared worktree per chat**, and one **backend
@@ -281,3 +287,12 @@ over the new `agent` column and `agent_sessions` — no dispatch-logic change.
"opencode's edits live in its worktree — boocode won't see them until applied."
Derived purely from per-change `agent` + current `value.provider`; no new state.
Keeps the §3a staging caveat from biting silently.
## 10. Lift sources for the remaining phases (added 2026-05-31)
From the second external review (`boocode_code_review_v2.md`). These supersede/augment §2b, §6, §9 for the unbuilt work:
- **Phase 2 (warm ACP, goose/qwen) — `qwen --acp` is a validated reference.** qwen-code ships a real stdio multi-session ACP agent (`Map<sessionID,Session>`, `loadSession`/`unstable_resumeSession`, mid-session model/mode switch), so `warm-acp.ts` (§2b) wires qwen into the existing `acp-dispatch.ts` stack as planned. **Caveat:** goose ACP exposes **no `loadSession`/resume** → its cross-restart resume needs a different design than opencode's (re-`session/new` + accept memory loss, or replay). Cross-check qwen's `@agentclientprotocol/sdk@^0.14` vs BooCode's `^0.22` handshake before relying on `unstable_resumeSession`. (`boocode_code_review_v2.md` §5f, §5n.)
- **Phase 3 (lifecycle hardening) — lift from `openchamber` (MIT, same warm-opencode-server architecture), not Paseo.** Health-monitor + crash auto-restart + busy-aware restart (skip-while-busy + stale-grace) + port reclaim (`killProcessOnPort`/`waitForPortRelease`) + stall-detecting SSE reader — a concrete state machine for §6's "supervise children / rebuild on next turn" sketch. Worktree reaper: Paseo's worktree-archive cascade (soft-delete + `Promise.allSettled` fan-out) + superset's destroy-saga (preflight dirty/unpushed inspect + ordered failure semantics). Bound the warm server's per-session Maps (LRU) — long-lived-daemon leak class. (`boocode_code_review_v2.md` §5c, §5b, §5j.)
- **Fix-next (Phase 1/2) — the post-interrupt stale-terminal bug (confirmed live).** `opencode-server.ts:~307` settles any `session.idle` onto whatever `activeTurn` holds the session slot, with **no turn-identity guard** → after abort + new prompt, a stale `session.idle` from the cancelled turn settles the *new* turn early as success. Paseo fix `1d38aac` (suppress-terminal-until-next-user-message). **Now one-click reachable** since `v2.6.5` shipped the Send→Stop composer. (`boocode_code_review_v2.md` §1 #6, §3.)
- **Phase 1 UX (§9) — opencode already streams token/ctx usage.** `session.next.step.ended` carries `{tokens, cost}` on the wire (SDK already installed) → consume it to fill ctx/token usage for opencode sessions, closing the "no usage for external agents" gap; surfaces beside the §9b chip. (`boocode_code_review_v2.md` §1 #8, §3.)