[FEATURE] Workflow tool: byte-exact data channel between workflow scripts and the host (model-retyped transport corrupts commands and payloads)

Open 💬 4 comments Opened Jun 11, 2026 by ghbaud

Summary

Workflow scripts (the Workflow tool, CLAUDE_CODE_WORKFLOWS=1) have no byte-exact way to move text between the script and the host. The sandbox bans filesystem, process, and clock access for replay determinism, so the only effector a script has is agent(). Every shell command a script wants to run must travel inside a subagent's prompt, and the subagent re-types it into its Bash tool call. Every result travels back as the subagent's final text. A language model therefore sits in the middle of every byte that crosses the script boundary, in both directions, and a model is not a reliable copier of long or quote-bearing strings.

We run a long-lived multi-agent orchestrator as a Workflow script (60+ subagent spawns per run, durable state checkpointed between runs). Over five consecutive runs, five distinct production failures traced to this single restriction. We believe the failure class is structural and worth a first-class fix in the Workflow API.

Observed failure modes (all reproduced in production runs)

  1. Multi-KB argument mis-escaped into invalid shell. A ~4 KB JSON document was passed as one argv element in the runner subagent's prompt ("pass each element as one argument verbatim"). The Bash-only subagent composed a double-quoted shell string, hand-escaped the inner quotes, and bash failed with exit 2 (unexpected EOF while looking for matching quote) before the target program ran. The workflow crashed.
  1. Silent single-token corruption on a write that exited 0. A ~100-entry key list crossed the prompt boundary with exactly one key altered. The command succeeded, the corrupted value persisted into durable state, and a later resume crashed on it. Nothing at write time could detect this without an application-level checksum.
  1. Mixed escape interpretation across subagent instances. The same prompt rendering (a JSON.stringify'd shell line) was decoded by one subagent instance (typed plain quotes) and copied verbatim by another (typed the escaped bytes \" literally). The same prompt produced different bytes on the wire depending on which sample of the model handled it.
  1. Behavioral deviation instead of copy error. A subagent ran the requested command correctly, then ran an additional command of its own and returned that second command's output as the requested command's stdout. A retry re-rolled the identical deviation 3 out of 3 times — retrying an LLM-backed step is not independent sampling when the prompt context is identical.
  1. Error text loss in the harness itself. When a Workflow script dies on an uncaught exception, the failed-run record serializes it as a bare Error with no message, so the crash cause has to be reconstructed from other evidence. This is the same theme at the harness layer: text crossing a process boundary loses fidelity.

Workarounds we had to build

Each of these works, and each is machinery no application author should have to write:

  • A chunked base64 stdin courier (tee with exact-byte input) for every file write, because inline content cannot survive the prompt hop.
  • FNV checksums embedded in durable state, verified with bounded re-reads on every read, because reads come back model-typed.
  • Redirect-to-file plus a separate cat re-read for command outputs, because inline stdout can be transcribed wrong.
  • Composition-time guards that reject double quotes, backslashes, and non-ASCII bytes from any composed command line, because some bytes reliably break the copy.
  • A rule that any payload over roughly 1 KB must ride a file, never a prompt.

The five failures above each cost a full diagnose / fix / review / relaunch cycle. The checksum, courier, and guard code now make up a meaningful fraction of our orchestrator.

Feature request

A deterministic, byte-exact channel between a Workflow script and the host. Any one of these would eliminate the class; they are listed in order of how completely they solve it:

  1. A harness-executed exec(argv, {input}) primitive in the Workflow API. The harness spawns the process directly (no model in the path) and journals the result exactly the way agent() results are journaled today, so resume/replay determinism is fully preserved.
  1. Sandbox-scoped byte-exact file primitives (readFile/writeFile under a per-run directory), journaled the same way.
  1. Verbatim tool-call binding for a subagent's first action: something like agent(prompt, {initialToolCall: {name: 'Bash', input: {...}}}), where the harness executes the given tool call with the given bytes and the model only interprets the result. The model adds judgment where judgment is wanted and is removed from the copy path where it is not.
  1. Artifact passing on agent(): accept and return file references rather than inline text, so large payloads never enter a prompt.

The sandbox restrictions exist for replay determinism, and we are not asking to weaken them. agent() already proves the pattern: a non-deterministic effect whose result is journaled is replay-safe. A process spawn is far more deterministic than a model call, so the same journaling mechanism covers it.

Environment

  • Claude Code 2.1.172
  • Windows 11 (host), subagent shell is Git Bash (POSIX)
  • Workflow tool enabled via CLAUDE_CODE_WORKFLOWS=1
  • Orchestrator pattern: one Workflow script, Bash-only haiku command-runner subagents, sonnet/opus worker subagents

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗