fix(issue-36): create_session hands the agent a brief path, not the task; a prompt file that cannot be written is removed
Review off26952c(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 <jobs>/<id>/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 onf26952c. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f26952ccc3
commit
59be1c8920
3 files changed
+82
-2
No files matched your search
@@ -622,6 +622,19 @@ if [ -n "$DELEGATE_JOB_ID" ]; then
|
|||||||
# dispatch (trial-2 finding: without them the token-checking ack below could
|
# 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).
|
# 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")"
|
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).
|
instructions="Your job_id is \"$DELEGATE_JOB_ID\" (the one just registered for THIS delegation).
|
||||||
|
|
||||||
On start run: $pub --event started.
|
On start run: $pub --event started.
|
||||||
@@ -629,7 +642,7 @@ On progress (optional): $pub --event progress --detail '<short status>'.
|
|||||||
On success run: $pub --event completed --detail '<one-line summary>'.
|
On success run: $pub --event completed --detail '<one-line summary>'.
|
||||||
On failure run: $pub --event error --detail '<one-line reason>'.
|
On failure run: $pub --event error --detail '<one-line reason>'.
|
||||||
|
|
||||||
Task: $SUBMIT_JOB_PROMPT"
|
Task: read the complete task in $task_brief and follow it."
|
||||||
|
|
||||||
# Inject instructions into the herdr pane
|
# Inject instructions into the herdr pane
|
||||||
rc=0
|
rc=0
|
||||||
|
|||||||
@@ -201,7 +201,8 @@ delegate_job_safe() {
|
|||||||
mkdir -p "$MAM_REAL_ROOT/.mam" \
|
mkdir -p "$MAM_REAL_ROOT/.mam" \
|
||||||
&& prompt_file="$(mktemp "$MAM_REAL_ROOT/.mam/prompt-shloop-XXXXXX")" \
|
&& prompt_file="$(mktemp "$MAM_REAL_ROOT/.mam/prompt-shloop-XXXXXX")" \
|
||||||
&& printf '%s' "$2" > "$prompt_file" \
|
&& 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")
|
args+=(--prompt-file "$prompt_file")
|
||||||
shift 2
|
shift 2
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -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 f"{refuse} registration refused" in res.stderr
|
||||||
assert "delegate_job_safe failed (exit 2)" in res.stderr
|
assert "delegate_job_safe failed (exit 2)" in res.stderr
|
||||||
assert "100% PASS" not in out
|
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
|
||||||
Reference in new issue
Block a user