From 59be1c89209a232059aec0c5aeecc2329929cb49 Mon Sep 17 00:00:00 2001 From: Godopu Date: Thu, 24 Sep 2026 07:02:20 +0000 Subject: [PATCH] fix(issue-36): create_session hands the agent a brief path, not the task; a prompt file that cannot be written is removed Review of f26952c (codex, job cc849cbf): - P2: create_session registered the task by file but then pasted the whole task into the onboarding text, which send_keys_safe hands to `herdr agent prompt` as one argv string. A 130,500-byte --submit-job was registered intact and never reached the agent (execve E2BIG). The task now goes into //brief.md, as the delegate-job wrapper does, and the injected text names that path. The publish command and its dispatch token (#23) are unchanged. The other send_keys_safe callers (the wrapper's short pointer, stop_session's exit key) carry no task text. - P3: delegate_job_safe's write-failure branch left the partial prompt file behind and logged to stdout, which callers capture. It now removes the file and logs to stderr. S8 (real create_session.sh, 100 and 130,500 bytes, strace when present) and S9 (the real function, RLIMIT_FSIZE write failure) carry the reviewer's reproductions over; all three fail on f26952c. Co-Authored-By: Claude Opus 5.5 --- .../scripts/create_session.sh | 15 ++++- .../multi-agent-mux-loop/scripts/run_loop.sh | 3 +- tests/test_issue36_prompt_transport.py | 66 +++++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/.agents/skills/multi-agent-mux-create/scripts/create_session.sh b/.agents/skills/multi-agent-mux-create/scripts/create_session.sh index 1494ce1..0547594 100755 --- a/.agents/skills/multi-agent-mux-create/scripts/create_session.sh +++ b/.agents/skills/multi-agent-mux-create/scripts/create_session.sh @@ -622,6 +622,19 @@ if [ -n "$DELEGATE_JOB_ID" ]; then # dispatch (trial-2 finding: without them the token-checking ack below could # never be satisfied by a real agent, and every onboarding sat out 180 s). pub="$(delegate_publish_cmd "$DELEGATE_JOB_ID" "$DELEGATE_JOB_TOKEN" "$DELEGATE_JOB_ATTEMPT")" + # Issue #36: the task goes into a brief in the job's directory and the pane + # gets only its path, as the delegate-job wrapper does. The injected text is + # one argv string to herdr (capped at 131,072 bytes), so the whole task + # plus this preamble could not reach the agent. + task_reg_dir="${MAM_JOBS_DIR:-${WORKSPACE_ROOT:-$PWD}/.mam/jobs}" + [[ "$task_reg_dir" == /* ]] || task_reg_dir="$PWD/$task_reg_dir" + task_brief="$task_reg_dir/$DELEGATE_JOB_ID/brief.md" + if ! { mkdir -p "${task_brief%/*}" && printf '%s' "$SUBMIT_JOB_PROMPT" > "$task_brief"; }; then + echo "ERROR: cannot write the task brief $task_brief" >&2 + delegate_publish_event "$DELEGATE_JOB_ID" error "task brief could not be written" "$DELEGATE_JOB_TOKEN" "$DELEGATE_JOB_ATTEMPT" + JOB_TERMINALIZED=1 + exit 1 + fi instructions="Your job_id is \"$DELEGATE_JOB_ID\" (the one just registered for THIS delegation). On start run: $pub --event started. @@ -629,7 +642,7 @@ On progress (optional): $pub --event progress --detail ''. On success run: $pub --event completed --detail ''. On failure run: $pub --event error --detail ''. -Task: $SUBMIT_JOB_PROMPT" +Task: read the complete task in $task_brief and follow it." # Inject instructions into the herdr pane rc=0 diff --git a/.agents/skills/multi-agent-mux-loop/scripts/run_loop.sh b/.agents/skills/multi-agent-mux-loop/scripts/run_loop.sh index 6be7fdb..437a5f8 100644 --- a/.agents/skills/multi-agent-mux-loop/scripts/run_loop.sh +++ b/.agents/skills/multi-agent-mux-loop/scripts/run_loop.sh @@ -201,7 +201,8 @@ delegate_job_safe() { mkdir -p "$MAM_REAL_ROOT/.mam" \ && prompt_file="$(mktemp "$MAM_REAL_ROOT/.mam/prompt-shloop-XXXXXX")" \ && printf '%s' "$2" > "$prompt_file" \ - || { log_error "delegate_job_safe: cannot write the prompt file under $MAM_REAL_ROOT/.mam"; return 1; } + || { [ -n "$prompt_file" ] && rm -f "$prompt_file" + log_error "delegate_job_safe: cannot write the prompt file under $MAM_REAL_ROOT/.mam" >&2; return 1; } args+=(--prompt-file "$prompt_file") shift 2 else diff --git a/tests/test_issue36_prompt_transport.py b/tests/test_issue36_prompt_transport.py index 81312c7..7ee73be 100644 --- a/tests/test_issue36_prompt_transport.py +++ b/tests/test_issue36_prompt_transport.py @@ -537,3 +537,69 @@ def test_s7b_shell_loop_ends_nonzero_when_a_job_cannot_be_registered(tmp_path, r assert f"{refuse} registration refused" in res.stderr assert "delegate_job_safe failed (exit 2)" in res.stderr assert "100% PASS" not in out + + +# --------------------------------------------------------------------------- +# S8 - create_session's onboarding / --submit-job: the task reaches the agent +# through a brief in the job directory, never inside the injected text +# (herdr `agent prompt` takes that text as one argv string). Real +# create_session.sh, registry and send_keys_safe; herdr and the agent CLIs are +# the conftest executables. From the review of job cc849cbf (codex). +# --------------------------------------------------------------------------- + +@pytest.mark.parametrize("size", [100, 130_500]) # the second is under the cap on its own +def test_s8_create_session_task_reaches_the_pane_through_a_brief(mam_sandbox, mock_herdr, mock_agents, + monkeypatch, size): + import shutil + monkeypatch.setenv("MAM_ONBOARD_ACK_S", "1") + monkeypatch.setenv("SKS_DELIVERY_GRACE_S", "0") + script = mam_sandbox / ".agents/skills/multi-agent-mux-create/scripts/create_session.sh" + prompt = "q" * size + cmd = ["bash", str(script), "--workspace", str(mam_sandbox), "--agent", "claude", + "--role", "Creator", "--session", "review36-creator-claude", "--submit-job", prompt] + trace = mam_sandbox / "exec.trace" + strace = shutil.which("strace") + if strace: # the kernel's own verdict on every exec, when available + cmd = [strace, "-f", "-s", "100", "-e", "trace=execve", "-o", str(trace)] + cmd + result = subprocess.run(cmd, cwd=mam_sandbox, capture_output=True, text=True, timeout=150) + out = result.stdout + result.stderr + + records = [json.loads(p.read_text()) for p in (mam_sandbox / ".mam/jobs").glob("*.json")] + assert [r for r in records if r.get("prompt") == prompt], out + jid = next(r["job_id"] for r in records if r.get("prompt") == prompt) + brief = mam_sandbox / ".mam/jobs" / jid / "brief.md" + assert brief.read_text() == prompt + + calls = [str(c) for c in json.loads(mock_herdr.read_text())["calls"]] + injected = [c for c in calls if "agent" in c and "prompt" in c and jid in c] + assert injected, f"rc={result.returncode}; no onboarding text reached herdr\n{out[-3000:]}" + text = injected[0] + assert str(brief) in text and prompt not in text + # the #23 ack contract is unchanged: the publish command names this dispatch + assert "--expect-dispatch-token" in text and "--event started" in text + if strace: + assert not [line for line in trace.read_text().splitlines() if "E2BIG" in line] + + +# --------------------------------------------------------------------------- +# S9 - the shell loop's delegate_job_safe: a prompt file that cannot be written +# is removed, and the cause reaches stderr (callers capture stdout). The real +# function is taken from run_loop.sh; the write fails for real (RLIMIT_FSIZE). +# From the review of job cc849cbf (codex). +# --------------------------------------------------------------------------- + +def test_s9_shell_loop_removes_a_partial_prompt_file_and_says_why(tmp_path): + source = (SKILLS / "multi-agent-mux-loop/scripts/run_loop.sh").read_text() + function = source[source.index("delegate_job_safe() {"):source.index("\nlog_info() {")] + (tmp_path / "input.md").write_text("x" * 2000) + harness = tmp_path / "run.sh" + harness.write_text('set -euo pipefail\nREPO_ROOT="$PWD"\nMAM_REAL_ROOT="$PWD"\n' + 'log_error() { printf "%s\\n" "$1"; }\n' + function + + '\ntrap "" XFSZ\nulimit -f 1\n' + 'output=$(delegate_job_safe submit --prompt "$(cat input.md)")\n') + result = subprocess.run(["bash", str(harness)], cwd=tmp_path, capture_output=True, text=True, timeout=60) + assert result.returncode != 0 + assert "File too large" in result.stderr + leftovers = list((tmp_path / ".mam").glob("prompt-shloop-*")) + assert not leftovers, [(p.name, p.stat().st_size) for p in leftovers] + assert "delegate_job_safe: cannot write the prompt file" in result.stderr