agent-pane-policy: status and peer_reconnect replies keep what the Swift host keeps; a shape that does not parse fails closed

Merged
LA
4d ago
+787-11

For _acpmux/status, the Swift agent pane host keeps each peer's transport, connected, state, lastSeen and attempts, plus the cached health warning inputs. transport is only the scheme of the peer's url: ssh, ws or wss. For _acpmux/peer_reconnect, it keeps transport, connected, state, lastSeen, attempts and error (AcpmuxPaneMethods+ReplyShapes.swift; b0fa7941918, 62e81abb3dd, de58118cc95, 9328d559498). The crate keeps only name for status, and name, state, error for reconnect. It has no bool or scheme shape. A host that runs the crate would therefore lose the environment picker's SSH/Cloud split, the connected marks and the load warnings.

What changes in the crate:

  • ReplyShape::Bool takes only a JSON true or false. ReplyShape::Scheme takes only ssh, ws or wss from the sibling url, read as Swift's URLComponents(string:) reads it: RFC 3986 characters only, a valid scheme, lower-cased.
  • policy.json has the Swift host's two shapes.
  • Fail closed: a shape that policy.json names but the crate cannot read now filters to an empty result. Before, the method lost its shape. A host that relays unshaped replies as they are would then send the whole reply, with URLs and tokens, to the page.
  • A filtered error's code is read as Swift's as? Int reads it (2.0 is 2, true is 1), not as -32603.

This is one of the Swift-only drifts that must reach the crate before the app calls it (rm-pane-policy, layer-ownership V-23). The app does not call the crate yet, and cmux-app-ffi does not link it, so nothing changes in the app and this PR is no FFI input.

Security

Each kept field is the running Swift host's, field for field. The URL never reaches the page; only its scheme does, from a fixed list of three. The loader change removes a fail-open path. No method or param is newly allowed.

Changelog

none

Observed / Not run

Observed (fleet, Linux, scripts/ci/cmux-tui-rust-check.sh test <filter>):

tests/cases/replies.json holds 21 cases. Every expected reply, the 7 old ones included, comes from a line-for-line Python copy of the Swift filter. Two old expectations changed, because the old ones were the drifted Rust shape. The new cases port AgentPaneStatusReplyShapeTests.

Not run: clippy (the workspace clippy was red on base, #31462); no Swift compile (no Swift change). reported_scheme checks the url's RFC 3986 components (userinfo without @, a reg-name or [IPv6] host, a port of digits, path, query and one fragment of their own characters). Where it is stricter than Foundation, a peer only loses its transport; the result is always ssh, ws, wss or nothing, and the url never reaches the page.

Review round 1 (FIX at 515bf828a7d5: five urls URLComponents refuses still reported a scheme): red e3fed9857dd4 job 3c93ecc6c63596478aa4bea6; green baa073d47bc0 replies 3d9312a4e774856d5239ca62, whole crate f893a31f6a56d3c9ecd8b5ee.