fix(id_order): server_not_running is unknown and cap 0 asks no herdr (Issue #63)

PR #69 review round 3 (rebuttal overruled; codex r2 applied as written).

- A failed `agent list`, herdr's server_not_running included, is unknown and
  keeps an orphaned marker; only a successful list without the name prunes.
- No probe floor: probes get only the time left until the wait deadline, so
  MAM_ID_ORDER_WAIT_S=0 asks herdr nothing (unknown, kept) and the refusal
  says so.
- The timeout message reports the last definite answer of this wait, so an
  alive orphan whose final probe ran out of time still reads as alive.
- Tests follow: server_not_running and cap 0 keep the marker and refuse;
  delayed-probe bound is the cap itself; probing tests use a small cap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
GodopuandClaude Opus 5.5 committed 2026-10-23 10:24:39 +09:00
1 parent 8f64174b6b
commit 7cac6f613e
2 files changed
+35 -34

No files matched your search

+15 -24
View File
@@ -30,9 +30,6 @@ from lib_py.agents.session_ids import canonical_cwd, claiming_rows, own_id
from lib_py.state import load_state_json
DEFAULT_WAIT_S = 600
# herdr probes of dead-owner markers share this budget past the wait deadline,
# so MAM_ID_ORDER_WAIT_S=0 still gets one look at whether an orphan is gone.
PROBE_FLOOR_S = 2.0
PROBE_TIMEOUT_S = 30.0
@@ -58,25 +55,12 @@ def pid_alive(pid: int) -> bool:
return False
def _server_not_running(*texts: str) -> bool:
"""herdr's own error envelope for a server that is down (lib.sh ISSUE-9)."""
for text in texts:
for line in [text] + text.splitlines():
try:
doc = json.loads(line)
except ValueError:
continue
err = doc.get("error") if isinstance(doc, dict) else None
if isinstance(err, dict) and err.get("code") == "server_not_running":
return True
return False
def herdr_session_state(server: str, session: str, timeout: float) -> Optional[bool]:
"""Is `session` live on herdr server `server`: True, False, or None when
herdr gave no authoritative answer. False only from a parsed `agent list`
without the name or herdr's server_not_running error; a failed, timed-out
or unparseable lookup is None. Never starts a server (MAM_NO_AUTOSTART)."""
herdr gave no authoritative answer. False only from a successful, parsed
`agent list` without the name; a failed (server down included), timed-out
or unparseable lookup, or no time left, is None. Never starts a server
(MAM_NO_AUTOSTART)."""
if timeout <= 0:
return None
skills_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
@@ -91,7 +75,7 @@ def herdr_session_state(server: str, session: str, timeout: float) -> Optional[b
except (OSError, subprocess.SubprocessError):
return None
if res.returncode != 0:
return False if _server_not_running(res.stdout, res.stderr) else None
return None
try:
agents = json.loads(res.stdout)["result"]["agents"]
except (ValueError, KeyError, TypeError):
@@ -294,13 +278,17 @@ def wait(cwd: str, agent: str, session: str, owner_pid: int,
os.makedirs(dpath, exist_ok=True)
lock_file = os.path.join(dpath, "lock")
deadline = time.monotonic() + cap
probe_until = max(deadline, time.monotonic() + PROBE_FLOOR_S)
announced = False
row_ref = {"name": session}
probe = pane_state or herdr_session_state
answers: Dict[str, bool] = {} # last definite answer per session, for the message
def bounded_probe(server: str, sess: str) -> Optional[bool]:
return probe(server, sess, probe_until - time.monotonic())
live = probe(server, sess, deadline - time.monotonic())
if live is not None:
answers[sess] = live
return live
while True:
with open(lock_file, "a", encoding="utf-8") as lf:
@@ -344,8 +332,11 @@ def wait(cwd: str, agent: str, session: str, owner_pid: int,
for m in markers:
if m.get("_orphan") and m.get("session") in blks:
srv = m.get("herdr_server") or "default"
seen = ("its herdr session is alive" if m["_orphan"] == "alive"
alive = m["_orphan"] == "alive" or answers.get(m["session"]) is True
seen = ("its herdr session is alive" if alive
else "herdr could not tell whether its session is gone")
if not alive and cap == 0:
seen += " (MAM_ID_ORDER_WAIT_S=0 leaves no time to ask herdr)"
print(
f"ERROR: {m['session']}'s create/resume exited before recording it, "
f"and {seen}; close it with: "