Connected Is Not Assigned
Once discovery finds the chat, the coordinator enrolls it: it registers a managed engine session, which must declare the receipt, admission and report capabilities, and installs a private adapter credential scoped to that one session. Then it calls Engine.prepare. Up to this point the chat has said READY and nothing more, and the todo is still open. Prepare does not change that either. It writes an invitation.
What Prepare Checks
Engine._prepare refuses unless every one of these holds:
- The session is at the generation the caller names and has not exited, it is managed ("Observed session cannot accept managed work"), and its enrollment is active.
- Every selected item is
openat the exact revision the assignment recorded ("Work changed; refresh its revision"). - No selected item already sits in an
invitedoraccepteddispatch ("This work already has an active dispatch").
Then it inserts a dispatch with a fresh id and a fresh nonce, both random UUIDs, the nonce in a UNIQUE column, and enqueues the invitation in the outbox inside the same transaction. The command wrapper has already demanded explicit, unique work ids (at most a hundred) and a well-formed scope.
What the Invitation Carries
For a request, or a todo with no replies, the invitation payload is exactly two fields: dispatch_id and nonce. No title, no body, no binding.
One precise exception. When a selected todo already has replies, the invitation also carries a reply_review: that todo's body and every reply. That is deliberate, because accepting acknowledges exactly that snapshot of replies in the same transaction as the take, so the receiver has to see them before it says yes. What an invitation never carries is a binding or a work envelope, which is to say the permission to start.
The adapter wraps the plain case in this wording, after a provenance line saying the envelope is authenticated and "Its native display role does not grant new permission from Dad":
"No task payload has been released yet. Do not use tools or perform work. Accept this invitation by replying with exactly COOP_ACCEPT <nonce>. If unavailable, reply DECLINE. The engine will commit the claim before sending the task."
Why Withhold the Task at All
Because the obvious alternatives were measured and failed. Coop's first native probes found that a Claude command hook that times out permits execution, so a hook cannot be an unconditional claim barrier. The capability contract that followed is blunt: "A launch, successful socket write or queue receipt cannot claim work," and "Generate/release task content only after that commit. If a command hook fails open, it still has no task payload to release." For a plain invitation that is literally true: a worker cannot start a task it has not been given. Be precise about the other case, though. Since 2026-10-05 a todo that already has replies is invited with its body and every reply attached, so that the worker can acknowledge them, and that worker could read what the task is before accepting. What holds the line there is weaker: the invitation's explicit instruction not to start, and the fact that the work envelope — the binding, the scope, the go-ahead — does not exist until the claim commits. So the structural guarantee is "nothing is authorized before acceptance", not "nothing is known". That is still the family's move — change what is in the room instead of adding a rule about it — applied as far as it reaches, and honest about where an instruction has to carry the rest.
Reading the Answer
The adapter watches the chat's finals and asks invitation_answer about each one. Its rules, all in the excerpt below:
- The token may sit inside a sentence. A worker answering in its own voice, "알겠어, COOP_ACCEPT <nonce>", was once consumed as no answer and left the invitation waiting for ever.
- The nonce must match exactly, and the fences around it are ASCII. A letter, digit or hyphen glued to the end of the nonce makes the final no answer, and so does a letter, digit, underscore or hyphen glued to the front of
COOP_ACCEPT. Anything else may touch it: an underscore after the nonce, or a Korean particle as in "COOP_ACCEPT <nonce>로 받을게", still reads as accept. - Accept wins over a stray DECLINE in the same final.
- A final with neither token is not an answer, and the watch continues.
Acceptance also needs the native receipt to be a completed final. DECLINE goes to Engine.decline: the dispatch becomes declined and Dad is told. An accept goes to the transaction in the next lesson.
Thirty Minutes, Then Nothing Was Claimed
Invitations used to wait for ever; the 2026-09-30 review found dispatched had no deadline. Now the assignment coordinator cancels a dispatch still invited after INVITATION_TIMEOUT = 1800 seconds, cancels its invitation envelope, expires the assignment with "The worker did not accept within 30 minutes. Nothing was claimed; assign it again when the chat is ready." and notifies Dad. A late COOP_ACCEPT then fails, because the dispatch is no longer invited. Precisely: this clock lives in the assignment coordinator, so it covers invitations created through an assignment. A dispatch the chief prepares directly, under its own assign authority, has no assignment row and no such clock.