# Flowchart: session-lifecycle-management
## Sources Consulted
- `src/services/worker/SessionManager.ts:1-678`
- `src/services/worker/ProcessRegistry.ts:1-528`
- `src/services/queue/SessionQueueProcessor.ts:1-149`
- `src/services/sqlite/PendingMessageStore.ts:1-150`
- `src/supervisor/process-registry.ts:175-409`
- `src/services/worker-service.ts:173-174, 508-560, 1100-1111`
## Happy Path Description
1. HTTP request (SessionRoutes) triggers `SessionManager.initializeSession(sessionDbId)` (SessionManager.ts:118).
2. ActiveSession created in-memory with AbortController; stale memorySessionId cleared from DB (205-235).
3. SDK subprocess spawned via `createPidCapturingSpawn` → registered in supervisor ProcessRegistry (393, 57, supervisor/process-registry.ts:223).
4. Observations persisted to `PendingMessageStore` (claim-confirm) before processing (SessionManager.ts:276, PendingMessageStore.ts:63).
5. `SessionQueueProcessor.createIterator` yields messages via EventEmitter; resets stale-processing >60s on claim (SessionQueueProcessor.ts:32, PendingMessageStore.ts:99).
6. SDKAgent consumes iterator, updates `lastGeneratorActivity` per yield (SessionManager.ts:666).
7. Messages confirmed only after successful DB commit (prevents loss on crash).
8. Idle timeout (3 min) → `onIdleTimeout` → `session.abortController.abort()` → generator exits → session deleted (SessionManager.ts:651-655, 381).
9. Stuck-generator detection (5 min inactive) → `reapStaleSessions` SIGKILLs subprocess (516-568, 535).
10. Orphan reaper (30s) cleans dead sessions + system orphans + idle daemon children (ProcessRegistry.ts:349).
## Mermaid Flowchart
```mermaid
flowchart TD
A["SessionRoutes triggers init"] --> B["SessionManager.initializeSession
SessionManager.ts:118"]
B --> C{In memory?}
C -->|Yes| D["Return cached"]
C -->|No| E["Create ActiveSession
SessionManager.ts:205-235"]
E --> F["Clear stale memorySessionId
SessionManager.ts:206-214"]
D --> G["SDKAgent.generateResponse
SessionManager.ts:631-670"]
F --> G
G --> H["createPidCapturingSpawn
ProcessRegistry.ts:393"]
H --> I["registerProcess
ProcessRegistry.ts:57"]
I --> J["supervisor.registerProcess
supervisor/process-registry.ts:223"]
K["queueObservation
SessionManager.ts:276"] --> L["PendingMessageStore.enqueue
PendingMessageStore.ts:63"]
L --> M["INSERT pending_messages status=pending"]
M --> N["emit 'message'"]
G --> O["getMessageIterator
SessionManager.ts:631"]
O --> P["SessionQueueProcessor.createIterator
SessionQueueProcessor.ts:32"]
P --> Q["claimNextMessage
PendingMessageStore.ts:99"]
Q --> R["Reset processing>60s → pending
PendingMessageStore.ts:107-116"]
R --> S["UPDATE status=processing"]
S --> T["Yield message
SessionManager.ts:648"]
T --> U["lastGeneratorActivity=now
SessionManager.ts:666"]
U --> V["SDK agent stores → confirmProcessed DELETE"]
V --> Q
Q -->|empty| Y["waitForMessage signal
SessionQueueProcessor.ts:116"]
Y --> Z{idle >= 3min?}
Z -->|Yes| AA["onIdleTimeout
SessionManager.ts:651"]
AA --> AB["abortController.abort"]
AB --> AC["Generator exits"]
AC --> AD["Auto-unregister on exit
ProcessRegistry.ts:479"]
AC --> AF["SessionManager.deleteSession
SessionManager.ts:381"]
AF --> AG["await generatorPromise 30s
SessionManager.ts:392-403"]
AF --> AH["ensureProcessExit 5s
ProcessRegistry.ts:185"]
AH -->|still alive| AI["SIGKILL escalation"]
AF --> AJ["supervisor reapSession SIGTERM→5s→SIGKILL
supervisor/process-registry.ts:292"]
AF --> AL["sessions.delete + queues.delete
SessionManager.ts:433-434"]
AL --> AM["onSessionDeletedCallback"]
AN["staleSessionReaperInterval 2min
worker-service.ts:547"] --> AO["iterate active sessions
SessionManager.ts:516-568"]
AO --> AP{idle > 5min?}
AP -->|Yes| AQ["detectStaleGenerator
SessionManager.ts:59"]
AQ --> AR["SIGKILL
SessionManager.ts:535"]
AR --> AS["abortController.abort"]
AO --> AU{idle > 15min?
no generator + no pending}
AU -->|Yes| AF
AW["startOrphanReaper 30s
ProcessRegistry.ts:508"] --> AX["reapOrphanedProcesses
ProcessRegistry.ts:349"]
AX --> AY["getActiveSessionIds"]
AY --> AZ["Kill orphan PIDs"]
AX --> BB["killSystemOrphans ppid=1
ProcessRegistry.ts:315"]
AX --> BC["killIdleDaemonChildren
ProcessRegistry.ts:244"]
```
## Timer Inventory
| Timer | Purpose | Lifetime | Cleared On | Location |
|---|---|---|---|---|
| `waitForMessage()` setTimeout | Wait for next message or idle | Per message | clearTimeout or abort | SessionQueueProcessor.ts:145 |
| Idle timeout | Trigger onIdleTimeout at 3min | Per iterator session | resolves or signal aborts | SessionQueueProcessor.ts:130 |
| `staleSessionReaperInterval` | Reap stuck gens (5min) + old sessions (15min) | Worker lifetime | clearInterval on shutdown | worker-service.ts:547, 1108 |
| Orphan reaper (`startOrphanReaper`) | Kill dead-session procs, orphans, idle daemons | Worker lifetime | clearInterval returned | ProcessRegistry.ts:508 |
| Stale-processing self-heal | Atomic UPDATE reset >60s | Per claim (inline SQL) | n/a | PendingMessageStore.ts:106 |
| Generator-exit wait | 30s timeout on deleteSession | Per delete | AbortSignal.timeout + Promise.race | SessionManager.ts:397 |
| `ensureProcessExit` | 5s before SIGKILL | Per delete | setTimeout for escalation | ProcessRegistry.ts:200 |
## Side Effects
- Process registration persisted to supervisor.json.
- PendingMessage lifecycle persisted to SQLite (INSERT → UPDATE → DELETE).
- AbortController cascades through iterator.
- Pool-slot notification on process exit.
- Broadcast callbacks on session delete.
## External Feature Dependencies
**Calls into:** SQLite (pending_messages + sessions), supervisor ProcessRegistry, SDKAgent, RestartGuard, SSEBroadcaster.
**Called by:** SessionRoutes, DataRoutes, worker-service lifecycle (reapers, shutdown).
## Confidence + Gaps
**High:** Happy path; stale detection thresholds (5min generator, 15min session); 3-min idle timeout; 30s orphan reaper; claim-confirm; supervisor-delegated registry model.
**KNOWN GAPS (critical for duplication analysis):**
1. **ProcessRegistry duplication:** YES — two files exist:
- `src/services/worker/ProcessRegistry.ts` — worker-level facade
- `src/supervisor/process-registry.ts` — supervisor-level persistent registry
- NOT fully independent; worker-level delegates via `getSupervisor().getRegistry()`. But there is real surface-area duplication.
2. **staleSessionReaperInterval vs startUnifiedReaper:**
- `staleSessionReaperInterval` is ACTIVE at worker-service.ts:547.
- `startUnifiedReaper` NOT present in codebase search — observation notes suggest T31/T32 refactor planned to unify the two reapers but NOT yet implemented.
- Currently TWO independent reapers: `startOrphanReaper` (30s) + stale-session reaper (2min). Unification pending.
3. **MAX_SESSION_IDLE_MS (15 min)** is used only by reapStaleSessions — may be deprecated but code still in place.