acpmux: autoResumeDelaysMs sets the retry loop's attempts, not only its waits

Merged
TE
8d ago
+145-19

Lawrence via Leo, 2026-10-28, word for word: "should have retry button + configurabl etimer loop retry".

acpmux already runs the timed loop: a turn a connection or capacity error cut off is continued by itself after autoResumeDelaysMs (hub/auto_resume.rs), default [10000, 60000, 300000, 1200000], [] off. The waits are configurable. The attempts are not, and that is this PR.

MAX_ATTEMPTS was one constant doing two jobs:

  1. the ceiling on the configured list — checked_delays truncated it to four entries, so a timed retry loop longer than the default could not be configured at all even though the key reads free-form;
  2. the cap on a chain of daemon-restart continuations — log_restart_continuation compared against the constant, not against the list, so autoResumeDelaysMs: [10000] ("retry once") still allowed four restart continuations.

Both numbers being four is what hid the second one: a configured list was truncated before it could disagree with the constant. Lift the ceiling and the latent bug becomes a live one, so both are fixed together, test first.

What changes

  • MAX_CONFIGURED_ATTEMPTS (12) is the ceiling on the configured list. The default stays four waits spanning about 26 minutes, and the per-entry 1 s to 1 h bound is unchanged; the warning text follows the new ceiling.
  • configured_attempts() reads the attempts from the configured list: its length, the default's four when the key is unset, none for [].
  • log_restart_continuation reads that instead of the constant, so a restart chain gives up after the attempts the config asked for, exactly as a transport cut already did. [] still turns it off, and a contended config lock still reads as the default waits, never as off.
  • Doc comments and the acpmux README now say the key is the whole loop, not only its waits.

Deliberately not in scope: no new UI, no new transport, no new setting surface, no change to the default waits or to which errors are transient. This is the configurable half of the clause only; the Retry button half is #25947, which is someone else's open PR and is untouched here.

Tests

Focused command, both runs scripts/ci/cmux-tui-focused-test.sh auto_resume through cmux-tui.yml in focused mode:

  • Red on the test-only commit a9a6376662603601574c1cd8f83f906ab37b67c0: run 40773673821. That commit adds the tests against unchanged product source, so the two failures are the two defects above.
  • Green at this head 62fde3f15c455b01137982c71a2da7057374210e: run 40773423237.

New tests:

  • hub::auto_resume::restart_resume::tests::the_configured_waits_set_how_many_restart_continuations_run — one wait runs one continuation and then gives up with auto_resume_exhausted; six waits run all six.
  • hub::auto_resume::restart_resume::tests::an_unset_key_keeps_the_default_attempts_and_an_empty_list_turns_it_off — the unset key keeps four, and [] logs nothing because off is not a chain that ran out.
  • hub::auto_resume::tests::the_configured_waits_say_how_many_attempts_run — configured_attempts and the ceiling, with the default asserted to stay under it.
  • hub::auto_resume::tests::configured_waits_are_checked — a 5- and a 12-entry list are kept whole.

Observed / Not run

  • Observed: the focused Rust runs above. No Swift source changed and no app compile is involved.
  • Not run: no capture. This changes no UI — acpmux is the daemon, and the only user-visible text is a tracing::warn! for a refused config. No webview, no native source.

Changelog

Fixed: autoResumeDelaysMs sets how many automatic retries a cut turn gets, not only how long it waits between them, and a loop longer than the default can be configured.

Agent session: agent session