agentleFS
sammasaksammasak/claude-code-skills/skills/agent-handoff/SKILL.md

agent-handoff

Skill1 starsChanged 28 days agoMITView on GitHub

Use when the owner wants to hand work to a specific running coding agent, draft an assignment or brief for an agent, send an agent a message or prompt through Herdr, or wait for a running agent to answer and relay what it said. Drafts first, sends only after the owner confirms, and never treats a reply as acceptance. Not for reading agent status (herdr-operations), starting a new session on a host (agent-orchestration), or delegating small mechanical fixes to the Pi worker (pi-offload).

15 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

1 rule. 0 are common, 0 are distinctive, and 1 is very specific to this project.

  • 0

    Common

    Most of the 11 edited versions we read keep these.

  • 0

    Distinctive

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Only the owner can ask for resend, and the task refuses one when the agent's state advanced in between (probably_received).

    line 76

“Common” is measured: kept by at least 6 in 10 of the 11 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Agent handoff (draft, confirm, send, await, verify)

Sending is the only act-class operation the Coordinator has. The sequence is fixed; skipping a step is a boundary violation.

  1. Identify the target from a fresh herdr:inventory record: its machine (the saved Herdr machine label; null means this host, then omit it) and exact agent_id. A blocked agent is never a target; tell the owner what it is waiting for instead.
  2. Draft with coord:handoff, copying machine, agent_id and session_id from the inventory record so the send is bound to that agent, not just its pane: devenv tasks run coord:handoff --input-json '{"machine":"<label>","agent_id":"<id>","session_id":"<session>","task_ref":"#N","scope":"...","expected_evidence":"...","acceptance":"...","allowed_actions":["comment","deploy:none"],"owner_input":"..."}' allowed_actions is required: a non-empty list (at most 8) of distinct entries from merge, close, label, comment, deploy:none, deploy:<host> (deploy:none excludes deploy:<host>). The task puts it in canonical order (merge, close, label, comment, deploy:* by host), so the same set always gives the same handoff_id, and renders it into the brief as the Allowed actions: line, which also tells the agent that reading, committing and opening a PR are always implied and that anything else, deleting included, is not allowed. scope, expected_evidence, acceptance and owner_input take up to 4000 characters each. Show the owner the returned text, handoff_id and target (host is the machine label, or this hostname) verbatim. Nothing has been sent.
  3. Confirm: the owner must say, in this conversation, that this handoff_id may go to this host/agent_id. Exception: inside the coordinator role (agents/coordinator/ROLE.md, Autonomy) the owner's label on the item replaces this per-draft confirmation; the send gate and its checks are the same. Silence, "looks good", or a general "go ahead" from earlier does not count. Note the GitHub issue (or PR) URL and the label the owner applied there for this handoff: the send records them as authorized_by. If the owner has not named an issue URL and a label, ask (inside the coordinator role, take them from gh:work); never infer or invent one. The authorizing issue is usually the handoff's task_ref, but it may be a tracker the owner gates from. The send fetches that issue's labels through gh and refuses with authorization_missing when the label is not there, when it is one the coordinator writes (needs-decision, blocked, needs-live), or when the audit log shows a gh:label act added it. The label is the owner's act; the record keeps who applied it and when (labeled_by, labeled_at, from the issue's label events, null when not found).
  4. Send with herdr:message (prompts for permission every time), the confirm string being exactly the draft's host/agent_id and authorized_by naming the issue URL and owner label: devenv tasks run herdr:message --input-json '{"handoff_id":"<id>","confirm":"<host>/<agent_id>","authorized_by":{"issue_url":"https://github.com/OWNER/REPO/issues/N","label":"<label>"}}' It forwards through herdr --machine <label> agent prompt for a saved machine, only sends to an idle/done agent and refuses: agent_blocked, agent_busy (mid-turn: wait, then send), worker_unavailable (its pane ends on the harness's usage-limit line: hand the item to another available worker, or the owner switches its model), author_merge (merge for the pane that implemented the task_ref; coord:handoff refuses it too: the reviewer merges, never the implementer), agent_changed (session differs from the draft), an edited draft (text or allowed_actions), a draft without allowed_actions (redraft it), a missing or malformed authorized_by, authorization_missing (the label is not on the issue), gh_error (the labels could not be read; not treated as present), and a second send of the same handoff_id. The audit record (audit.jsonl) carries allowed_actions and authorized_by (verified: true, verified_at, labeled_by, labeled_at) for every attempt. A refusal before sending has no outcome, only error.kind (invalid_input, not_confirmed, authorization_missing, gh_error, agent_busy, agent_blocked, worker_unavailable, author_merge, agent_not_found, agent_changed, agent_restarted, agent_state_unknown, duplicate, probably_received, audit_corrupt, audit_failed, busy, timeout, command_failed): nothing was sent; report it verbatim and stop (inside the coordinator role, every "stop" in this step ends that item, not the session). For agent_busy, check with herdr:inventory until the agent is idle, then send. Otherwise read outcome:
    • delivered → continue with step 5.
    • unknown_delivery or agent_prompt_stalled → the text may already sit in the agent's input box. Run herdr:inspect, report what you see, stop. Only the owner can ask for resend, and the task refuses one when the agent's state advanced in between (probably_received).
    • agent_blocked, agent_not_found → nothing was delivered; report and stop.
  5. Await with herdr:await, passing the message's audit id so machine, agent, session, since_seq and the delivery outcome come from the record (it refuses an undelivered attempt with not_delivered): devenv tasks run herdr:await --input-json '{"audit_id":"<audit_id>","timeout_s":300}' The record separates herdr_state, turn_observed, reached, worker_done, needs_owner; accepted is always false here. On no_turn_observed, agent_restarted, agent_changed or agent_not_found, inspect the pane and report; do not resend.
  6. Verify with work:status: only work_verdict: verified with its evidence_ref closes the loop. Report that, or report what is missing.

GitHub writes (gh:label, gh:comment, gh:issue)

The coordinator's only other acts. Each is bounded to one issue or PR and appends two phase: gh lines to the same audit.jsonl (stage: attempt before the write, stage: outcome after), naming the handoff_id it serves or a reason: devenv tasks run gh:label --input-json '{"repo":"OWNER/REPO","number":N,"label":"blocked","action":"add","handoff_id":"<id>"}' (labels: needs-decision, blocked, needs-live, never a label that authorizes a send; actions: add, remove) and devenv tasks run gh:comment --input-json '{"repo":"OWNER/REPO","number":N,"body":"...","reason":"..."}' (body up to 4000 characters). The task ends every comment with the one signature -- Coordinator (automated; not the owner), replacing any -- Coordinator line the body already had. A comment reports, quotes or asks; it never answers, decides, approves or promises in the owner's name. devenv tasks run gh:issue --input-json '{"repo":"OWNER/REPO","title":"...","body":"...","labels":["enhancement"],"reason":"..."}' creates one issue in the owner's repositories (title one line, up to 256 characters; body up to 8000, signed the same way; labels only enhancement, needs-decision, blocked, needs-live, never one that authorizes a send). It refuses when an open issue already has that title, so a retry never opens a second one. Approval stays gh:label's, under the owner's delegation. They run only after the owner confirmed, in this conversation, the exact label or text and its issue (inside the coordinator role, its Autonomy rules instead). A gh failure is recorded in the audit line and returned as error.kind (command_failed, timeout, malformed); report it and do not retry blindly.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.