Show agent update outcomes automatically

Merged
TE
16d ago
+308-38

Agent update outcomes now show up in Settings and the palette on their own, including for restored jobs. You no longer have to press Refresh. The app listens for terminal changes on the original owner's control connection and then reads that owner's retained receipts. A reconnect, a withdrawn policy or a detached owner stops the listening. A signal can never start an update or count as success; only a bounded read of the original receipt sets an outcome.

#21469 (recovery across restarts) has merged, so this PR is now just the observation layer on top of feat-cmux-next:

  • AgentCLIUpdateActions+Observation.swift: follow(_:), observe(_:route:) and terminalChanges(...). The terminal-change stream comes from the captured source/serial only.
  • Route.changes is captured in Route.capture. Explicit refresh and the follow task both use the same observe(_:route:). That function keeps base's unknown-outcome field and its closed-tab (terminalGone) settle path.
  • When base's owner(of:) swaps in a re-captured route after a reconnect, it restarts observation on the new route. remember restarts it when a batch gains a receipt.
  • terminal-registry-changed outlives snapshots, collapses to the newest event per source/serial in the event inbox, and is delivered through side events.

Merge note (adopted 2026-10-20): base rebuilt #21469 after this branch forked from it. Every conflict takes base's side, and only the observation commit (23441cccc) and its later fixes are re-applied on top.

Changelog

Fixed: Agent update outcomes appear without pressing Refresh.

Observed / Not run

  • Ran: scripts/cmux-next/prepush-gate.py (no new violations) and scripts/web-lint.sh. Fleet Swift suites (CmuxNext, 4 shards) on the head: see the comment below.
  • Not run: GUI dogfood.

Agent session: cosmic-gray

🤖 Generated with Claude Code