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
@@ -307,11 +307,13 @@ class StallWatch(object):
|
||||
st.blocked_notified = False
|
||||
sig.agent_status = new
|
||||
try:
|
||||
self._act(sig, idle_since)
|
||||
idle_since = self._act(sig, idle_since)
|
||||
except HerdrError as exc:
|
||||
log.warning("stall_watch: herdr call failed while judging: %s", exc.code)
|
||||
|
||||
def _act(self, sig, idle_since):
|
||||
"""Notify/nudge per `judge`; returns the idle start to keep (restarted
|
||||
when a transition was missed between the wait and the judgement)."""
|
||||
st, agent = self.st, self.args.agent
|
||||
if sig.agent_status == "blocked" and not st.blocked_notified:
|
||||
sig.blocked_text = self.src.blocked_text(sig.pane_id or self.src.agent_get().get("pane_id", ""))
|
||||
@@ -319,16 +321,21 @@ class StallWatch(object):
|
||||
log.warning("stall_watch: agent %s is blocked: %s", self.args.target, sig.blocked_text)
|
||||
self._notify(("stalled:blocked " + sig.blocked_text).strip())
|
||||
st.blocked_notified = True
|
||||
return
|
||||
return idle_since
|
||||
if sig.agent_status not in IDLE_STATES or not st.reliable or st.nudged:
|
||||
return
|
||||
return idle_since
|
||||
sig.idle_for = time.monotonic() - idle_since
|
||||
if sig.idle_for < st.idle_s:
|
||||
return
|
||||
return idle_since
|
||||
seq = sig.state_change_seq
|
||||
self.src.collect(sig)
|
||||
if seq is not None and sig.state_change_seq is not None and sig.state_change_seq != seq:
|
||||
return # a transition we did not see: the next wait re-observes
|
||||
# Review R4: a transition we did not see. Decision 9 wants S seconds
|
||||
# of *continuous* idle, so the window restarts from this observation
|
||||
# (collect() already took the new seq as the baseline).
|
||||
log.info("stall_watch: state_change_seq %s -> %s since the idle start; idle window restarts",
|
||||
seq, sig.state_change_seq)
|
||||
return time.monotonic()
|
||||
if sig.background_children:
|
||||
log.debug("stall_watch: idle but background children %s", sig.background_children)
|
||||
elif sig.input_empty is None:
|
||||
@@ -345,6 +352,7 @@ class StallWatch(object):
|
||||
self._notify("stalled:idle")
|
||||
st.nudged = True
|
||||
self.src.prompt(NUDGE.format(job=self.args.job))
|
||||
return idle_since
|
||||
|
||||
|
||||
def main(argv=None) -> int:
|
||||
|
||||
Reference in new issue
Block a user