cx-v9k2f: acpmux decides each session's lane (working, input, idle since, failed with why, lost)

Merged
TE
23d ago
+2871-664

Bead: cx-v9k2f (part 1 of 2; the app's lanes view follows in a second PR)

Problem

Leo, 2026-10-13: about 25 lanes sat idle for hours unnoticed, some dead on out-of-credit errors. acpmux decides each session's status dot (indicator), but nothing says how long a session has been in that state or why a turn failed. A daemon restart mid-turn also wrote a failed turn_result without recording it as the last turn, so that session read as idle.

Change

  • Every session summary (_acpmux/watch, session_changed, _acpmux/sessions) carries lane = {state, since, error}. acpmux decides it once next to indicator, from the summary's own fields:
    • working since its turn started;
    • input while a permission or question waits;
    • idle since its last turn ended, or since it was created;
    • failed with the last turn's errorText;
    • lost when the agent process is gone without an answer.
    • A closed session has no lane.
  • Peer sessions get it too, and it's re-decided when a peer goes unreachable (views::decide replaces the three separate indicator writes).
  • Both outcome_unknown paths (a daemon restart, or a host that never got the prompt) now call record_last_turn. Those sessions read failed with the reason.
  • restart_marks_unknown_outcome moves to tests/hub_integration/restart.rs, because hub_integration.rs was at its 1500-line cap.
  • The lastTurn key list is views::LAST_TURN_KEYS, so hub/turns.rs (over the god-file limit on base) does not grow.

Verification

  • Red at b8305e5 (tests only), hosted --filter lanes::: test result: FAILED. 0 passed; 5 failed on Linux and on macOS. Every lane test panicked because summaries had no lane.
  • Green on 7076fa6 (base 541435d), hosted --filter lanes:: and --filter restart_marks_unknown_outcome: lint (linux), test (linux) and macOS lint and tests pass.
    • One run's release-path dogfood artifacts / build aarch64-apple-darwin failed; the other run built the same SHA. That job is not part of this change.
  • Green again on fresh-merge head e8d8c24 (both filters): lint (linux) and test (linux) pass.
    • Earlier, at 5099ccd, the macOS hosted tests passed, and the peer test passed on 492bd45 (Linux).
    • The PR's own checks are all green on 7076fa6, including the macOS daemon tests and the same-tree daemon tests.
  • crash_ratchet.py: ok.

Changelog

none

🤖 Generated with Claude Code