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
@@ -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
|
||||
Reference in new issue
Block a user