Skip to content

fix(webviews): relay local daemon peer state events - #26885

Closed
teamleaderleo wants to merge 6 commits into
feat-cmux-nextfrom
lane/nx-peer-events-source-1028
Closed

teamleaderleo wants to merge 6 commits into
feat-cmux-nextfrom
lane/nx-peer-events-source-1028

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Local daemon peer transitions currently do not reach the web session's peer state. This narrow source PR intentionally extracts and replaces the stale peer-state carrier in our existing remote-location strip draft #26796 (b749e778f5bb2203333556be9a53ff1437095dc1), which also carries unlanded picker prerequisites. #26796 remains draft and must rebase onto this foundation and remove its superseded carrier hunks before review. This PR supplies the carrier foundation; remote-strip UI completion and real-machine proof remain outstanding.

The owning overlap index covered 482 open PRs. The feature overlap is our own #26796; other overlaps are shared transport or native allowlist files, with no other peer-state adapter identified. This change leaves picker presentation, optional-project behavior, recent folders, defaults, colors, and glass untouched.

The adapter follows the merged daemon contracts from #26756 (honest initialize handshake), #26823 (peer-state events), and #26860 (origin privacy). It enables only when initialize advertises peerStateEvents and reports the local origin used by LOCAL/APP connections. Peer, remote, missing, and unknown origins cannot request or consume peer topology.

  • Read _acpmux/peers {} once after initialization and on each reconnect, then apply _acpmux/peer_state_changed from that same connection. No polling.
  • Fence snapshots by connection/read generation and per-peer revision so an older read cannot overwrite a newer event. Repeated identical observations remain idempotent while still fencing stale reads. Explicitly clear old lastSeen and error values, and reset capability before a reconnect initializes.
  • Add only the missing native _acpmux/peers relay, accepting no parameters and filtering replies to name, state, lastSeen, and error. Existing incoming notification forwarding carries the events. No daemon or session/auth fallback changes.
  • Preserve existing snapshot behavior when the feature is absent.

Validation on Linux at 085467e71042c174e90a7b7a2d748e04e502be08 over 1f723c64fe2a70709ac21b88f10f4bc8bfdf0823: meaningful tests-first red was 3 failures / 4 passes; the repaired integration and reducer suites pass 15 tests / 66 assertions. Full bun --bun run check passes. The supported scoped web prepush helper passes under Bun, and the permitted source-scanner prepush layers report no new violations across all eight changed files. Native policy tests are included but unrun.

The #26809 hold is lifted. No real remote-machine transition, spinner/green/red, no-project Send, light/dark, or gallery capture has been verified. Root owns fresh independent exact-head review, required-check reconciliation, and the final landing loop. Our existing #26796 remains unchanged.

Agent session: agent session


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Relays local daemon peer state events to the web session's peer state, so peer transitions on the local machine show live instead of only at the last _acpmux/status read.

  • Adds a zero-parameter _acpmux/peers native relay that filters replies to name, state, lastSeen, and error.
  • The web client reads one snapshot after initialize and on each reconnect, then applies _acpmux/peer_state_changed notifications; it never polls.
  • Enables the feature only when initialize advertises peerStateEvents and the origin is local/app; other origins and older daemons keep the existing snapshot behavior.
  • Fences snapshot reads per connection/read generation and per-peer revision so stale reads cannot overwrite newer events, and clears nullable lastSeen and error on repeated observations.
  • On an _acpmux/lagged notice, re-reads the snapshot so a peer that went down while the client lagged no longer shows as connected.
  • This is the carrier foundation for draft Show remote machine connection state in the composer #26796; remote-strip UI and real-machine validation remain outstanding, and native tests are included but unrun.

Written for commit e60b18f. Summary will update on new commits.

View guided diff Turn on auto-fix

On base 1f723c6, bun test src/agent-session/acpmux/peerStateEvents.test.ts runs 7 tests: 3 fail and 4 pass. The client never reads peers and ignores the local state feed. Nonlocal and feature-absent fallback cases pass. Native validation is held by #26809.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-93c3a66aa0c6bbfa650c
Gate peer events on the advertised feature and local/app origin. Read once after watch at initialize/reconnect, retain exact daemon state, clear nullable metadata, deduplicate lag replay, and reject superseded sockets/reads. Add a zero-parameter pane snapshot relay filtered to name, state, lastSeen and error. Preserve picker presentation and policies.

Linux web validation: 14 focused tests / 62 assertions pass; bun --bun run check passes including formatting, lint, typecheck (2933 files), and protocol checks. The focused regression was red before repair (3 failed / 4 passed). Native relay tests were added but native build/test/parse and CI dispatch remain held by #26809/HQ4779. No real-machine capture is claimed.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-93c3a66aa0c6bbfa650c
The replacement socket must initialize its own local/app origin and peerStateEvents feature before receiving peer notes; invalidate prior read tokens at open start. Focused Linux web tests: 15 pass, 66 assertions. Full bun --bun run check passes. Native verification and CI remain held by #26809/HQ4779.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-93c3a66aa0c6bbfa650c
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Default changes

This pull request changes 1 default at e60b18f2a9c4 (what everyone gets without configuring anything):

  • tunable-source.CmuxNextAgentPane/AcpmuxPaneMethods+ReplyShapes.swift:replyShapes: "f5d370fa79ee61c3399b1fa44053174a9d47bacb50ea9fd0fd742e14b2407c7d" -> "bafde009ec4ceab8bf105bbd3d39d8cbb35df760835310366b3d6aa8c5b98b4d"

Needs a review. It passes with an independent review receipt on this commit: a comment or review whose first line is Review (<reviewer>): SHIP at e60b18f2a9c45e2ce3f4284a6f6741853ab93fcc, from a member, not a self-review (the newest verdict naming the head decides; a later BLOCK beats an earlier SHIP), or with an approving review on this commit from @lawrencecchen or @austinywang (the call owners in .github/defaults-call-owners.json, never the author). A receipt posted as a review re-runs this check; after a plain comment, re-run it. The needs a call label alone does not pass, and a new push needs a new receipt.

Regenerate the snapshot with python3 scripts/ci/defaults_guard.py snapshot.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Bead cx-tff68u created for this PR by the cmux coordinator.

@github-actions github-actions Bot added the risk:high Risk tier (docs/ci/risk-tiers.md) label Oct 28, 2026
@blacksmith-sh

blacksmith-sh Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Found 37 test failures on Blacksmith runners:

Failures

Test View Logs
src/agent-session/acpmux/conversation/MarkdownMath.test.tsx/MarkdownMath.test.tsx View Logs
src/agent-session/acpmux/conversation/MarkdownMath.test.tsx/MarkdownMath.test.tsx View Logs
src/agent-session/acpmux/conversation/queuedMessages.test.ts/queuedMessages.test View Logs
src/agent-session/acpmux/conversation/queuedMessages.test.ts/queuedMessages.test View Logs
src/pages/settings/chiefLook.test.tsx/chiefLook.test.tsx View Logs
src/pages/settings/chiefLook.test.tsx/chiefLook.test.tsx View Logs
src/pages/settings/rowSummary.test.tsx/rowSummary.test.tsx View Logs
src/pages/settings/rowSummary.test.tsx/rowSummary.test.tsx View Logs
src/pages/settings/themePane.test.tsx/themePane.test.tsx View Logs
src/pages/settings/themePane.test.tsx/themePane.test.tsx View Logs

...and 27 more test failures. View all on Blacksmith: run 40995096762, run 40995096677

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review fbb1240: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr26885 -f ref=lane/nx-peer-events-source-1028 -f platform=macos -f backend_mode=local

Unclear: web / react-apps-check failed: no known signature; failed step: Check webview test shards.

CI failed on e60b18f2a9 (run 40995096762 attempt 1): 1 code, 1 unknown.

Job Verdict Why
web / react-apps-base-check (2) code a web test failed
web / react-apps-check unknown no known signature; failed step: Check webview test shards

Failures:

  • seen on other PRs MarkdownMath.test.tsx keeps shell command dollar signs as plain text without dropping content: failed (job)
  • seen on other PRs queuedMessages.test.ts each agent message is a received-card row, in the queue's order, with its promptId: failed (job)
  • seen on other PRs chiefLook.test.tsx search renders Bubble Style in the Look card: failed (job)
  • seen on other PRs rowSummary.test.tsx no description line on any Settings page is over 60 characters, in any language: failed (job)
  • seen on other PRs rowSummary.test.tsx every schema summary fits 60 characters in every language: failed (job)
  • seen on other PRs themePane.test.tsx the Theme page draws each theme key once, in one card, with no second App Theme group: failed (job)
  • seen on other PRs agent-pane-no-capsules.test.ts agent pane page > rounds its controls as small-radius rects, not capsules: failed (job)
  • seen on other PRs agent-pane-pending-steer.test.tsx Send now on a waiting steer asks queue_edit send_now for its promptId, and goes once it is read: failed (job)
Matched log lines
web / react-apps-base-check (2): (fail) keeps shell command dollar signs as plain text without dropping content [4.30ms]

Not re-run automatically: web / react-apps-base-check (2), web / react-apps-check are not machine failures.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

red: cmux-next checks (god files, concurrency, crash safety, l10n); NEW: the test helper died on signal 5; crashed thread: no crash report matched; searched /tmp/tmp.GiNP1W7XTP/DiagnosticReports: 0 matched, 1 older than this run, 1 a is not red on the base a14725a; do not merge until green; see lane-rules
red: web / Web status; base red: the same failure on the base a14725a; does not block
red: web / react-apps-base-check (2); no finished base run on a14725a to compare; do not merge until green; see lane-rules
red: web / react-apps-check; base red: the same failure on the base a14725a; does not block

Stale: these images are of fbb1240; the head is now e60b18f and a new capture is running.

Gallery before/after for fbb1240: 16 states rendered, 0 images differ, 2320 more touched states not rendered. Images are in the run's artifact, not on this public PR.

gh run download 40976867750 -R manaflow-ai/cmux -n gallery-advisory-26885-fbb1240e98bd36cdc23903ef32c393c133c98e50 (then read gallery-review.json)

States
State Theme Status Diff Picked by
agent-pane.auth-health/signed-in Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.auth-health/signed-in Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.auth-health/signed-out Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.auth-health/signed-out Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.auth-health/expired Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.auth-health/expired Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.auth-health/unknown Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.auth-health/unknown Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.chat-width/standard Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.chat-width/standard Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.chat-width/wide Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.chat-width/wide Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.chat-width/wide-blocks Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.chat-width/wide-blocks Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.composer-edge/short-edge Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.composer-edge/short-edge Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.composer-edge/wide-edge Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.composer-edge/wide-edge Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.composer-edge/no-edge Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.composer-edge/no-edge Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.composer-floats/scrolled-to-end Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.composer-floats/scrolled-to-end Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.composer-floats/composer-grown Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.composer-floats/composer-grown Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.continuation-spacing/shipped Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.continuation-spacing/shipped Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.continuation-spacing/two-thirds Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.continuation-spacing/two-thirds Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.continuation-spacing/half-tucked Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.continuation-spacing/half-tucked Apple System Colors unchanged 0% imports direct.ts (1 hops)
agent-pane.continuation-spacing/before Apple System Colors Light unchanged 0% imports direct.ts (1 hops)
agent-pane.continuation-spacing/before Apple System Colors unchanged 0% imports direct.ts (1 hops)

Pick other states with a Gallery: entry.id, entry.id/variant line in the PR body.

@teamleaderleo

teamleaderleo commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator Author

Owned by an agent session · session

@teamleaderleo
teamleaderleo marked this pull request as ready for review October 29, 2026 23:00
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex-review-box/gpt-6-astra high, lane-place): BLOCK at 9ba799f

BLOCK

  1. [P2] direct.ts:6846 — Recover peer state after _acpmux/lagged. The daemon drops peer notifications when its outbound queue fills and sends this marker, but resyncAfterLag() never refreshes peer state. If a disconnect notification is dropped, the picker can show that machine as connected indefinitely: even refreshPeers() discards the fresh status by overlaying the stale authoritative event cache here. Refresh _acpmux/peers on lag and emit the reconciled snapshot, including when no session is selected.

@teamleaderleo teamleaderleo removed the risk:high Risk tier (docs/ci/risk-tiers.md) label Oct 29, 2026
@github-actions github-actions Bot added the risk:high Risk tier (docs/ci/risk-tiers.md) label Oct 29, 2026
The daemon drops peer_state_changed notices when a client lags and says so
with _acpmux/lagged; resyncing only the selected chat left a machine that
went down meanwhile shown as connected. Review P2 on #26885.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-710a4a518c009a6ad126
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra high, review-router): BLOCK at fbb1240

  • webviews/src/agent-session/acpmux/peerStateEvents.ts:3,56-58: _acpmux/peers can report state: "restarting", but observation rejects it. The authoritative snapshot then clears that peer’s state/connection maps, so the picker treats a restarting peer as ready and may start a chat on it.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

take it. The BLOCK at e60b18f (two P2s: restarting snapshot entries are rejected; peers the snapshot drops become selectable again) has a fix with tests on branch drain-acpmux/26885-p2-fixes (ffbb9de, one commit on top of e60b18f). Cherry-pick it if useful; I have stopped working on this PR.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

This PR's lane is gone, so its work continues in #27971: these commits with their authorship, rebased onto current feat-cmux-next, plus fixes for the review finding here (a restarting peer from _acpmux/peers was dropped, so the picker read it as ready) and a second path to the same result (a listed peer the snapshot omits). Each fix has its own red/green test. Leaving this PR open for its owner to close.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Superseded by #27971, which drain-acpmux carried with the review fixes (comment at 04:25Z).

cmux-bot Bot pushed a commit that referenced this pull request Oct 31, 2026
* test(webviews): reproduce missing local peer state events

On base 1f723c6, bun test src/agent-session/acpmux/peerStateEvents.test.ts runs 7 tests: 3 fail and 4 pass. The client never reads peers and ignores the local state feed. Nonlocal and feature-absent fallback cases pass. Native validation is held by #26809.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-93c3a66aa0c6bbfa650c

* fix(webviews): consume local peer state snapshots and events

Gate peer events on the advertised feature and local/app origin. Read once after watch at initialize/reconnect, retain exact daemon state, clear nullable metadata, deduplicate lag replay, and reject superseded sockets/reads. Add a zero-parameter pane snapshot relay filtered to name, state, lastSeen and error. Preserve picker presentation and policies.

Linux web validation: 14 focused tests / 62 assertions pass; bun --bun run check passes including formatting, lint, typecheck (2933 files), and protocol checks. The focused regression was red before repair (3 failed / 4 passed). Native relay tests were added but native build/test/parse and CI dispatch remain held by #26809/HQ4779. No real-machine capture is claimed.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-93c3a66aa0c6bbfa650c

* fix(webviews): reset peer event capability before reconnect

The replacement socket must initialize its own local/app origin and peerStateEvents feature before receiving peer notes; invalidate prior read tokens at open start. Focused Linux web tests: 15 pass, 66 assertions. Full bun --bun run check passes. Native verification and CI remain held by #26809/HQ4779.

Agent-Session: https://cmux.com/deeplink/agent?id=aid-93c3a66aa0c6bbfa650c

* fix(webviews): a lag notice reads the peer snapshot again

The daemon drops peer_state_changed notices when a client lags and says so
with _acpmux/lagged; resyncing only the selected chat left a machine that
went down meanwhile shown as connected. Review P2 on #26885.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-710a4a518c009a6ad126

* test(webviews): reproduce a restarting peer dropped from the peer snapshot

_acpmux/peers says `restarting` (peer_link.rs PeerState::as_str) while a
peer's daemon restarts. The adapter drops that row, so the authoritative
snapshot clears the peer's state and the picker reads it as ready.

Red: bun test src/agent-session/acpmux/peerStateReducer.test.ts
src/agent-session/acpmux/peerStateEvents.test.ts: 16 pass, 2 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* fix(webviews): keep a restarting peer in the peer snapshot

_acpmux/peers names every PeerState acpmux has, `restarting` included
(peer_link.rs as_str), while peer_state_changed maps it to reconnecting.
The adapter now accepts it, so a restarting peer keeps its state and
reads not connected instead of falling back to ready in the picker.

Green: bun test src/agent-session/acpmux/peerStateReducer.test.ts
src/agent-session/acpmux/peerStateEvents.test.ts: 18 pass, 0 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* test(webviews): reproduce a listed peer the peer snapshot omits reading as ready

A peer that _acpmux/status lists but the authoritative _acpmux/peers
snapshot omits (removed between the two reads, or an invalid row) loses
its state and `connected`, so environmentReady offers it as ready.

Red: bun test src/agent-session/acpmux/peerStateEvents.test.ts: 10 pass, 1 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* fix(webviews): a listed peer the peer snapshot omits reads not connected

An authoritative _acpmux/peers snapshot replaced the peer maps with its
own rows, so a peer the status read still lists lost its state and
`connected` and the picker offered it as ready. Such a peer now reads
not connected until a note or the next read says otherwise.

Green: bun test src/agent-session/acpmux/peerStateEvents.test.ts: 11 pass, 0 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* test(webviews): reproduce an omitted peer losing its last-seen status

When the authoritative peer snapshot omits a peer the status read lists,
its peerStatus becomes only { connected: false }, so the offline banner
(chatUnavailable) loses lastSeen and attempts.

Red: bun test src/agent-session/acpmux/peerStateEvents.test.ts: 10 pass, 1 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* fix(webviews): an omitted peer keeps its last-seen status

The authoritative-snapshot branch replaced an omitted peer's status with
{ connected: false } alone. It now keeps lastSeen, attempts and error
from the last read and drops only the stale state, so the offline banner
still says when the machine was last seen.

Green: bun test src/agent-session/acpmux/peerStateEvents.test.ts: 11 pass, 0 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* test(agent-pane-policy): reproduce _acpmux/peers missing from the Rust relay policy

The web pane sends _acpmux/peers (zero parameters) and the macOS host
admits it, but policy.json does not list it, so a Rust host refuses the
request and its reply is not filtered. The cases also keep a numeric
lastSeen, as the Swift .number shape does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* test(webviews): reproduce a stale restarting state and the snapshot on the first frame

- A status read after a snapshot said `restarting` keeps it: a failed
  restart goes restarting -> reconnecting, both `reconnecting` on the wire,
  so no note says so and readPeers re-applies the snapshot.
- connect awaits _acpmux/peers before the first frame, a second round trip
  after the one #27755 keeps it to (a held snapshot holds connect).
- chatUnavailable calls a restarting peer offline instead of connecting.
The initial snapshot is settled in the fixture, and the in-flight status
test now races a real in-flight read.

Red: bun test src/agent-session/acpmux/peerStateEvents.test.ts: 11 pass, 2 fail;
bun test test/chat-unavailable.test.ts: 1 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* fix(agent-pane-policy): admit _acpmux/peers in the Rust relay policy

The web pane sends _acpmux/peers and the macOS host admits it with a
zero-parameter rule and a name/state/lastSeen/error reply filter.
policy.json now does the same, so a Rust host no longer refuses the
request. ReplyShape gains Number (a finite number, never a boolean),
mirroring Swift's .number, so lastSeen survives the filter.

Red at a4b6b6e (cmux-ci, cargo test -p cmux-agent-pane-policy): parity
replies FAILED, 15 passed, 1 failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* fix(webviews): a status read clears a stale restarting state; the snapshot leaves the first frame

- A _acpmux/status read now drops every peer event entry no note has
  updated since the read started (PeerStateEvents.statusRead), so a
  failed restart that the daemon reports only as a state (no note) reads
  reconnecting instead of restarting forever.
- connect starts the _acpmux/peers snapshot once the list's watch
  answered and draws it when it lands, instead of awaiting it before the
  first frame (#27755's one round trip).
- chatUnavailable counts a restarting peer as still connecting.

Green: bun test peerStateEvents, peerStateReducer, environments and
test/chat-unavailable.test.ts: 28 pass, 0 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* test(agent-pane-policy): _acpmux/peers passes; the escaped-name refusal samples another unlisted method

frames.json sampled _acpmux/peers as a daemon method the pane never
sends. The pane now sends it (policy.json admits it), so those two
refusal cases move to _acpmux/peer_remove, which stays unlisted, and a
pass case covers _acpmux/peers like _acpmux/status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* test(webviews): reproduce status and snapshot reads landing out of order

The status-read fence and the snapshot fence are separate, so:
- a snapshot that started before a status read, and landed after it,
  brings back the restarting state the status read cleared;
- a status read that started before a snapshot, and landed after it,
  deletes the newer snapshot's state.

Red: bun test src/agent-session/acpmux/peerStateEvents.test.ts: 13 pass, 2 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* fix(webviews): order status and snapshot reads on one clock

PeerStateEvents now ticks one clock for notes and for the start of every
read (statusStarted, readStarted). A snapshot that landed after a status
read started later is dropped, and a status read drops only entries
written before it started, so an older read of either kind can neither
bring back a cleared restarting state nor erase a newer snapshot. A
status read clears the snapshot's authority only when it is newer. The
reducer tests take the read's tick in place of a separate revision.

Green: bun test peerStateEvents, peerStateReducer, environments and
test/chat-unavailable.test.ts: 30 pass, 0 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* fix(webviews): a replaced socket's replies still land; only its peer notes drop

The stale-socket guard dropped every message from a socket the client
had replaced, so a request asked on it never settled: the export that
watches for a socket swap waited forever instead of rejecting
(conversation/exportClient.test.ts timed out on this branch and passes on
base). Only _acpmux/peer_state_changed from a replaced socket (or after
close) is dropped now; everything else is received as on base.

Red: bun test ./src/agent-session/acpmux/conversation/exportClient.test.ts
times out at a01e4e5 (CI react-apps-check shard 1: TIMEOUT twice, passes
replayed on base ad6de25). Green: 3 pass; peerStateEvents 15, reducer 8,
chat-unavailable 7 pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* test(webviews): reproduce an older status read overwriting a newer one

readPeers writes each _acpmux/status reply into peers, peerConnected,
peerStates and peerStatus before the clock fence runs, so of two
refreshPeers calls the one asked first and answered last wins. The
fixture's status read now answers with what the daemon knew when the read
reached it.

Red: bun test ./src/agent-session/acpmux/peerStateEvents.test.ts: 15 pass, 1 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

* fix(webviews): an older status read that lands late is dropped whole

statusRead now says whether the read is newer than every status read that
already landed (and than this connection); readPeers drops one that is
not before it writes peers, peerConnected, peerStates or peerStatus, so
two refreshPeers calls answered out of order keep the newer answer.

Green: peerStateEvents 16, peerStateReducer 8, chat-unavailable 7,
exportClient 3, chatOpenTrips 3, agent-pane-peer-copy 55,
agent-pane-cached-chat-open 10 pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-c35ebd1681cf79649972

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

This branch was previously deployed

1 inactive deployment
ci-routing-agent — e60b18f2 Deployed Oct 30, 2026 by teamleaderleo via wait for same-key tree publisher #79971
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:high Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant