Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion pipeline/haiku.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,13 @@ def call_haiku(
RuntimeError: If the subprocess times out or exits with a non-zero
return code, or if the JSON response cannot be parsed.
"""
# Prompt goes on STDIN, not argv: a session extract can exceed Linux's
# MAX_ARG_STRLEN (128KB per single argument), which raises E2BIG ("Argument
# list too long") at exec time and silently kills saves of long sessions.
# `claude -p` with no positional prompt reads the prompt from stdin.
cmd = [
"claude",
"-p", prompt,
"-p",
"--output-format", "json",
"--no-session-persistence",
"--exclude-dynamic-system-prompt-sections",
Expand All @@ -116,6 +120,7 @@ def call_haiku(
try:
result = subprocess.run(
cmd,
input=prompt,
capture_output=True,
text=True,
# claude emits UTF-8; without this, text=True decodes with the
Expand Down
20 changes: 20 additions & 0 deletions tests/test_haiku.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,26 @@ def test_call_haiku_success(mock_run):
assert "CLAUDECODE" not in env


@patch("pipeline.haiku.subprocess.run")
def test_call_haiku_sends_prompt_on_stdin_not_argv(mock_run):
"""The prompt is delivered on STDIN, never as an argv string.

A session extract can exceed Linux's MAX_ARG_STRLEN (131072 bytes / 128KB
per single argument); the old ``claude -p <prompt>`` form fails at exec()
with OSError E2BIG ("Argument list too long"), silently losing the save.
Guard both halves so the regression can't silently return: the prompt must
arrive via ``input=`` and must NOT appear in the command argv (``-p`` is a
bare flag, immediately followed by the next option)."""
mock_run.return_value = MagicMock(
returncode=0, stdout=_mock_claude_response("ok"), stderr="")
call_haiku("the full prompt text")
args = mock_run.call_args
cmd = args[0][0]
assert args[1]["input"] == "the full prompt text"
assert "the full prompt text" not in cmd
assert cmd[cmd.index("-p") + 1] == "--output-format"


@patch("pipeline.haiku.subprocess.run")
def test_call_haiku_strips_parent_session_env(mock_run, monkeypatch):
"""The nested claude -p must not inherit the PARENT Claude Code session
Expand Down
Loading