A delegation queue offers one job for one session to claim. An Open Run queue offers seats in a shared contest. Calling both operations “take” hides the difference: an Anvil participant joins a run whose role, phase, composed expectations, and existing seats already matter.
The phrase join anvil queue #N starts with the public resolver, which returns only a run summary and protocol pointer. The session then declares its actual harness, model, and effort. If that differs from the expected pair, the session asks Dad to confirm the override in that conversation; the expected value remains visible beside the actual joined-as truth.
A join is state-sensitive, not universally idempotent. The resolver may offer an open entry seat, a later phase for a session that already holds a mask, or a precise conflict because the role is full or the run has moved on. Repeating the phrase must re-resolve current truth; it must not assume an old seat is still the valid target.
That is why next-round work has its own explicit phrase and mask-aware rejoin. The protocol coordinates a changing board rather than promising that the same words always return the same binding.
Join resolves the live board; take claims a task. Preserve expected intent and actual identity without freezing yesterday's phase.
Code
A state-aware join — same phrase, different answer·python
def resolve_join(run, session):
"""Repeating the phrase re-resolves current truth, every time."""
if run["state"] == "closed":
return {"code": "run_passed"}
if session["mask"] and run["phase"] > 0:
return {"ok": True, "action": "rejoin", "mask": session["mask"],
"phase": run["phase"]}
if run["open_seats"] <= 0:
return {"code": "role_full"}
if session["actual"] != session["expected"]:
return {"ok": True, "needs": "override_confirm",
"expected": session["expected"], "joined_as": session["actual"]}
return {"ok": True, "action": "enter", "seat": "open"}
run = {"state": "live", "phase": 0, "open_seats": 1}
fresh = {"mask": None, "expected": "h1/m1", "actual": "h1/m1"}
print(resolve_join(run, fresh))
mismatch = {"mask": None, "expected": "h1/m1", "actual": "h1/m2"}
out = resolve_join(run, mismatch)
# Both survive — neither value erases the other.
assert out["expected"] and out["joined_as"]
print(out)
later = {"mask": "mask-A", "expected": "h1/m1", "actual": "h1/m1"}
print(resolve_join({**run, "phase": 2}, later))