fix(issue-9): translate only a structured server_not_running error in the shim's ls

The ls translation searched stderr and the whole stdout for the substring
server_not_running, so a successful `agent list` that merely carried the text
(an agent named server_not_running-codex, or metadata) made the shim report
"no server running". reconcile.sh reads that as a confirmed-empty server, and
drift A would then mark every live row on it terminated (codex review of
0918917).

- keep the RPC exit code and translate only a failed call whose error
  envelope (the whole stream or any line of it) has
  error.code == "server_not_running"; every other failure keeps the unknown
  path (nonzero exit, herdr_confirmed False) as before
- tests: the real herdr 0.9 envelope, a successful list carrying the
  substring in a name and in metadata, other failures staying unknown, and
  reconcile keeping both live rows with no drift A; the new cases fail on
  0918917
- delegate wrapper: log a non-pending status seen during the inject ack
  grace as "registry advanced" instead of attributing it to the agent
- VERSIONS.md v4.4.2: describe the structured check and count 16 new tests

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HCGpJXhSZasJLU38ksDghe
This commit is contained in:
GodopuandClaude Opus 5 committed 2026-09-13 00:32:57 +09:00
1 parent 091891756b
commit 7c40bf4930
5 files changed
+150 -22

No files matched your search

+32 -5
View File
@@ -1245,15 +1245,42 @@ except Exception:
# running", tmux's phrasing) instead of an opaque parse failure, which
# would flip herdr_confirmed=False and freeze drift detection for every
# server on the next heartbeat.
# Only a FAILED call whose structured error envelope carries
# error.code == "server_not_running" means "down". A successful list can
# legitimately contain that text (an agent named server_not_running-codex),
# and reading it as "down" made reconcile terminate every live row on that
# server. Any other failure falls through to the unknown path below.
_ls_err=$(mktemp 2>/dev/null || echo "/tmp/mam_ls_err_$$")
_ls_raw="$(_real_herdr agent list 2>"$_ls_err")" || true
if grep -q server_not_running "$_ls_err" 2>/dev/null \
|| printf '%s' "$_ls_raw" | grep -q server_not_running; then
rm -f "$_ls_err" 2>/dev/null || true
_ls_rc=0
_ls_raw="$(_real_herdr agent list 2>"$_ls_err")" || _ls_rc=$?
_ls_down=0
if [ "$_ls_rc" -ne 0 ]; then
_ls_down=$(printf '%s' "$_ls_raw" | MAM_LS_ERR_FILE="$_ls_err" python3 -c '
import json, os, sys
out = sys.stdin.read()
try:
with open(os.environ["MAM_LS_ERR_FILE"]) as f:
err_text = f.read()
except Exception:
err_text = ""
down = 0
for text in [out, err_text] + out.splitlines() + err_text.splitlines():
try:
doc = json.loads(text)
except Exception:
continue
err = doc.get("error") if isinstance(doc, dict) else None
if isinstance(err, dict) and err.get("code") == "server_not_running":
down = 1
break
print(down)
' 2>/dev/null || echo 0)
fi
rm -f "$_ls_err" 2>/dev/null || true
if [ "$_ls_down" = "1" ]; then
echo "no server running (herdr session ${_sess:-default})" >&2
exit 1
fi
rm -f "$_ls_err" 2>/dev/null || true
printf '%s\n' "$_ls_raw" | \
MAM_LS_FORMAT="$format" MAM_REAL_HERDR="$REAL_HERDR" MAM_LS_SESSION="$_sess" \
TZ=UTC LC_ALL=C python3 -c '
@@ -569,15 +569,18 @@ _disk_status() {
# reports `working`, yet publishes `started` within seconds. Closing such a job
# as agent_blocked strands a live agent whose later `completed` the CAS table
# then refuses. Before finalising, give the registry MAM_INJECT_ACK_GRACE_S
# (default 10 s) to show the agent's own acknowledgement: any status other than
# pending means the prompt was delivered.
# (default 10 s) to advance past pending. Any other status means the job moved
# on - normally the agent's own `started` - so the prompt is treated as
# delivered. The status is not pinned to this dispatch and may come from an
# external writer; the terminal handling after this still keeps such an
# error/cancelled as it is rather than turning it into success.
_inject_acknowledged() {
local job_id="$1" deadline st
deadline=$(( $(date +%s) + ${MAM_INJECT_ACK_GRACE_S:-10} ))
while :; do
st="$(_disk_status "$job_id")"
if [ -n "$st" ] && [ "$st" != "pending" ]; then
echo "inject: herdr could not confirm delivery, but job $job_id is already '$st' — the agent acknowledged it; continuing." >&2
echo "inject: herdr could not confirm delivery, but job $job_id already advanced to '$st' in the registry; treating the prompt as delivered and continuing." >&2
return 0
fi
[ "$(date +%s)" -ge "$deadline" ] && return 1