Environment: Windows 11 Home, Python 3.x, Claude Code installed via npm install -g @anthropic-ai/claude-code
. Measured 2026-08-14 to 2026-08-16.
I was comparing two agent harnesses on the same set of web tasks. The first run of harness B scored 3 out of 24. I didn't touch the model and I didn't touch the prompt wording. I changed only how the prompt reached the process, and the score went to 21/24. Here is what was actually broken.
I was running an eval to see how well current agents handle routine web work, the kind a triage process would flag as "candidate for RPA." Two harnesses, same 8 tasks:
max_turns=40
), 8 tasks x 1 trial.claude -p
) plus Playwright, same model, 8 tasks x 3 trials = 24 runs.Success in both harnesses is decided by machine verification only: submitted form payloads compared against expected JSONL, downloaded files checked for existence and size, answers matched by regex. The agent's own claim of "done" is never trusted as a success signal.
First run of harness B: 3/24. Only the first task passed. I assumed the model was the weak link and started digging into what harness B was actually sending it.
On Windows, when you drive claude -p
from Python via subprocess
and let shutil.which
resolve the executable, a multi-line prompt gets truncated at the first newline before the process ever sees it. Everything after line 1 is silently dropped.
Root cause: the npm global install of Claude Code puts a file named claude.CMD
on PATH. That's the file shutil.which("claude")
resolves to — not the real executable. It's a batch wrapper. Its body is:
"%dp0%\node_modules\@anthropic-ai\claude-code\bin\claude.exe" %*
Let me separate what I measured from what I'm inferring.
What I measured: swapping only argv[0]
between the two paths changes the outcome. Point it at claude.CMD
and the tail is gone; point it at claude.exe
and it arrives. Prompt, flags, and environment are identical, so the difference is the shim.
What I'm inferring: %*
is cmd.exe's mechanism for forwarding arguments to the wrapped command, and the newline appears to be lost in that forwarding. I have not isolated which layer drops it (cmd.exe's argument expansion, the shell Python interposes when executing a .CMD
, or both). Either workaround below is sufficient without knowing, so I stopped there.
Minimal reproduction:
import os, subprocess
EXE = os.path.join(os.environ["APPDATA"], "npm", "node_modules",
"@anthropic-ai", "claude-code", "bin", "claude.exe")
CMD = os.path.join(os.environ["APPDATA"], "npm", "claude.CMD")
PROMPT = "Follow the instruction below exactly.\nOutput the string MARKER_TAIL_9137 and nothing else."
for label, argv0 in (("claude.CMD (shim)", CMD), ("claude.exe (direct)", EXE)):
r = subprocess.run([argv0, "-p", PROMPT], capture_output=True, text=True,
encoding="utf-8", errors="replace", timeout=180)
out = (r.stdout or "") + (r.stderr or "")
print(label, "tail_received =", "MARKER_TAIL_9137" in out)
Measured result:
claude.CMD (shim)
: tail_received = False
. The model's reply asks for the missing instruction, having seen only line 1: "Follow the instruction below exactly."claude.exe (direct)
: tail_received = True
.There are two fixes:
claude.exe
directly instead of trusting whatever shutil.which("claude")
returns.This is hard to catch because there's no exception, no non-zero exit code, and no warning anywhere in the pipeline. cmd.exe forwards a truncated string and everything downstream treats it as a normal, well-formed prompt. The only symptom is a low success rate, which looks identical to "the model isn't capable enough for this task." I spent time suspecting the model before I suspected the wrapper.
--model
flag set and was running on whatever the CLI's default model was, so this result wasn't comparable to harness A, which was pinned to sonnet-5. Confounded comparison, not a usable data point.max_turns=40
plus a 600-second timeout; harness B had only the timeout, no turn cap. Part of the 6/8 vs 21/24 gap comes from that difference, so don't read those two numbers as a head-to-head ranking. The claim of this post is the within-harness-B change, 3/24 to 21/24.Success rate is the product of model capability and harness quality, and the two are easy to conflate when the harness fails silently. Before concluding a model can't do a task, I now check four things: is the target (URL, path) actually present in what gets sent; is the full instruction arriving intact (print the exact prompt the process received, and read it — don't assume); can the agent actually reach whatever files it's supposed to produce or read; are model and stop conditions pinned explicitly on both sides being compared.
If I'd taken that first run at face value, I would have reported that Claude Code plus Playwright can't handle routine web work. The cause had nothing to do with the model: a batch file was dropping everything after the first newline.