From 9e78ff3ba3d65d432205ff15e955455ff7bb4891 Mon Sep 17 00:00:00 2001 From: Godopu Date: Sat, 10 Oct 2026 18:14:48 +0000 Subject: [PATCH] fix(issue-50): the loop path reads this attempt's token before it waits (review reviewer-grok-01) The `--status pending` reset is a redispatch and issues a new dispatch token, but the loop read `_disp_tok` only after the registry and idle waits. A busy or idle_wait close from phase two on therefore carried the previous phase's token and the engine refused it, leaving the record pending while the caller saw session_busy. The read now follows the reset directly; s1 pins reset < token read < registry wait. Co-Authored-By: Claude Fable 5.1 --- .../multi-agent-mux-delegate-job | 9 ++++++--- tests/test_issue50_dispatch.py | 4 ++++ 2 files changed, 10 insertions(+), 3 deletions(-) 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 99dd45c..5fdb64b 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 @@ -528,6 +528,12 @@ EOF --role "$display_role" \ --status "pending" + # The reset above is a redispatch: it issued this attempt's token. Read it + # now, so a close from the waits below (busy, idle_wait) names this + # attempt, not the previous phase's (review reviewer-grok-01) + local _disp_tok="" + _disp_tok="$("$PY" -c "import json, os; p=os.path.join('$REGISTRY_DIR', '$JOB_ID.json'); print(json.load(open(p)).get('dispatch_token','') or '') if os.path.exists(p) else print('')" 2>/dev/null || true)" + # Registry wait (Issue #50), then idle wait - on the record just reset to # pending for this phase, so the waiter's signal is written and other # waiters see this job in the FIFO (review F1) @@ -546,9 +552,6 @@ EOF fi fi - local _disp_tok="" - _disp_tok="$("$PY" -c "import json, os; p=os.path.join('$REGISTRY_DIR', '$JOB_ID.json'); print(json.load(open(p)).get('dispatch_token','') or '') if os.path.exists(p) else print('')" 2>/dev/null || true)" - # Start subscriber local logf="$REGISTRY_DIR/${JOB_ID}.iter_${iteration}_${display_role}.subscriber.out" local loop_sub_args=(--attempt-id "$attempt" --ack-timeout "$TIMEOUT") diff --git a/tests/test_issue50_dispatch.py b/tests/test_issue50_dispatch.py index 20e8594..ace0301 100644 --- a/tests/test_issue50_dispatch.py +++ b/tests/test_issue50_dispatch.py @@ -285,6 +285,10 @@ def test_s1_both_dispatch_paths_wait_by_the_registry_before_the_idle_wait(): # phase (review F1): a terminal self record cannot carry the signal (u3c) assert src.count('--status "pending"') == 1 assert src.index('--status "pending"') < loop + # ... and reads the token that reset issued before it waits: a busy or + # idle_wait close from phase two on must carry this attempt's token + tok = src.index('_disp_tok="$(', src.index('--status "pending"')) + assert src.index('--status "pending"') < tok < loop body = src.split("_registry_wait() {")[1].split("\n}\n")[0] assert "wait-open-jobs" in body and 'finalize_unfinished "$job_id" "error" "$SUB_RC_ERROR" "registry_wait"' in body assert "exit 5" in body and "mam_int_knob MAM_DISPATCH_WAIT_S 600 0" in body