feat(handoff): claim_pings/review_pings accept a list — ping every reviewer

Multi-reviewer setups (e.g. a correctness + a readability adversary) can now have
the watchdog ping ALL reviewers on a claim, each in its own session with its own
submit key. A bare string still works (single agent). _ping_agents() helper.
This commit is contained in:
2026-06-22 00:24:41 +00:00
parent 781db071dd
commit 98d198baa9
2 changed files with 21 additions and 11 deletions

View File

@ -599,15 +599,22 @@ def _show_pushed(cfg, repo, path):
return r.stdout
return ""
def _ping_agents(cfg, value, default, msg):
"""Ping one or more agents. `value` is an agent name, a LIST of names, or falsy (→ default).
Each target is pinged in its own session with its own backend's submit key — so a handoff can
notify multiple reviewers (e.g. claim_pings = ["correctness-adversary", "readability-adversary"])."""
names = value if isinstance(value, list) else [value or default]
for name in names:
agent = cfg["agents"].get(name)
session = agent["session"] if agent and agent.get("session") else (cfg["session_prefix"] + str(name))
submit = backend_of(cfg, agent).get("submit_key", "Enter") if agent else "Enter"
ping_session(session, msg, submit_key=submit)
def handoff_check(cfg):
h = cfg["loop"].get("handoff")
if not h:
return
repo = handoff_repo(cfg)
sub = lambda name: cfg["agents"].get(name, {}).get("session", cfg["session_prefix"] + name)
builder_name = h.get("review_pings", "builder")
submit = (backend_of(cfg, cfg["agents"][builder_name]).get("submit_key", "Enter")
if builder_name in cfg["agents"] else "Enter")
claim_pat = h.get("claim_pattern", "^claim")
review_pat = h.get("review_pattern", "^review")
_git(repo, "fetch -q origin")
@ -618,15 +625,15 @@ def handoff_check(cfg):
elif head != _hand["sha"]:
subjects = _git(repo, f"log --format=%s {_hand['sha']}..origin/main").stdout
if re.search(claim_pat, subjects, re.M | re.I):
log("handoff: claim commit → pinging reviewer")
ping_session(sub(h.get("claim_pings", "adversary")),
log("handoff: claim commit → pinging reviewer(s)")
_ping_agents(cfg, h.get("claim_pings", "adversary"), "adversary",
"watchdog ping: the other loop pushed a gate CLAIM commit. "
"Pull and verify the claimed gate now.", submit_key=submit)
"Pull and verify the claimed gate now.")
if re.search(review_pat, subjects, re.M | re.I):
log("handoff: review commit → pinging builder")
ping_session(sub(h.get("review_pings", "builder")),
_ping_agents(cfg, h.get("review_pings", "builder"), "builder",
"watchdog ping: the other loop pushed a verdict/finding commit. "
"Pull the review file and act.", submit_key=submit)
"Pull the review file and act.")
_hand["sha"] = head
inboxes = h.get("inboxes", [])
md5 = lambda s: hashlib.md5(s.encode()).hexdigest()
@ -642,9 +649,9 @@ def handoff_check(cfg):
hh = md5(content)
if hh != _hand[key]:
log(f"handoff: {fname} changed → pinging {target}")
ping_session(sub(target),
_ping_agents(cfg, target, target,
f"watchdog ping: the other loop pushed {sub_dir}/{fname} — pull, read it, "
f"act, then delete the file (commit + push) to mark it consumed.", submit_key=submit)
f"act, then delete the file (commit + push) to mark it consumed.")
_hand[key] = hh
else:
_hand[key] = ""