# Sessions — Per-Session Runtime State

Each session gets a directory under `var/sessions/` named by the SHA-256
hash of its session key. This is where my per-session persistent state lives.

## Directory Layout

```
var/sessions/{sha256-hash}/
├── state.json       ← persistent session state (source of truth)
├── meta.md          ← session metadata (routes, display name, kind)
├── inbox/           ← raw incoming items (*.pending files)
└── mailbox/
    ├── mailbox.md   ← rendered mailbox for SDK consumption
    ├── pending/     ← merged items awaiting drain (*.item.json)
    └── notes.jsonl  ← append-only drain notes
```

## state.json

The single source of truth for persistent session data:

- **Identity** (immutable after creation): `session_key`, `cwd`, `plane`,
  `permission_profile`, `created_at`
- **SDK session**: `sdk_session_id` — the Claude Agent SDK session ID for
  conversation resume. Written when SDK confirms a new session, cleared on
  `/clear` or `/reset`.
- **Drain watermark**: `last_event_id`, `last_event_at` — crash recovery
  markers. Ensures the `outbox write → watermark → mailbox finalize`
  ordering survives process crashes.
- **Channel capabilities**: declared by `channel.pull` consumers.
- **Pending injections**: gateway notices, interrupted context, skip rewind,
  outbound attachments — drain-scoped data consumed on the next turn.
- **Last error**: `last_error` — most recent drain failure. Cleared on next
  successful drain. Used by dashboard for error observability.

## Mailbox Pipeline

Messages flow through two stages:

1. `inbox/*.pending` → raw items from ingress (one file per message)
2. `mailbox/pending/*.item.json` → merged items ready for SDK consumption

The drain loop merges inbox into mailbox, processes items, then deletes
completed files. A crash at any stage is recoverable: the watermark in
state.json tells the runner which items were already processed.

## meta.md

YAML frontmatter with session metadata:

- `session_key`, `display_name`, `kind` (channel/job/meta/system)
- `routes` — delivery routes for job result notifications
- `owner_session` — parent session for job sessions
