fix(issue-30-33): review 5ec8ef33 - a multi-line codex draft is not an empty box, blocked notices reach the loop while the job runs, and both clocks are labelled on every close path
- R1 (P1): without rules the input region runs from the last prompt line to the end of the pane; only a last line matching the adapter's footer pattern (codex: ` <model> · ~/<cwd> · …`) is dropped, so a draft on a second line keeps the nudge withheld. - R2: while the synchronous submit runs, a relay thread finds the job it registers (same session, created since the submit) and prints its stalled:/stall_watch: notices; before, they appeared only after the job. - R3: the broker-unavailable disk wait that runs into the deadline is the wall clock: wall_clock_timeout (caller rc stays 3). - R4: a state_change_seq change seen right before the judgement restarts the idle window (decision 9: S seconds of continuous idle). Regression tests are the reviewer's reproductions, each with its passing twin; the wrapper-level #30 tests now use this environment's broker instead of the #18 harness's public default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
47e42744e7
commit
bf0f5c1014
8 files changed
+269
-12
No files matched your search
@@ -20,6 +20,7 @@ import sqlite3
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||
@@ -534,6 +535,11 @@ class LoopRunner:
|
||||
env = dict(os.environ)
|
||||
if "DELEGATE_JOB_PYTHON" not in env:
|
||||
env["DELEGATE_JOB_PYTHON"] = sys.executable
|
||||
stop = threading.Event()
|
||||
relay = threading.Thread(target=self._relay_while_submitting,
|
||||
args=(self.session_label(agent_session), job_registry._utcnow(), stop),
|
||||
daemon=True)
|
||||
relay.start()
|
||||
try:
|
||||
return subprocess.run(cmd, capture_output=True, text=True, cwd=str(self.workspace_root), env=env)
|
||||
except OSError as e:
|
||||
@@ -543,6 +549,8 @@ class LoopRunner:
|
||||
f"(prompt {len(p_text.encode('utf-8'))} bytes, wrapper {wrapper_script})")
|
||||
return subprocess.CompletedProcess(cmd, 126, "", msg)
|
||||
finally:
|
||||
stop.set()
|
||||
relay.join(timeout=5)
|
||||
try:
|
||||
os.unlink(prompt_file)
|
||||
except OSError:
|
||||
@@ -652,6 +660,25 @@ class LoopRunner:
|
||||
log_error(f"Job '{job_id}' timed out after {max_wait}s.")
|
||||
return False
|
||||
|
||||
def _relay_while_submitting(self, label: str, since: str, stop: threading.Event) -> None:
|
||||
"""Issue #30 (review R2): the wrapper runs until its job ends and its
|
||||
output is captured, so a blocked agent would only reach the loop log
|
||||
after the job. While the submit runs, find the job it registers (this
|
||||
session, created since the submit started) and relay its notices."""
|
||||
jid = None
|
||||
while not stop.wait(1.0):
|
||||
if jid is None:
|
||||
try:
|
||||
jobs = job_registry.list_jobs(str(self.jobs_dir))
|
||||
except (OSError, ValueError):
|
||||
continue
|
||||
mine = [r for r in jobs if r.get("agent_session") == label
|
||||
and str(r.get("created_at") or "") >= since]
|
||||
if not mine:
|
||||
continue
|
||||
jid = str(mine[-1].get("job_id"))
|
||||
self._relay_stall_warnings(jid)
|
||||
|
||||
def _relay_stall_warnings(self, job_id: str) -> None:
|
||||
"""Issue #30: surface the stall watcher's progress events (detail
|
||||
`stalled:...` / `stall_watch:...`) in the loop log, once per detail.
|
||||
|
||||
Reference in new issue
Block a user