fix(issue-36): the loop passes every delegated prompt as a file; a wrapper that cannot start is a failed submit, on the first try and on the retry
delegate_job writes the prompt to a temp file under the control root's .mam/ and hands the wrapper --prompt-file; the file is removed once the wrapper returns. An OSError from starting the wrapper becomes a failed submit (rc 126) that the existing error branch logs, so the session-busy retry no longer dereferences a missing result. A retry that yields no job id, and a compliance job that cannot be registered, are now logged instead of silent. The collector caps are documented as the size of the evidence a reviewer is shown; delivery has no cap of its own any more. The loop skill notes that a pipeline hides the loop's exit code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
68a5d31381
commit
c77806b1b1
3 files changed
+32
-3
No files matched your search
@@ -464,7 +464,17 @@ class LoopRunner:
|
||||
if not wrapper_script.exists():
|
||||
wrapper_script = Path(__file__).resolve().parent.parent / "multi-agent-mux-delegate-job" / "multi-agent-mux-delegate-job"
|
||||
|
||||
def _run_cmd(p_text: str):
|
||||
def _run_cmd(p_text: str) -> subprocess.CompletedProcess:
|
||||
# Issue #36: the prompt goes by file. One argv string is capped at
|
||||
# MAX_ARG_STRLEN (131,072 bytes on Linux), and a review prompt
|
||||
# carries the whole diff. The wrapper blocks until the job ends,
|
||||
# so nothing reads the file after it returns.
|
||||
self.mam_dir.mkdir(parents=True, exist_ok=True)
|
||||
with tempfile.NamedTemporaryFile("w", encoding="utf-8", dir=self.mam_dir,
|
||||
prefix=f"prompt-{self.loop_id}-", suffix=".md",
|
||||
delete=False) as fh:
|
||||
fh.write(p_text)
|
||||
prompt_file = fh.name
|
||||
cmd = [
|
||||
"bash", str(wrapper_script), "submit",
|
||||
"--agent-session", self.session_label(agent_session),
|
||||
@@ -472,12 +482,24 @@ class LoopRunner:
|
||||
"--type", "direct",
|
||||
"--role", role,
|
||||
"--registry-dir", str(self.jobs_dir),
|
||||
"--prompt", p_text,
|
||||
"--prompt-file", prompt_file,
|
||||
] + self.contract_args()
|
||||
env = dict(os.environ)
|
||||
if "DELEGATE_JOB_PYTHON" not in env:
|
||||
env["DELEGATE_JOB_PYTHON"] = sys.executable
|
||||
return subprocess.run(cmd, capture_output=True, text=True, cwd=str(self.workspace_root), env=env)
|
||||
try:
|
||||
return subprocess.run(cmd, capture_output=True, text=True, cwd=str(self.workspace_root), env=env)
|
||||
except OSError as e:
|
||||
# A wrapper that cannot start is a failed submit (126, the shell's
|
||||
# "cannot execute"), on the first try and on the retry alike.
|
||||
msg = (f"cannot start the wrapper: {e} "
|
||||
f"(prompt {len(p_text.encode('utf-8'))} bytes, wrapper {wrapper_script})")
|
||||
return subprocess.CompletedProcess(cmd, 126, "", msg)
|
||||
finally:
|
||||
try:
|
||||
os.unlink(prompt_file)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
res = _run_cmd(prompt)
|
||||
match = re.search(r"registered job:\s*([A-Za-z0-9_-]+)", res.stdout)
|
||||
@@ -500,6 +522,7 @@ class LoopRunner:
|
||||
self.failed_submit_jobs.add(job_id_retry)
|
||||
log_error(f"delegate_job retry failed (exit {res_retry.returncode}):\n{res_retry.stderr or res_retry.stdout}")
|
||||
return job_id_retry
|
||||
log_error(f"delegate_job retry failed (exit {res_retry.returncode}):\n{res_retry.stderr or res_retry.stdout}")
|
||||
if job_id:
|
||||
self.created_jobs.append(job_id)
|
||||
self.failed_submit_jobs.add(job_id)
|
||||
@@ -1362,6 +1385,8 @@ class LoopRunner:
|
||||
comp_jid = self._dispatch(self.creator, creator_type, "Worker", comply_prompt)
|
||||
if comp_jid:
|
||||
self.wait_for_job(comp_jid)
|
||||
else:
|
||||
log_error("Failed to register Creator compliance job.")
|
||||
|
||||
# Promotion
|
||||
if self.plan_mode and self.current_plan and self.plan_job_id:
|
||||
|
||||
Reference in new issue
Block a user