acpmux: preserve refusal retry argv and support creation tags

Merged
TE
11d ago
+432-19

Remote session refusals currently drop the prompt and launcher options from their suggested command. This preserves the original acpmux argv, shell-quotes both SSH layers, embeds prompt-file text through stdin, and rewrites only actual options needed by the existing refusal remedy. The original argv is captured at the acpmux entry point, including when invoked as cmux acp.

Repeatable --tag K=V now works at creation on new, run, exec, and ensure, locally and through --host. Existing ensured/reused sessions retain their tags. Updated the remote guide, acpmux README and guide/help in the same change.

Leo: "this + more i was talking to workspace-218 i wanted them to help work on ergonomics too" and "these issues and such all need to be addressed".

Risk: command replay must preserve prompt bytes and option boundaries across two shells. The remote policy and mode gates are unchanged. Prompt file text is included in refusal output, just as an inline prompt is.

Observed / Not run:

  • Seven focused hint unit tests pass, including execution through two POSIX shell layers with quotes, dollars, command substitutions, newlines, option-looking prompt words, and a near-1-MiB prompt file. Prompt-file input is streamed outside SSH’s command argument to avoid exec argument-size limits.
  • The focused creation-tag integration test and three parented-launch unit tests pass. Isolated live CLI checks with the fake ACP harness pass for new/run/exec/ensure through --host; new/run/ensure list both supplied tags. The pasted refusal starts the exact original prompt, including a prompt file after deleting the source file.
  • Local shell smoke needed RUST_MIN_STACK=16777216 for this debug binary. No live model/provider was used.
  • Remote cmux-ci test dispatch was attempted but its HQ bootstrap requires raw GitHub authentication unavailable on this broker-only VM. The lane's glaeda-gh watcher is also down after its sanctioned restart attempt. Full suites were not run locally.
  • CI caught an added panic site in retry-option lookup; it was replaced with a fallible lookup. The added tags also pushed NewArgs over clippy’s enum-size threshold; Command::New now boxes its payload. Both introduced CI failures are fixed; final-head rustfmt, clippy, crash ratchet and fast gates pass. GitHub installation rate-limit failures affect status publication/merge-tree infrastructure.

Live refusal transcript on 2e9f176aacda1b422f5721ea25c6e8bbeeba182a (two isolated daemons; ws peer's data.runOn is b, so the command is pasted in b's environment; two-layer SSH execution is covered by the shell unit test):

PASTE: mkdir -p /tmp/ergo-tag-smoke-h2wsgngp/project && acpmux --json new -n demo -m fake --policy approve-all --cwd=/tmp/ergo-tag-smoke-h2wsgngp/project --tag a=b -d 'Do not expand '\''$HOME'\'' or $(touch nope); say OK.
Second line'\''s text.'
{
  "ephemeral": false,
  "name": "demo",
  "permissions": 0,
  "permissionsDenied": 0,
  "reply": "echo: Do not expand '$HOME' or $(touch nope); say OK.\nSecond line's text.",
  "sessionId": "01a1762a-7637-7d03-afca-6940715720d2",
  "stopReason": "end_turn"
}

$ acpmux --json ls  # selected fields
{"name":"demo","tags":{"a":"b"}}

Prompt-file variant (the original local file was deleted before pasting):

PASTE (source file deleted): printf %s 'file'\''s $HOME $(touch nope)
trailing newline
' | (mkdir -p /tmp/ergo-tag-smoke-h2wsgngp/project && acpmux --json new -n file-demo -m fake --policy approve-all --cwd=/tmp/ergo-tag-smoke-h2wsgngp/project --tag a=b --prompt-file=/dev/stdin 'Do not expand '\''$HOME'\'' or $(touch nope); say OK.
Second line'\''s text.')
{
  "ephemeral": false,
  "name": "file-demo",
  "permissions": 0,
  "permissionsDenied": 0,
  "reply": "echo: Do not expand '$HOME' or $(touch nope); say OK.\nSecond line's text.\n\nfile's $HOME $(touch nope)\ntrailing newline\n",
  "sessionId": "01a1762a-779d-7423-a2f2-ec8bba74b0a8",
  "stopReason": "end_turn"
}

Overlap audit: pr-overlap cannot read GitHub here (credential broker refusal); broker search found no duplicate tags/full-argv fix. #25049 is the merged predecessor. Adjacent #25270 shares docs; #25251 shares CLI/help/docs for remote groups. Neither branch was modified.

Review (independent Codex gpt-6-astra xhigh session /root/independent_review): SHIP at 7c7ca06cf1268d5d2fafd454ebbcce7f270724cd

Independent review audited Retry rendering, SSH stdin streaming, option scanning and -- delimiter, prompt-file handling, unchanged remote policy flow, and local/remote/parent tags. No blocking findings remain.

Area-owner follow-up at bd9165797f192d3dc896bbbf971b996439cc362c: strip argv and promptFileText from the forwarded session/new, leaving the original params available to the local hint builder. New wire-level regression test verifies neither key reaches the peer, creation tags still arrive, and the refusal retains both prompts. The focused test passes with RUST_MIN_STACK=16777216; the default-stack debug run overflows. Area owner will rerun the fleet suites on this head. Independent Codex xhigh incremental review returned SHIP for this exact head: only the forwarded clone loses the hint metadata, tags remain forwarded, and the wire test verifies the boundary and complete local retry.

Stack-overflow fix at 7c7ca06cf1268d5d2fafd454ebbcce7f270724cd: retry option rewriting now builds only NewArgs’ clap grammar instead of the entire CLI inside the async request handler. The wire regression test is unchanged and passes with RUST_MIN_STACK unset; all seven hint unit tests also pass with the override unset. Formatting and diff checks pass. Independent Codex xhigh review returned SHIP for this exact head, confirming the creation grammar includes all value flags and global switches remain safely skipped. Fleet rerun is pending.

Agent session: gcp-codex-lane-3

Takes back #25019: no behavior change; Command::New now boxes NewArgs (the added tags field), so the --parent route unboxes it with *args.