- `opencode_idle.py`: During a live OpenCode smoke, switch models and verify
  that `session.model` follows the active model before summarize; if it lags,
  source the compaction model from the newest user event's `info.model` (G2 O3).
- `projections/*`: `ResolvedLaunchSpec.claude_runtime` (formerly `claude_plugin_dirs`) is Claude-only but
  forced a `_DELEGATED_FIELDS` edit in six non-Claude projections, following the
  `disallowed_tools` / `pi_extension_entrypoints` / `opencode_version`
  precedent. That is the 10-file-edit smell from `AGENTS.md`; the next
  harness-specific spec field should go on a per-harness spec extension rather
  than another row in every projection (thermo F7).
- `codex_idle.py` and `../notify/label.py`: two separate `tmux` subprocess helpers. One
  `capture_pane(pane, timeout)` in `lib/platform/` (or a harness-level
  `tmux.py`) serves the sensor and the label (thermo F10).
- `opencode_idle.py`: the V2 sensor leaves `IdleFacts.context_tokens` as
  `None`, so `idle.min_compact_tokens` never vetoes an OpenCode compaction.
  OpenCode 2.x exposes `session.usage.updated` on the stream and
  `GET /api/session/{id}/context`; feed one of them once a live compaction
  shows the guard matters (work item `idle-ttl-detection`, phase 2).
- `opencode_idle.py`: two dialects (frozen V1, V2) now share
  `_OpenCodeIdleSensorBase` in one ~700-line module. When V2 grows again,
  move it to `opencode_v2_idle.py` with the base in `opencode_idle.py`,
  mirroring the `connections/opencode_http.py` / `opencode_v2_http.py` split.
- `connections/opencode_http.py`: split `events()` into a per-response drain that
  reports its close reason and small outer policies for spawn and primary-observer
  lifetimes; the current shared loop intentionally keeps the review fold bounded.
