fix(id_order): reserve startup when resume resolves an unrecorded uuid (Issue #63)

This commit is contained in:
Godopu committed 2026-10-21 19:48:44 +00:00
1 parent c747374aa6
commit d115bf7156
2 files changed
+72 -13

No files matched your search

+16 -13
View File
@@ -122,12 +122,14 @@ def blockers(state: Dict[str, Any], cwd: str, agent: str, me: str,
prev_epoch = int(m.get("prev_epoch") or 0)
prev_end_epoch = int(m.get("prev_end_epoch") or 0)
pinned_for_attempt = bool(cur_own) and (
cur_own != prev_own
or (status == "running" and row_epoch > prev_epoch)
)
if pinned_for_attempt:
continue
if status == "running":
pinned_for_attempt = bool(cur_own) and (
cur_own != prev_own or row_epoch > prev_epoch
)
if not pinned_for_attempt:
found.append(m_sess)
found.append(m_sess)
else:
ended_after_attempt = (
end_epoch > max(m_at, prev_end_epoch)
@@ -231,14 +233,15 @@ def wait(cwd: str, agent: str, session: str, owner_pid: int,
markers = _read_and_prune_markers(dpath, session, is_alive=is_alive)
blks = blockers(state, cwd, agent, session, markers, is_alive=is_alive)
if not blks:
if not uuid and owner_pid and owner_pid > 0:
prev_row = next(
(
s for s in (state.get("herdr_sessions") or [])
if isinstance(s, dict) and s.get("name") == session
),
None,
)
prev_row = next(
(
s for s in (state.get("herdr_sessions") or [])
if isinstance(s, dict) and s.get("name") == session
),
None,
)
already_recorded = bool(uuid) and own_id(prev_row, agent) == uuid
if not already_recorded and owner_pid and owner_pid > 0:
write_marker(dpath, session, owner_pid, cwd, agent, prev_row=prev_row)
return 0
+56
View File
@@ -978,3 +978,59 @@ def test_canonical_cwd_distinguishes_underscore_hyphen_and_nested_paths_with_sym
res_hyphen = decide_resume(state_stopped, "hyphen-agy", "agy", str(ws_hyphen), home=str(home))
assert res_hyphen["uuid"] == UB
assert res_hyphen["notes"] == []
def test_id_order_resolved_but_unrecorded_resume_uuid_reserves_startup_until_recorded(tmp_path, monkeypatch):
"""When a single stopped post-discovered row with no own_id resolves a uuid from disk,
id_order.wait reserves startup (writes a marker) until that uuid is recorded on the row."""
from lib_py.agents import id_order
from lib_py.agents.session_ids import decide_resume
home = tmp_path / "home"
ws = tmp_path / "ws"
ws.mkdir(parents=True)
yaml_path = tmp_path / "agent-sessions.yaml"
monkeypatch.setenv("HOME", str(home))
monkeypatch.setenv("AGENT_SESSIONS_YAML", str(yaml_path))
_agy_conv(home, UA, time.time() - 10)
_agy_cache(home, {ws: UA})
old_epoch = int(time.time()) - 100
row_a = _row("same-ws-creator-agy", "agy", ws, old_epoch, status="stopped", agy_conversation_id_own=None)
state = {"herdr_sessions": [row_a]}
yaml_path.write_text(yaml.safe_dump(state))
dec = decide_resume(state, "same-ws-creator-agy", "agy", str(ws), home=str(home))
assert dec["uuid"] == UA
proc_a = subprocess.Popen(["sleep", "120"])
proc_b = subprocess.Popen(["sleep", "120"])
try:
# Admit A with resolved (but not yet recorded) uuid UA -> writes startup marker
assert id_order.wait(
str(ws), "agy", "same-ws-creator-agy", proc_a.pid, uuid=UA,
yaml_path=str(yaml_path), home=str(home), wait_s_val=0,
) == 0
# While A has not yet recorded UA on its row, B must be refused at cap 0
assert id_order.wait(
str(ws), "agy", "same-ws-reviewer-agy", proc_b.pid, uuid="",
yaml_path=str(yaml_path), home=str(home), wait_s_val=0,
) == 1
# Once A records UA on its row (via update_yaml_resumed.sh), B is released
recorded_a = _row(
"same-ws-creator-agy", "agy", ws, old_epoch + 50,
pid=proc_a.pid, status="running", agy_conversation_id_own=UA,
)
yaml_path.write_text(yaml.safe_dump({"herdr_sessions": [recorded_a]}))
assert id_order.wait(
str(ws), "agy", "same-ws-reviewer-agy", proc_b.pid, uuid="",
yaml_path=str(yaml_path), home=str(home), wait_s_val=0,
) == 0
finally:
proc_a.kill()
proc_b.kill()
proc_a.wait()
proc_b.wait()