Contracts: bridge senders by object, closed empty schemas, raw method strings, typed acpmux params and _meta, open-remote --focus

Merged
TE
1d ago
+2501-314

Leo, 2026-11-05: "You guys keep running into these. These are weird issues that I feel like you should be able to just fix beforehand and check on beforehand."

The open follow-ups from #36260, #36265, #36579 and #36592, in one PR. Each check that stops a hand-named field from drifting across a boundary now covers the cases it missed.

Webview to Swift bridge (agentBridgeContract.test.ts)

  • The scan also finds senders that arrive as object properties (opts.send(...)), destructured parameters ({ post }, { send: s }, defaults included), and destructured locals (const { postNative: post } = deps). The self-test covers each shape, plus a daemon sender passed by object that it must not report.
  • On its first run it found git.capabilities in checkpoints/controller.tsx, which no app serves. It is now listed under unserved in schemas/agent-bridge/contract.json with the reason. The Swift test fails once AgentPageOps serves it.

Swift to acpmux (scripts/ci/check-swift-acpmux-protocol.py)

  • A closed object schema with no properties (additionalProperties: false) now allows no keys. Before, it allowed any key.
  • The Swift method list and knownParams are tokenized as Swift strings: raw (#"..."#), multiline ("""), and strings containing ] or ,.
  • _meta.acpmux keys are checked against the new closed AcpmuxRequestMeta type in the spec. That covers knownParams' meta set and the pane's writes (acpmux["k"] =, setAcpmuxMeta, setDecision(field:)). The check fails if the spec has no AcpmuxRequestMeta.
  • acpmux (protocol/params.rs): all 65 methods the app sends now have closed params schemas (63 were untyped). The keys come from each handler's reads. AcpmuxRequestMeta lists the 68 keys acpmux reads from params._meta.acpmux. The IR and the web methods.ts/types.ts are regenerated, so most of this diff is generated. Three new allowlist entries, each with its reason: noProject is the pane's own key, stripped by AcpmuxPathPolicy before sending. ownerCloud and features are reply keys that RemoteAcpmuxWire writes when it answers initialize as the remote daemon.

CLI to app actions (cli/app.rs, action_argument_contract.rs)

  • cmux open-remote --focus [true|false] and --focus=false now reach the action's own focus argument. Before, only --arg focus=... reached it. The run's own focus follows the same value. An action without a focus argument keeps --focus as the run's focus only.
  • open_remote_focus_reaches_the_action pins those spellings.
  • every_argument_named_like_a_cli_flag_reaches_its_action checks every argument an offered action takes whose --flag the CLI also owns. Each one must reach the action through that flag, or be listed in GLOBAL_FLAG_ARGUMENTS with the spelling that does reach it. Today four are listed: session on agent open, agent open-tab, remote connect and history resume, all reached with --arg session= or cmux action run ... --session. A new collision fails the test. So does a stale entry.
  • cli-action-requests.json is regenerated: open-remote now sends focus through --focus.

After screenshots don't apply: this changes contract checks and the CLI's argument encoding only, with no UI.

Checks, all on fleet Linux:

  • cmux-tui: action_argument_contract 6/6, clippy -D warnings on cmux-tui and acpmux, fmt.
  • acpmux: protocol tests.
  • Webviews: bridge contract 3/3, fmt.
  • check-swift-acpmux-protocol.py on the repo, plus its 13 unit tests.

The review's follow-ups (multiline method literals in the app-wide scan; compression and any other request key missing from AcpmuxRequestMeta) are filed as follow-ups from the review receipt.

every_safe_transport_operation_has_a_noun_first_path (git.diff base/head) and root_help_lists_every_scope... (remote-host) fail the same way on base 424f297. They are not from this PR.

Takes back #28098: its untyped(method::…) registry line in acpmux/src/protocol/mod.rs is now keyed::<…Params>(method::…) with a closed params schema; the method, its kind and its handler are unchanged (the IR still lists all 292 methods).
Takes back #28859: its untyped(method::…) registry line in acpmux/src/protocol/mod.rs is now keyed::<…Params>(method::…) with a closed params schema; the method, its kind and its handler are unchanged (the IR still lists all 292 methods).
Takes back #29003: its untyped(method::…) registry line in acpmux/src/protocol/mod.rs is now keyed::<…Params>(method::…) with a closed params schema; the method, its kind and its handler are unchanged (the IR still lists all 292 methods).
Takes back #30188: its untyped(method::…) registry line in acpmux/src/protocol/mod.rs is now keyed::<…Params>(method::…) with a closed params schema; the method, its kind and its handler are unchanged (the IR still lists all 292 methods).
Takes back #30879: its untyped(method::…) registry line in acpmux/src/protocol/mod.rs is now keyed::<…Params>(method::…) with a closed params schema; the method, its kind and its handler are unchanged (the IR still lists all 292 methods).

Changelog

  • CLI: cmux open-remote --focus false now opens the remote pane without focusing it, as the action's own focus argument says.

Agent session: agent session

🤖 Generated with Claude Code


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


Summary by cubic

Closes the contract-check follow-ups from #36260, #36265, #36579 and #36592: each check that stops a hand-named field from drifting across a boundary now covers the cases it missed.

Bridge and Swift-to-acpmux checks

  • The webview scan now catches senders passed as object properties or destructured parameters and locals; this caught git.capabilities, now listed as unserved in the agent-bridge contract.
  • Closed schemas with no properties reject any key, Swift method strings tokenize as raw and multiline strings, and _meta.acpmux keys must appear in the new closed AcpmuxRequestMeta.
  • All 65 methods acpmux serves now have closed params schemas derived from handler reads; AcpmuxRequestMeta covers the 68 _meta.acpmux keys. The IR and web types are regenerated, so most of the diff is generated.
  • Three allowlist entries added: noProject (the pane's own key, stripped by AcpmuxPathPolicy) and ownerCloud/features (reply keys for initialize).

CLI action arguments

  • cmux open-remote --focus [true|false] and --focus=false now reach the action's own focus argument; only --arg focus=... did before.
  • A new contract test fails on any action argument whose --flag the CLI owns and that can't reach the action through that flag; four session cases are listed in GLOBAL_FLAG_ARGUMENTS. cli-action-requests.json is regenerated.

Changelog: cmux open-remote --focus false now opens the remote pane without focusing it. Two test failures (every_safe_transport_operation_has_a_noun_first_path, root_help_lists_every_scope...) also fail on base and are not from this PR.