From 77f2829b8e1416c9fe0c0b0162536f046e82e3e9 Mon Sep 17 00:00:00 2001 From: Godopu Date: Sun, 13 Sep 2026 20:17:51 +0900 Subject: [PATCH] fix(issue-18): default ack timeout T = 180 s In the v4.5.0 review round grok read the brief and published `started` 70 s after the injection; with T = 60 the wrapper had already closed the job as agent_blocked and refused its later PASS. T bounds delivery, not work, so a generous default costs nothing when the agent answers fast. Code, docs and the CLI snapshot updated together. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff --- .agents/MULTI_AGENT_RULES.md | 2 +- .agents/skills/lib_py/delegation/registry.py | 6 ++++-- .agents/skills/multi-agent-mux-delegate-job/SKILL.md | 2 +- .../multi-agent-mux-delegate-job | 4 ++-- .agents/skills/multi-agent-mux-delegate-job/registry.md | 2 +- .agents/skills/multi-agent-mux-loop/SKILL.md | 2 +- MESSAGING.md | 2 +- VERSIONS.md | 4 ++-- tests/test_issue18_liveness_contract.py | 2 +- tests/test_issue5_phase1_delegation.py | 2 +- 10 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.agents/MULTI_AGENT_RULES.md b/.agents/MULTI_AGENT_RULES.md index 19527d5..322a20f 100644 --- a/.agents/MULTI_AGENT_RULES.md +++ b/.agents/MULTI_AGENT_RULES.md @@ -168,7 +168,7 @@ To ensure that agents running in TMUX environments do not lose debug logs or pre - **Identity Verification**: The guard validates process liveness via `pid` + `lstart` to prevent livelocks on PID rollover. ### ⏱️ Timeout Configuration & Alignment Rules -- **Job Execution Limits (`timeout_sec` & `idle_timeout_sec`)**: Each job independently manages its overall execution timeout (`timeout_sec`, default 3600s) and idle timeout without receiving messages (`idle_timeout_sec`). Since v4.5.0 (Issue #18) the idle timeout is the liveness contract's K x N: the agent heartbeats every `heartbeat_sec` (N, default 30 — automatic once it publishes `started --heartbeat N`), and `heartbeat_misses` (K, default 4) missed heartbeats close the job as `deadline_timeout` (default 120 s, `--idle-timeout` overrides). `started` must arrive within `ack_timeout_sec` (T, default 60) of the injection or the job fails as `agent_blocked` without re-injection. herdr status and pane text are never used for these decisions. +- **Job Execution Limits (`timeout_sec` & `idle_timeout_sec`)**: Each job independently manages its overall execution timeout (`timeout_sec`, default 3600s) and idle timeout without receiving messages (`idle_timeout_sec`). Since v4.5.0 (Issue #18) the idle timeout is the liveness contract's K x N: the agent heartbeats every `heartbeat_sec` (N, default 30 — automatic once it publishes `started --heartbeat N`), and `heartbeat_misses` (K, default 4) missed heartbeats close the job as `deadline_timeout` (default 120 s, `--idle-timeout` overrides). `started` must arrive within `ack_timeout_sec` (T, default 180) of the injection or the job fails as `agent_blocked` without re-injection. herdr status and pane text are never used for these decisions. - **Heartbeat timer and agent life**: the automatic timer is bound to the agent process that ran the start command (the nearest agent-CLI ancestor, or `--watch-pid`) and stops within one period once that process is gone; a crashed agent is therefore closed after K x N like any silent one, while an agent that is alive but stuck keeps heartbeating (process liveness, not work liveness). - **Monitor Idle Waiting (`SUB_IDLE_TIMEOUT`)**: The idle timeout for the monitor script (`reconcile.sh`), `SUB_IDLE_TIMEOUT`, must always be set generously to `3600s` (1 hour) or more to align with the maximum job budget. This prevents the monitor from terminating early due to idle detection, which would lose control over background tasks before they finish. diff --git a/.agents/skills/lib_py/delegation/registry.py b/.agents/skills/lib_py/delegation/registry.py index b774f21..8d3de48 100755 --- a/.agents/skills/lib_py/delegation/registry.py +++ b/.agents/skills/lib_py/delegation/registry.py @@ -52,10 +52,12 @@ def generate_job_id(bits: int = 32) -> str: # heartbeats (K x N seconds since the last event) as unresponsive; `started` # must arrive within T seconds of the prompt injection. K x N with these # defaults equals the historical 120 s idle timeout, so a caller that never -# asked for the contract sees the same idle window as before. +# asked for the contract sees the same idle window as before. T is generous: +# a real agent reads the brief and reasons before it runs the start command +# (grok took ~70 s in the v4.5.0 review round), and T only bounds delivery. DEFAULT_HEARTBEAT_SEC = 30 DEFAULT_HEARTBEAT_MISSES = 4 -DEFAULT_ACK_TIMEOUT_SEC = 60 +DEFAULT_ACK_TIMEOUT_SEC = 180 def liveness_window(heartbeat_sec: int, heartbeat_misses: int) -> int: diff --git a/.agents/skills/multi-agent-mux-delegate-job/SKILL.md b/.agents/skills/multi-agent-mux-delegate-job/SKILL.md index 77317b3..e073daa 100644 --- a/.agents/skills/multi-agent-mux-delegate-job/SKILL.md +++ b/.agents/skills/multi-agent-mux-delegate-job/SKILL.md @@ -40,7 +40,7 @@ multi-agent-mux-delegate-job submit \ --agent-session herdr: \ --prompt "Task description or instructions here" \ --role \ - --timeout 3600 [--heartbeat 30 --heartbeat-misses 4 --ack-timeout 60] [--idle-timeout ] + --timeout 3600 [--heartbeat 30 --heartbeat-misses 4 --ack-timeout 180] [--idle-timeout ] # Liveness contract (Issue #18): the agent heartbeats every N s (automatic), # the job is unresponsive after K x N s (--idle-timeout overrides), `started` # must arrive within T s of the injection or the inject fails closed. diff --git a/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job b/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job index f9718df..79ed31c 100755 --- a/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job +++ b/.agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job @@ -86,7 +86,7 @@ multi-agent-mux-delegate-job [options] # Liveness contract: the agent heartbeats every --heartbeat N s # (default 30, automatic), the job is unresponsive after # --heartbeat-misses K x N s (default 4 x 30 = 120; --idle-timeout - # overrides), `started` must arrive within --ack-timeout T s (60). + # overrides), `started` must arrive within --ack-timeout T s (180). status --job [--registry-dir ] list [--registry-dir ] verify --job --validate