# state/ — must-do follow-ups

- process_scope_projection.py callers in spawn_service.py and process_cleanup.py:
  move synchronous projection reads off the async event-loop thread with
  `asyncio.to_thread()`. Reads now have a bounded lock wait, but can still block
  the loop for up to that bound when a projection writer is wedged.
- [#454] state↔ops import cycle for identity migration: user_paths.py
  dynamically imports ops/migration.py, which imports back into state identity;
  legacy-id reading is duplicated in both. #388's B2 already removed the
  lock_held cross-cycle flag and the unlocked writer; the residual is
  structural — move the identity-transition mechanism into a state-owned
  project_identity module and leave ops with only active-spawn policy + CLI
  rendering. (B3)
- `session_store.py` / `ops/session_list.py`: route browse's recent-primary page
  through the official SQLite session index when it lands in 0.4.0. Preserve the
  current live-first `--limit` contract and keep index ownership out of
  `cli/browse/`; until then, replay `sessions.jsonl` and enrich only the visible
  page.
