executor-harness
How a workflow step reaches an agent. The harness starts and supervises steps over ting-a2a through two transport families — the DSH loopback HTTP transport and sandboxed external CLI dispatches — and normalizes every outcome into one shape for step bookkeeping. Failures are ToolFailures classified by ToolFailureKind, converted into executor_core::WorkflowError::Harness at the boundary.
DSH loopback transport (dsh)
ADR 0003: the DSH endpoint is reached over HTTP on loopback. Requests are sent without Origin headers; responses decode as JSON-RPC (JsonRpcRequest / JsonRpcResponse / JsonRpcError). The transport is DshTransport; an HttpResponse is the raw wire view.
Hardening (hardening)
The loopback boundary is enforced, not assumed:
validate_loopback_url/is_loopback_host— only loopback URLs are accepted.- Userinfo in the URL is rejected.
reject_redirect— redirects are refused; a loopback client does not follow them elsewhere.validate_args— arguments are validated before dispatch.ToolFailure/ToolFailureKind— typed classification of what went wrong.
External CLI transports (cli)
Three vendor CLIs are first-class transports: Claude Code (claude), Pi (pi), OpenAI Codex (codex) — ExternalCli::all(). Each dispatch runs the CLI as a sandboxed child process:
- no shell — direct
argv; - scrubbed environment;
- hard byte budgets (
CliLimits) on stdin, stdout, and stderr, so a chatty CLI can never exhaust the harness; oversized stdin is rejected before the process starts; - a wall-clock timeout per dispatch.
The raw process result is normalized into a NormalizedOutcome with an OutcomeStatus, which is the only thing step bookkeeping sees.