fix(followups): F4a/F4c - the runner's wait knobs follow the integer rule; MAM_WAIT_GRACE_S documented
MAM_WAIT_GRACE_S silently fell back to 300 on anything but bare digits
(" 30 " too), MAM_CHILD_WAIT_S went through float() (abc raised ValueError,
0.5 and 1e3 were taken) and MAM_JOB_TREE_MAX through int() (abc and an
empty value raised). All three now go through runner._int_knob, which is
_positive_int's rule (PR #43 review grok): decimal, blanks around allowed,
an invalid value warns once and the default is used. Minimums:
MAM_WAIT_GRACE_S >= 0 (wait exactly the budget), MAM_CHILD_WAIT_S >= 0
(look once; test_issue34 uses 0), MAM_JOB_TREE_MAX >= 1 (a cap of 0 calls
every tree "over"; not waiting is MAM_CHILD_WAIT_S=0).
The loop SKILL.md now names MAM_WAIT_GRACE_S in the budget row and the
sub-job paragraph (PR #41 M6), with the knobs' integer rules.
Tests: RUNNER_INT_KNOBS in test_issue26_ints.py holds the minimums; the
real wait_for_descendants is driven per knob, and a static check ties the
call-site minimums to the table (15 of the new cases fail at 0051fd6; the
14 that pass are valid values whose result did not change).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HCGpJXhSZasJLU38ksDghe
This commit is contained in:
1 parent
71ea8095c5
commit
8b046390be
3 files changed
+92
-8
No files matched your search
@@ -624,8 +624,22 @@ class LoopRunner:
|
||||
today's 3900 - 3600). The grace covers the wrapper's close/publish tail
|
||||
after the wall clock and the 1 s record poll; it is not proportional
|
||||
to the budget because nothing observed would justify that."""
|
||||
grace = os.environ.get("MAM_WAIT_GRACE_S", "")
|
||||
return self.job_budget + (int(grace) if grace.isdigit() else 300)
|
||||
grace = self._int_knob("MAM_WAIT_GRACE_S", 0)
|
||||
return self.job_budget + (300 if grace is None else grace)
|
||||
|
||||
def _int_knob(self, name: str, minimum: int) -> Optional[int]:
|
||||
"""$name by _positive_int's rule (the minimums are in
|
||||
tests/test_issue26_ints.py RUNNER_INT_KNOBS); None when unset, or when
|
||||
invalid - then with one warning per knob - so the caller uses its default."""
|
||||
raw = os.environ.get(name, "")
|
||||
n = _positive_int(raw, minimum)
|
||||
if n is None and raw:
|
||||
warned = self.__dict__.setdefault("_warned_knobs", set())
|
||||
if name not in warned:
|
||||
warned.add(name)
|
||||
print(f"WARNING: {name}={raw} ignored (not a decimal integer >= {minimum}); using the default",
|
||||
file=sys.stderr)
|
||||
return n
|
||||
|
||||
def wait_for_job(self, job_id: str, max_wait: Optional[float] = None, check_interval: float = 1.0) -> bool:
|
||||
"""Blocking wait for job completion or error with safety timeout."""
|
||||
@@ -745,11 +759,13 @@ class LoopRunner:
|
||||
A terminal record is not a stopped agent (a deadline close does not
|
||||
interrupt it); that is left to the sub-job table and the stale-review
|
||||
guard, since nothing signals such an agent's end."""
|
||||
child_env = os.environ.get("MAM_CHILD_WAIT_S")
|
||||
max_wait = float(child_env) if child_env else float(self.job_wait_s)
|
||||
wait_src = "MAM_CHILD_WAIT_S" if child_env else \
|
||||
child_wait = self._int_knob("MAM_CHILD_WAIT_S", 0)
|
||||
max_wait = float(child_wait if child_wait is not None else self.job_wait_s)
|
||||
wait_src = "MAM_CHILD_WAIT_S" if child_wait is not None else \
|
||||
f"MAM_CHILD_WAIT_S default: job budget {self.job_budget} + {self.job_wait_s - self.job_budget}"
|
||||
tree_max = int(os.environ.get("MAM_JOB_TREE_MAX", "50"))
|
||||
tree_max = self._int_knob("MAM_JOB_TREE_MAX", 1)
|
||||
if tree_max is None:
|
||||
tree_max = 50
|
||||
since = self._created_at(root)
|
||||
start = time.time()
|
||||
last = None
|
||||
|
||||
Reference in new issue
Block a user