Action argument contract: the app exports each action's arguments, and a Linux test fails any CLI call site that names one the app does not take
Leo, 2026-11-05, after a Cloud chat failed because the CLI sent promptId and the app's cloudRelayChat only took prompt-id: "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."
This makes that class of drift (a CLI call site naming an app action argument the app does not take) fail CI before it merges.
What changes
-
One source of truth.
ActionSurfaceExportnow writes each action'sargs(name, kind, required, enum values, target kind) into the checked-inplans/cmux-next/action-surfaces.json. It is regenerated here, and the existingexportIsFreshand PR autofix keep it current. -
Rust contract test (Linux).
cli/tests/action_argument_contract.rsscans cmux-tui's sources for every place an app action is named by hand: CLI words (["cloud", "relay-chat", ..]),action_run_params("ID"), or"action": "ID". It fails on any--flagorargskey the action does not take under its canonical name. Words that the CLI's own grammars take first (mux, agents, and chat_scope's sender flags) are skipped. A self-test plants thepromptIddrift in each call shape. -
Swift contract test.
ActionArgumentContractTestschecks four things: every argument is lowerCamelCase, no two arguments of one action fold to one name, the export carries every argument, and the old spellings still resolve. -
Canonical names. camelCase is the canonical spelling, because the CLI turns every
--flag-nameintoflagName. The router folds case,-and_, soprompt-id/prompt_idkeep working for one release. -
Drift fixed (found by the new checks):
history.opennew_tabbecomesnewTab.palette.installCLIcmux_nextbecomescmuxNext.- The in-process
tab.moveToNewSplitkeep_focusbecomeskeepFocus.
None of these could be reached from
cmux.cloudRelayChat'spromptIditself was fixed by #36193.
Changelog
Fixed: cmux history open --new-tab and cmux settings install-cli-in-path --cmux-next reach their app arguments.
Verification
- The fleet Mac regenerated the exports at de08d2f with 14 tests passing, the new suite included.
cargo test -p cmux-tui --lib action_argument_contractpassed on fleet Linux at this head: 3 tests.
Agent session: agent session
š¤ Generated with Claude Code
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Open in [code]smith Ā· Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
The app now exports each action's arguments into plans/cmux-next/action-surfaces.json as the single source of truth, so a CLI call site can't name an argument the app doesn't take ā which previously broke cmux cloud start-chat when the CLI sent promptId while the app expected prompt-id. A Linux test scans cmux-tui sources for hand-written call sites and fails on any --flag or args key the action doesn't take under its canonical lowerCamelCase name.
- Canonical argument names are lowerCamelCase, the spelling the CLI's
--flag-nameproduces;history.open'snewTab,palette.installCLI'scmuxNext, and the in-processtab.moveToNewSplit'skeepFocusare renamed. - The router still folds case,
-, and_, so old spellings likeprompt-idandprompt_idkeep resolving for a release. - A Swift contract test, named so
check-action-surfaces.shruns it on every catalog change, keeps the export current and checks the router's folding. - Three renamed arguments were unreachable from
cmux; thepromptIddrift itself was already fixed.