Skip to content

fix(agent): send/ask teach the verbatim body form first, and take it on stdin (DIVE-4421) - #933

Merged
5dive-bot merged 1 commit into
mainfrom
dive-4421-agent-send-verbatim
Sep 13, 2026
Merged

fix(agent): send/ask teach the verbatim body form first, and take it on stdin (DIVE-4421)#933
5dive-bot merged 1 commit into
mainfrom
dive-4421-agent-send-verbatim

Conversation

@5dive-bot

Copy link
Copy Markdown
Collaborator

Two of a customer's agents independently lost text out of a2a messages within two hours — a backtick-quoted function name vanished to command substitution, and US$4,500 arrived as US,500 ($4 = empty positional). Neither sender saw an error; both deliveries printed OK.

The premise "there is no safe form" was wrong: --message-file=<path> has existed since DIVE-2627 and reads the body verbatim. The defect is discoverability — the usage line taught the two shell-interpolated forms first and the safe one last, and nothing at the point of typing said the shell expands --message before 5dive ever sees it. DIVE-2627's warning lived in a parser comment; a warning that is reachable is not a warning that is effective.

What shipped (one commit, 4fb717d)

  1. Usage leads with the safe form. main.sh and agent send|ask --help now list --message-file=<path> first, with --message=<text> annotated as short plain text only and naming $, backticks and $( ) explicitly. agent send --help and agent ask --help used to die on unknown flag: --help; they render now.
  2. --message-file=- reads stdin, via _read_prose_file, so every *-file prose flag in the CLI gains it and the stdin read is the SAME read -r -d '' as the file read — no second code path to drift.
  3. Send-side nudge: an inline --message body whose literal argv still carries a backtick or $ prints a one-line warn naming --message-file. Those survive only when the caller quoted them, so it has no false positives on the dangerous cases — and it is silent on the already-corrupted ones. That is stated in the comment: it is a reminder, not a detector.

Request 2 declined as the row specified (receive-side warn on "empty expansion artifacts" like US,500, a lone backtick, an unbalanced quote). It is a by-name artifact list — the same incomplete-list trap the report itself names for "just escape it" — and US,500 is a legal string. The reason sits in the code comment beside the nudge so the next reader cannot re-add it by accident.

Scope added beyond the row, and why

agent ask was checked as the row asked, and it does not share the body flags — it had --message= and no file form at all. Teaching "the file form is the safe one" in its usage while that form did not exist would have been a lie on the verb whose bodies are longer (a question with code in it). So ask got --message-file plus the dupe and positional guards that come with it, rather than a usage line pointing at nothing.

Correction to acceptance clause (c)

The row asks that a real path "still refuses a symlink / a file another seat owns (DIVE-2627 arms unchanged)". No such check exists on main and none was added. _read_prose_file gates on -e, -f|-p|-c and -r only, and -f FOLLOWS a symlink, so a symlink to a readable regular file is accepted today exactly as before; tests/prose_file_flags_unit.sh has no symlink or ownership arm either. Arm (c) therefore grades the refusals that DO exist and are unchanged — missing path, non-regular path, empty file, --message-file vs --message, --message-file vs positional — and the harness records the discrepancy in place rather than asserting a guard that never shipped. If seat-ownership on a prose file is actually wanted, it is a new row against _read_prose_file, not a clause of this one.

Evidence worth keeping

The first draft of the help text wrote and `-` reads it from stdin and the RENDERED help said "and reads it from stdin" — usage() is an unquoted cat <<USAGE heredoc, so the text teaching people to avoid command substitution was itself eaten by command substitution. A grep of the source would have passed. That is why arm (a) builds the bundle and grades the rendered bytes, and it is the customer's own defect class one layer in.

Tests

tests/agent_send_verbatim_unit.sh, 25 arms, all green — including a control body carrying every hazard at once (US$4,500, backticks, $HOME, $(date)) delivered byte-identical over stdin. tests/prose_file_flags_unit.sh 21/21 unchanged; 10 sibling send/ask/envelope harnesses green; shellcheck -S error --shell=bash clean on all three changed sources. Pre-push rail green (3/3 changed harnesses).

Residual I am not signing

No live delivery into a real agent's pane. The bundle smoke proves stdin is consumed and the body survives parsing — it stops at the agent lookup. What is unproven is the tmux send-keys leg for a multi-line stdin body, which is unchanged by this diff: the body reaches the existing wrap+inject path identically to a --message-file=<path> body, which DIVE-2627 already shipped.

Not done, deliberately: README.md:363 / README.zh-CN.md:339 still show 5dive agent send <name> <text> in the quick-start. That is the legitimate use of the inline form (short plain prose), and the row asks for the fix on the tool, not in a doc.

Row: DIVE-4421. Related: DIVE-2627, DIVE-4412, DIVE-4413, DIVE-4415, DIVE-4416, DIVE-4419.

🤖 Generated with Claude Code

…on stdin (DIVE-4421)

The corruption a customer reported happens in the CALLER'S shell, before this
CLI runs: a backtick-quoted verb runs as command substitution and its words are
deleted, `US$4,500` becomes `US,500`, and the send still prints OK. The safe
form (--message-file, DIVE-2627) already existed and was documented last in the
usage line and in a parser comment — a warning that is reachable, not effective.

- usage + `agent send|ask --help` lead with --message-file and say what the
  inline forms do to your text (--help used to die on `unknown flag`)
- --message-file=- reads the body from stdin, via _read_prose_file, so a quoted
  heredoc needs no temp file and every *-file prose flag gains it at once
- agent ask gets --message-file (it had --message= and no file form at all)
- a send-side nudge naming --message-file when an inline body still carries a
  backtick or $; it is a reminder, NOT a detector, and says so
- the requested receive-side artifact warning is declined, reason on the row

tests/agent_send_verbatim_unit.sh (25 arms) grades the RENDERED help, a
byte-identical stdin round trip, a mutation arm on the shipping reader, and the
DIVE-2627 refusals unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@5dive-bot
5dive-bot added this pull request to the merge queue Sep 13, 2026
Merged via the queue into main with commit b400264 Sep 13, 2026
31 checks passed
@5dive-bot
5dive-bot deleted the dive-4421-agent-send-verbatim branch September 13, 2026 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants