From fd1c24d7c6474d1f8f4e33eca12c57dc66f61b57 Mon Sep 17 00:00:00 2001 From: Mikkel Georgsen Date: Wed, 4 Feb 2026 14:35:53 +0000 Subject: [PATCH] fix(01): revise plan 01-03 based on checker feedback Clarify subprocess persistence on session switch, mandatory auto-spawn on /session, and message queueing delegation to ClaudeSubprocess. Co-Authored-By: Claude Opus 4.5 --- .../01-session-process-foundation/01-03-PLAN.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.planning/phases/01-session-process-foundation/01-03-PLAN.md b/.planning/phases/01-session-process-foundation/01-03-PLAN.md index 5ef9293..8e95da2 100644 --- a/.planning/phases/01-session-process-foundation/01-03-PLAN.md +++ b/.planning/phases/01-session-process-foundation/01-03-PLAN.md @@ -82,8 +82,7 @@ from telegram.claude_subprocess import ClaudeSubprocess **Module-level state:** - Create a `SessionManager` instance (singleton for the bot process) -- Create a dict `subprocesses: dict[str, ClaudeSubprocess]` to track active subprocess per session -- Track `active_session: str | None` at module level (or via SessionManager) +- Create a dict `subprocesses: dict[str, ClaudeSubprocess]` to track subprocess per session — **CRITICAL: this dict persists ALL session subprocesses, not just the active one. When switching from session A to B, the subprocess for A stays alive in `subprocesses['A']` with status "suspended" via SessionManager. This implements the locked decision: "Switching sessions suspends (not kills) the current process — it stays alive. No limit on concurrent live Claude Code processes."** **New command: `/new [persona]`** @@ -105,8 +104,8 @@ Handler: `async def switch_session_cmd(update, context)` - Extract name from `context.args[0]` (required) - Validate: if no args, reply with usage and list available sessions - If session doesn't exist: reply "Session '' doesn't exist. Use /new to create it." -- Call `session_manager.switch_session(name)` -- If session has no running subprocess: spawn one (create ClaudeSubprocess, don't send message) +- Call `session_manager.switch_session(name)` — this marks the PREVIOUS session as "suspended" in metadata (subprocess stays alive in `subprocesses` dict) +- **MANDATORY auto-spawn:** After switching, check if `subprocesses.get(name)` is None or `not subprocesses[name].is_alive`. If so, create a new ClaudeSubprocess with session directory and persona, store in `subprocesses[name]`. This implements the locked decision: "Switching to a session with no running process auto-spawns Claude Code immediately." The subprocess idles waiting for first message. - Reply "Switched to session ''." (include persona if set) **Modified message handler: `handle_message`** @@ -114,8 +113,8 @@ Handler: `async def switch_session_cmd(update, context)` Replace the current implementation (which saves to inbox) with session-aware routing: - If no active session: reply "No active session. Use /new to start one." - If active session exists: - - Get or create ClaudeSubprocess for session - - Call `await subprocess.send_message(update.message.text)` + - Get or create ClaudeSubprocess for session (auto-spawn if not alive) + - Call `await subprocess.send_message(update.message.text)` — **Note: ClaudeSubprocess handles internal message queueing via asyncio.Queue (Plan 02 design). If the subprocess is busy processing a previous message, `send_message()` queues the message and it will be sent after the current response completes. The bot handler does NOT need to check `is_busy` — just call `send_message()` and the subprocess manages the queue.** - The on_output callback will need to send messages back to the chat. For Phase 1, create callbacks that send to the Telegram chat: ```python async def on_output(text):