# CTM Restart Lifecycle Architecture

CTM restart must make the HTTP process replaceable quickly while preserving
browser-side tab state and terminal snapshots. The critical path is intentionally
small: acknowledge the restart request, stop accepting new connections, drain
or force-close network handles, checkpoint SQLite, stop child processes, and
exit for launchd.

## Phases

1. **Announce**
   - `/api/restart/ctm` sends `server-restarting` to connected browsers before
     any sockets are closed.
   - Browsers should keep local tab, route, draft, and terminal cache state while
     reconnecting.

2. **Stop Intake**
   - CTM calls `server.close()` for each listener so no new HTTP requests are
     accepted.
   - Idle HTTP keep-alive sockets are closed immediately.
   - Remaining HTTP sockets are force-closed after a bounded grace window.

3. **Close Upgraded Sockets**
   - The WebSocket server runs in `noServer` mode, so HTTP listener close does
     not own upgraded WebSocket connections.
   - CTM closes each tracked WebSocket with service-restart code `1012`, waits a
     short grace window for the close handshake, then terminates stragglers.

4. **Stop Background Work**
   - Scheduler, session capture, session stream, and JSONL watcher stop before
     PTYs are killed.
   - CTM DB-owner queued writes are drained with a timeout. Timed-out work is
     logged with active/pending labels instead of blocking restart indefinitely.

5. **Persist And Exit**
   - CTM performs a passive WAL checkpoint, closes its SQLite connection, kills
     PTYs, stops Wall-E, then exits non-zero so launchd starts the replacement.

## Startup Contract

The new process should listen before non-critical reconciliation work starts.
Session metadata and cached terminal state are the only startup-critical data.
Heavy work such as permission-file reconciliation, JSONL full scans, prompt
harvest, conversation import, FTS backfill, and title/model refresh belongs
behind timers or scheduler jobs with idle checks and bounded batches.

## Restored Session Runtime State

Restart restore has two separate signals:

- **Runtime liveness**: CTM has spawned or is spawning a PTY so the tab can be
  used again.
- **Semantic activity**: the agent produced new output, or the user submitted
  new input that can produce new output.

The UI must not merge these signals. During startup, restored PTYs are marked
`resuming` and their initial replay output is quarantined for a bounded restore
window. That replay may hydrate the browser terminal, but it must not update
`lastActivity`, `lastPtyActivity`, SessionStream activity, or the live status
bus. If no real provider output arrives, the session naturally falls back to
`idle` when the restore window expires. If the user types into the session, the
restore quarantine ends immediately because any following output belongs to the
new turn.

This keeps CTM restart behavior predictable:

1. Restart begins: existing sessions are not promoted to fresh running work.
2. Restore starts: session status is `resuming`.
3. Restore replay draws: terminal cache may update, activity freshness does not.
4. No new work arrives: status becomes `idle`; last-output time remains old.
5. User input or real provider work arrives: status can become `running` and
   freshness advances from that real event.

`startup_tasks.status` is lifecycle metadata, not the UI source of truth.
Heartbeats may update ownership fields such as process id and heartbeat time,
but they must not rewrite a task status to `running`. Desktop, phone, and
standup cards should read the server live-status projection instead.

## SQLite Contract

CTM owns `task-manager.db`; Wall-E owns `wall-e-brain.db`. CTM background writes
that are not request-critical should go through the CTM DB-owner queue so restart
can drain a single known queue. SQLite WAL allows concurrent readers with one
writer, but CTM still keeps application-level write ownership explicit so
background maintenance cannot race restart, backup, or recovery.

## Anti-Patterns

- Do not add long synchronous startup scans before `listen`.
- Do not rely on `server.close()` to close WebSocket clients.
- Do not leave background timers running during restart.
- Do not block process exit waiting for unbounded child-process or DB work.
- Do not read/write CTM and Wall-E SQLite files from both processes as peers.
- Do not let restore replay, heartbeats, or PTY attach bytes advance session
  freshness or promote a restored session to `running`.
