Phase 03: Lifecycle Management - 2 plans in 2 waves - Plan 01 (wave 1): Idle timer module + session metadata + PID tracking - Plan 02 (wave 2): Suspend/resume wiring, /timeout, /sessions, startup cleanup, graceful shutdown - Ready for execution Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
6.2 KiB
| phase | plan | type | wave | depends_on | files_modified | autonomous | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 03-lifecycle-management | 01 | execute | 1 |
|
true |
|
Purpose: Foundation components needed before wiring suspend/resume into the bot. The idle timer provides per-session timeout detection, and metadata extensions store timeout configuration and subprocess PIDs.
Output: New idle_timer.py module, updated session_manager.py and claude_subprocess.py
<execution_context> @/home/mikkel/.claude/get-shit-done/workflows/execute-plan.md @/home/mikkel/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/03-lifecycle-management/03-CONTEXT.md @.planning/phases/03-lifecycle-management/03-RESEARCH.md @telegram/idle_timer.py (will be created) @telegram/session_manager.py @telegram/claude_subprocess.py Task 1: Create SessionIdleTimer module telegram/idle_timer.py Create `telegram/idle_timer.py` with a `SessionIdleTimer` class that manages per-session idle timeouts using asyncio.Class design:
__init__(self, session_name: str, timeout_seconds: int, on_timeout: Callable[[str], Awaitable[None]])-- stores config, initializes _timer_task to None, _last_activity to now (UTC)reset(self)-- updates _last_activity to now, cancels existing _timer_task if running, creates new asyncio.create_task(_wait_for_timeout())async _wait_for_timeout(self)-- awaits asyncio.sleep(self.timeout_seconds), then callsawait self.on_timeout(self.session_name). Catches asyncio.CancelledError silently (timer was reset).cancel(self)-- cancels _timer_task if running (used on shutdown/archive)@property seconds_since_activity-- returns float seconds since _last_activity@property last_activity-- returns the datetime of last activity (for /sessions display)
Use datetime.now(timezone.utc) for timestamps. Import typing for Callable, Optional, Awaitable.
Add module docstring explaining this is the idle timeout manager for session lifecycle. Log timer start/cancel/fire events at DEBUG level, timeout firing at INFO level.
python3 -c "from idle_timer import SessionIdleTimer; print('import OK')" run from telegram/ directory succeeds.
SessionIdleTimer class exists with reset(), cancel(), _wait_for_timeout(), seconds_since_activity, and last_activity. Imports cleanly.
-
In
create_session(), add"idle_timeout": 600(10 minutes default) to the initial metadata dict (alongside existing fields like name, created, last_active, persona, pid, status). -
Add a helper method
get_session_timeout(self, name: str) -> intthat reads metadata and returnsmetadata.get('idle_timeout', 600). This provides a clean interface for the bot to query timeout values. -
No changes to list_sessions() -- it already returns full metadata which will now include idle_timeout.
claude_subprocess.py changes:
-
Add a
@property pid(self) -> Optional[int]that returnsself._process.pid if self._process and self._process.returncode is None else None. This lets the bot store the PID in session metadata for orphan cleanup on restart. -
In
start(), after successful subprocess spawn, store the PID in aself._pidattribute as well (for access even after process terminates, useful for logging). Keep the property returning live PID only.
These are minimal, targeted changes. Do NOT refactor existing code. Do NOT change the terminate() method or any existing logic.
python3 -c "from session_manager import SessionManager; sm = SessionManager(); print('SM OK')" and python3 -c "from claude_subprocess import ClaudeSubprocess; print('CS OK')" both succeed from telegram/ directory.
Session metadata includes idle_timeout (default 600s). SessionManager has get_session_timeout() method. ClaudeSubprocess has pid property returning live process PID.
<success_criteria>
- idle_timer.py exists with SessionIdleTimer class implementing asyncio-based per-session idle timeout
- session_manager.py creates sessions with idle_timeout=600 in metadata and has get_session_timeout() helper
- claude_subprocess.py exposes pid property for PID tracking
- All three modules import without errors </success_criteria>