acpmux peer snapshots (2t): the store's tests

Merged
TE
9d ago
+393-0

Lawrence's list item, word for word: "start new VMs from acp chat - decouple persistence of acp trajectories from the VM itself, in case the VM is resuming".

Part of the client-snapshot piece of manaflow-ai/cmuxterm-hq#4381 ("Persistence, pause and resume, and history hydration"): the Mac's acpmux keeps a durable, bounded copy of each peer session's events, so the app paints a paused or offline peer chat at once, marked possibly stale, and catches up from the host with afterSeq. The host's journal stays the authority; the copy is not a write lease, an execution checkpoint or a backup. The shared fenced-writer database from #4381 is not part of this work.

This replaces #25323 (store) and #25378 (hook and pane), split so each piece lands on its own under about 800 changed lines, tests included. The series:

  1. the on-disk form of a copy (#25448, landed)
  2. the store's thread: bounds, writes, removals (#25445)
    2t. the store's tests (#25505)
  3. what starts or grows a copy: generations, joins, links (#25446)
  4. purges, removed peers, the queue's bound (#25447)
  5. the hub's taps (wiring) (#25449)
  6. the read hook _acpmux/peer_snapshot, which the VM start/resume work calls too (#25473)
  7. the pane's offline banner and the reply's reading (#25474)
  8. the pane paints the copy, then catches up (#25475)
    8t. the pane's tests (#25506)
    8t2. tests of 8's attach fixes (#25620)

This PR (2t): the store's tests

The tests of 2 (the store's thread, its bounds, writes and removals), split off so each PR stays under the size cap. No code change.

Findings from the earlier reviews these tests hold

S1 to S11, S16, D12, D15 and S31 to S43 of the ledger, for the code in 2. The round-3 fixes each have a test here:

  • 1000 empty copies with a refusing writer stay within the memory bound (S39);
  • a flush keeps a failed write's backoff (S43);
  • the disk bound evicts written copies before an older unwritten one (S42);
  • copies a late scan restores stay within KEPT_TOTAL, written ones going first, with a refusing writer (S44);
  • the backoff check moves the retry a minute out first, so it cannot race a 10 ms retry (S45);
  • the eviction fixtures set explicit ages, so no two copies tie on a millisecond (S51);
  • ops a thread never took give back their room, and the next thread joins the one that ended and takes what it left before anything it is sent (S55, S62).
  • a written copy a late scan restores, with unwritten copies at the bound, goes and every unwritten one stays (S68); a head grown past its bound goes when charged, before any write (S69); a capture while the disk does not read wakes the thread no earlier (S67); the restart race inside std's receiver drop is answered (S70).

Tests

These 11 tests, plus the 5 from 1, pass on the fleet (Linux, 32 vCPU), with cargo fmt --check and cargo clippy -p acpmux --all-targets -D warnings clean:

  • a restart keeps each seq once;
  • a record past the session bound goes alone;
  • bounds and least-recently-used eviction;
  • a full disk: records kept, written once it can, a flush keeps the backoff, unwritten and empty copies within the memory bound;
  • the disk bound evicts written copies first;
  • copies a late scan finds stay within their bound;
  • a restart drops what is not a copy;
  • refused removals are counted;
  • an unreadable or symlinked root holds up writes;
  • a head past its bound is not written, and a flush waits in the queue once.

Timers, threads and loops

None: tests only.

Base reds, not from this PR (they fail the same way on feat-cmux-next): agent_tools::tests::own_servers_run_under_the_daemon_prefix, and the flaky session_pool::pooled_sessions_end_with_the_daemon_and_after_a_crash.

Agent session: agent session

šŸ¤– Generated with Claude Code