agent pane peer snapshots (7): the offline banner and the reply's reading
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:
- the on-disk form of a copy (#25448, landed)
- the store's thread: bounds, writes, removals (#25445)
2t. the store's tests (#25505) - what starts or grows a copy: generations, joins, links (#25446)
- purges, removed peers, the queue's bound (#25447)
- the hub's taps (wiring) (#25449)
- the read hook
_acpmux/peer_snapshot, which the VM start/resume work calls too (#25473) - the pane's offline banner and the reply's reading (#25474)
- 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 (7): the offline banner and the reply's reading
This PR holds what the pane needs to draw a peer chat from the Mac's copy, before anything draws it:
peerCopy.tsreads the daemon's_acpmux/peer_snapshotreply as a tail to draw and the copy it keeps: machine, generation, newest seq, and whether its history is whole. It also tells a down machine (peer_unreachable) from another failure, and a row that answers again from one that does not.peerCopyis a new snapshot field. Nothing sets it until 8.PeerCopyBanneris the line over the chat. It says the machine is offline, that the copy may be out of date, and when the Mac last updated it. It has a gallery entry (agent-pane.peer-copy) and strings in every shipped language (English translated, the rest marked for review).
Before and after
These are gallery captures (Chromium, dark and light), rendered on the fleet from this branch:
| Before (the chat had nothing to draw) | After (the Mac's copy, at once, marked possibly out of date) | |
|---|---|---|
| Dark | ![]() | ![]() |
| Light | ![]() | ![]() |
The after state is what 8 draws when a paused or offline VM's chat opens: its history shows at once, its header names the machine (dev-vm-2, a fixture name), and the banner says it is the Mac's copy.
Tests
On the fleet:
agent-pane-peer-copy-reply.test.tscovers: no records or no machine is no copy; anything that is not a record is left out; a whole copy from seq 1 says its history is all there; a generation is the same only when both sides name the same one; a down machine is told from another failure.bun run checkpasses its format, lint, layer, token and protocol steps.gallery-coveragefails the same way onfeat-cmux-next(the components it lists are not from this PR).- Release compile (cmux-next, Xcode 26.6) on the fleet through the controller at head 51f6a2dd2e996eab0440303fcc2149c5628b9a5f (rebased onto 6 as landed): green, step
9495bef6dfd74d467225b918; at the head before, 444936da4f0, stepe3bbb6254904802ea3407a49. - Review: Codex (gpt-6-astra, xhigh) SHIP at 558f6c92be1;
git patch-id --stableof the diff vs base matches at this head (845de3d9f49f).
Timers, threads and loops
None.
Base reds, not from this PR (they fail the same way on feat-cmux-next): bun run check exits 1 there too, and so does gallery-coverage (it lists components that are not from this PR). Gallery health breaks the same composer, model-picker and new-tab states on feat-cmux-next (none of them this PR's banner), and the cmux-next checks red is tests/test_cmux_next_hybrid_ref_capture.py (from #25740: a timeout-minutes read as a string).
Agent session: agent session
š¤ Generated with Claude Code



