From 8fbb506b05a0c07eaee96dccceaf8db8aafe5bad Mon Sep 17 00:00:00 2001 From: Yuto Nishida Date: Wed, 19 Aug 2026 00:04:49 -0700 Subject: [PATCH] [papyrus] Adopt `spawn from-id`: record-first, retire orphan machinery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Record the durable session before spawning, then run a uniform `silverwood spawn from-id --workstream ` for every tab — silverwood now decides the kind and first-run vs resume. Forward CLAUDE_CONFIG_DIR in the spawn seed so it can locate Claude's transcripts. Retire the automatic orphan-detection that record-first makes obsolete: the buildNode transient-tab projection, the NO_CONVERSATION_MARKER scraping and on-exit doctoring, the POST …/doctor endpoint and its silverwood.ts wrapper, and the client's orphan-delete UI (disconnectReason/doctorKind). A never-prompted claude session now simply starts fresh on reconnect; explicit per-tab delete stays. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01DY9N9STx6HMGtSEJauX1KG --- .../papyrus/client/src/components/Sidebar.tsx | 55 +----- .../papyrus/client/src/stores/useStore.ts | 5 - experimental/papyrus/server/routes/api.ts | 176 ++++++------------ .../papyrus/server/services/sessionManager.ts | 84 +++------ .../papyrus/server/services/silverwood.ts | 17 -- 5 files changed, 83 insertions(+), 254 deletions(-) diff --git a/experimental/papyrus/client/src/components/Sidebar.tsx b/experimental/papyrus/client/src/components/Sidebar.tsx index 1d450c9..7762866 100644 --- a/experimental/papyrus/client/src/components/Sidebar.tsx +++ b/experimental/papyrus/client/src/components/Sidebar.tsx @@ -284,42 +284,6 @@ export function Sidebar() { } }; - // Doctor + conditionally delete a session that failed to resume. The server runs - // `silverwood session doctor` and only removes the session if no conversation exists - // on disk; a removed tab vanishes on the next reconcile. - const doctorSession = async (tab: SessionTab) => { - if (!selectedNodeId) return; - setBusy(true); - setConnectError(null); - try { - const res = await fetch(`/api/sessions/${selectedNodeId}/sessions/${tab.sessionId}/doctor`, { - method: "POST", - }); - const data = await res.json().catch(() => ({})); - if (!res.ok) { - setConnectError(data.error || "Failed to check session"); - return; - } - if (data.removed) { - setPending((p) => { - const n = { ...p }; - delete n[tab.sessionId]; - return n; - }); - } else { - setConnectError( - data.conversationExists - ? "A saved conversation exists — try Connect again." - : "Session left in place.", - ); - } - } catch (e: any) { - setConnectError(e.message); - } finally { - setBusy(false); - } - }; - const displayColor = editColor || session?.customColor || session?.color || "#888"; // Workstream-level indicator: silverwood's algebraic state, plus the N/M-connected // agent count for a Ready Basic workstream. @@ -685,7 +649,7 @@ export function Sidebar() {

{activeTab.kind === "plain-shell" ? "Disconnected — connecting opens a new shell in the checkout." - : "Disconnected — connecting resumes this Claude Code conversation."} + : "Disconnected — connecting resumes this Claude Code conversation (or starts it fresh if none was saved)."}

{activeTab.lock && !activeTab.lock.mine ? ( @@ -713,23 +677,6 @@ export function Sidebar() { Connect )} - {activeTab.disconnectReason === "no-conversation" && - activeTab.doctorKind === "claude-code" && ( -
-

- No saved conversation was found on disk for this session. -

- -
- )} {activeTab.kind === "plain-shell" && (