From b378819089fc308ddb81a62a0ba2abe022aa3131 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 11:11:34 +0000 Subject: [PATCH] fix(claude-relay-plugin): address review on the workspace-key removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seven findings, all confirmed: - **Foreground pipeline deadlock (P1).** "Do NOT release yourself — stay idle" is right for backgrounded team/fanout workers but wrong for pipeline, whose stages run in the foreground: a worker that stays idle never returns, so the blocking Agent call never completes and the lead can neither read the DONE nor spawn the next stage. Pipeline workers now end their turn after DONE. The distinction between ending a turn and releasing a relay identity (`remove_agent`) is now stated explicitly in all three skills, since conflating them caused this. - **Concurrent runs share the project pin (P1).** The pin is last-writer-wins, so two leads in one checkout can cross workers into each other's workspace. Documented the one-team-per-checkout constraint and pointed at the ACK gate that already detects it — a worker in the wrong workspace finds no assignment and cannot ACK. - **Registration failure had no usable channel.** The old text said to report it "to your lead", but `send_dm` needs the registration that just failed. The worker now stops without retrying and makes the error its final response, which is what the lead gets back from the Agent call. Same for an empty inbox, the symptom of the pin race above. - **Plugin version was not bumped.** Marketplace clients use it to detect updates, so existing installs would have stayed on the prompts that leak workspace keys. Bumped to 0.2.0 in `plugin.json`, `package.json`, and the marketplace entry. - **`get_observer_url` may not be present yet** — it ships in AgentWorkforce/relay#1422. The instruction now falls back to `agent-relay observer` so a lead is never stuck on a missing tool. - **Later pipeline stages had no handoff requirement**, unlike stage 1; the final stage now also owes evidence. - **MD040**: gave the spawn examples a `text` language. Also drops the now-stale "if any of steps 1-2 fail, retry once" from the worker definition — step 2 became a standing prohibition, not a fallible action. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Jmke9G9s7ftrN49opNmdx1 --- .claude-plugin/marketplace.json | 2 +- .../.claude-plugin/plugin.json | 2 +- .../agents/relay-worker/agent.md | 25 ++++++++++++++----- .../hooks/subagent-bootstrap.sh | 11 ++++++-- plugins/claude-relay-plugin/package.json | 2 +- .../skills/relay-fanout/SKILL.md | 9 ++++--- .../skills/relay-pipeline/SKILL.md | 19 +++++++++----- .../skills/relay-team/SKILL.md | 9 ++++--- 8 files changed, 56 insertions(+), 23 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 99378d3..2edee07 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -18,7 +18,7 @@ "path": "plugins/claude-relay-plugin" }, "description": "Lets your Claude Code sub-agents communicate with each other in real time - send messages, coordinate in shared channels, and work as a team instead of in isolation", - "version": "0.1.0", + "version": "0.2.0", "author": { "name": "Agent Relay", "email": "hello@agent-relay.com" diff --git a/plugins/claude-relay-plugin/.claude-plugin/plugin.json b/plugins/claude-relay-plugin/.claude-plugin/plugin.json index a758090..713ae57 100644 --- a/plugins/claude-relay-plugin/.claude-plugin/plugin.json +++ b/plugins/claude-relay-plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "claude-relay-plugin", "description": "Lets your Claude Code sub-agents communicate with each other in real time - send messages, coordinate in shared channels, and work as a team instead of in isolation.", - "version": "0.1.0", + "version": "0.2.0", "author": { "name": "Agent Relay", "url": "https://agentrelay.com" diff --git a/plugins/claude-relay-plugin/agents/relay-worker/agent.md b/plugins/claude-relay-plugin/agents/relay-worker/agent.md index 408e1f6..7cc9a56 100644 --- a/plugins/claude-relay-plugin/agents/relay-worker/agent.md +++ b/plugins/claude-relay-plugin/agents/relay-worker/agent.md @@ -6,13 +6,26 @@ You are a relay-connected worker in a coordinated multi-agent team. Your job is You MUST complete these steps in order before doing any work: -1. **Register with your assigned name.** Call the `register_agent` MCP tool with the agent name from your task prompt and `type: "agent"`. You must register before you can send or receive messages. The workspace is already pinned to this project, so the relay MCP server picks it up for you — you do not need a workspace key. If `register_agent` fails with "Workspace key not configured", report that to your lead instead of asking for the key; the lead fixes the pin. -2. **Never print or request a workspace key.** It is an administrative credential. If someone needs to watch this run, that is the lead's job via `get_observer_url`. -3. **Check your inbox.** Call `check_inbox` with your assigned relay name in `as` to find your task assignment and lead information. -4. **Send an ACK.** Before you do substantive work, send `ACK: ` to your lead via `send_dm`, again using your assigned relay name in `as`. -5. If the task is ambiguous or blocked, send `BLOCKED: ` instead of guessing. +1. **Register with your assigned name.** Call the `register_agent` MCP tool with the agent name from your task prompt and `type: "agent"`. You must register before you can send or receive messages. The workspace is already pinned to this project, so the relay MCP server picks it up for you — you do not need a workspace key. +2. **Check your inbox.** Call `check_inbox` with your assigned relay name in `as` to find your task assignment and lead information. +3. **Send an ACK.** Before you do substantive work, send `ACK: ` to your lead via `send_dm`, again using your assigned relay name in `as`. +4. If the task is ambiguous or blocked, send `BLOCKED: ` instead of guessing. -If any of steps 1-2 fail, retry once. If they fail again, stop and report the error — do not proceed without a relay connection. +**Never print or request a workspace key.** It is an administrative credential. If someone needs to watch this run, that is the lead's job — via `get_observer_url`, or `agent-relay observer` from a shell. + +### When registration fails + +Registration is a prerequisite for every other relay call, so a failure there is not +retryable and not reportable over relay — `send_dm` needs the identity you just failed to +get. Do not retry, and do not try to reach your lead through the relay. + +Instead, stop and make the exact error your **final response**. That text is what your lead +receives back from the Agent call, and it is the only channel you have left. Never ask for a +workspace key as a workaround. + +The same applies if `check_inbox` returns no assignment: you may have registered into a +different workspace than your lead, because the project pin is shared by everything running +in this directory. Report that as your final response rather than guessing at the work. ## Working Rules diff --git a/plugins/claude-relay-plugin/hooks/subagent-bootstrap.sh b/plugins/claude-relay-plugin/hooks/subagent-bootstrap.sh index 29bfd90..da2326e 100755 --- a/plugins/claude-relay-plugin/hooks/subagent-bootstrap.sh +++ b/plugins/claude-relay-plugin/hooks/subagent-bootstrap.sh @@ -10,12 +10,19 @@ MANDATORY relay setup — complete these steps IN ORDER before any other work: 1. Call \`register_agent(name: "$AGENT_NAME", type: "agent")\` to register with the relay. The workspace is already pinned to this project, so the relay MCP server resolves it for you — you do NOT need a workspace key, and must never print or ask for one. - If this fails with "Workspace key not configured", report that to your lead. 2. Call \`check_inbox(as: "$AGENT_NAME")\` to get your task assignment. 3. Send an ACK to your lead via \`send_dm(as: "$AGENT_NAME")\` when you understand the task. 4. When finished, send a DONE message with a concise completion summary via \`send_dm(as: "$AGENT_NAME")\` before stopping. IMPORTANT: Include \`as: "$AGENT_NAME"\` on EVERY relay tool call to ensure correct message attribution. -Do NOT skip step 1. Without it you cannot send or receive messages. +IF STEP 1 FAILS (for example "Workspace key not configured"): stop. Do not retry, and do +not attempt any other relay call — every one of them needs the registration you just failed +to get, so they will fail too. Relay is unavailable to you, which means you cannot tell your +lead over relay. Report the exact error as your final response instead; that text is what +your lead receives back from the Agent call. Do not ask anyone for a workspace key. + +IF STEP 2 RETURNS NO ASSIGNMENT: you may have registered into a different workspace than +your lead (the project pin is shared by everything running in this directory). Do not guess +at the work. Report that as your final response so the lead can re-spawn you. EOF diff --git a/plugins/claude-relay-plugin/package.json b/plugins/claude-relay-plugin/package.json index d3f3a86..4b40897 100644 --- a/plugins/claude-relay-plugin/package.json +++ b/plugins/claude-relay-plugin/package.json @@ -1,6 +1,6 @@ { "name": "claude-relay-plugin", - "version": "0.1.0", + "version": "0.2.0", "description": "Agent Relay plugin for Claude Code - multi-agent coordination via Agent Relay MCP and hooks", "author": "Agent Relay ", "license": "MIT", diff --git a/plugins/claude-relay-plugin/skills/relay-fanout/SKILL.md b/plugins/claude-relay-plugin/skills/relay-fanout/SKILL.md index b26471b..acec64a 100644 --- a/plugins/claude-relay-plugin/skills/relay-fanout/SKILL.md +++ b/plugins/claude-relay-plugin/skills/relay-fanout/SKILL.md @@ -16,6 +16,7 @@ Workers are spawned with Claude Code's built-in **Agent tool**. The relay is onl - Use `subagent_type: "relay-worker"`. Only `relay-worker` subagents get the Agent Relay MCP server, the inbox-polling hooks, and the worker protocol. Other subagent types (`researcher`, `general-purpose`, …) cannot talk over the relay. - Run all workers in **background mode** (`run_in_background: true`) so they work concurrently. - Workers inherit the workspace automatically — the relay MCP server resolves the workspace pinned to this project. **Do not put the workspace key in a worker prompt.** It is an administrative credential, and copying it into N prompts puts it in N transcripts. If a worker reports no workspace, fix the pin (step 2) rather than pasting the key. +- **One relay team per checkout.** The pin is per-project and last-writer-wins, so two leads running teams from the same directory will fight over it and a worker can register into the other lead's workspace. Run concurrent teams from separate checkouts or git worktrees. The ACK gate below is what catches this: a worker that landed in the wrong workspace finds no assignment and cannot ACK. - The `SubagentStart` hook injects the relay bootstrap (register, check inbox, ACK, DONE) into every worker. - Use the relay MCP tools (`send_dm`, `check_inbox`) to monitor progress. - Do not add setup scripts or dependencies. Use the plugin's existing hooks, MCP tools, and `relay-worker` agent definition. @@ -24,12 +25,12 @@ Workers are spawned with Claude Code's built-in **Agent tool**. The relay is onl 1. Pick a stable coordinator name — `relay-lead`. Pass `as: "relay-lead"` on **every** relay tool call you make, so your messages, inbox reads, and reactions stay attributed to the lead. 2. **Set up the workspace.** Call `register_agent` with `relay-lead`. If it fails with "Workspace key not configured", call `create_workspace`, then `register_agent` again. Both `create_workspace` and `set_workspace_key` pin the workspace to this project, which is how workers pick it up. -3. **Give the user a link to follow along.** Call `get_observer_url` and print the URL it returns. It is backed by a read-only token that expires, so it is safe to share. Never build an observer URL from the workspace key, and never print the key. +3. **Give the user a link to follow along.** Call `get_observer_url` and print the URL it returns — or run `agent-relay observer` if your session does not expose that tool. Either way the link is backed by a read-only token that expires, so it is safe to share. Never build an observer URL from the workspace key, and never print the key. 4. Confirm the work is genuinely parallelizable. Every worker must be able to finish without waiting on another worker's output. If that is not true, use the pipeline pattern instead. 5. Pick the worker count from the task shape. Prefer 2–8, and stay low enough that you can still track every ACK and DONE. 6. Partition the work into independent units — each with its own files, target, or scope boundary, and no shared intermediate state. 7. Spawn one worker per unit with the Agent tool: - ``` + ```text Agent( subagent_type: "relay-worker", run_in_background: true, @@ -39,7 +40,9 @@ Workers are spawned with Claude Code's built-in **Agent tool**. The relay is onl Your unit: [specific target/scope]. Files: [list of files/directories]. Deliver: [concrete output]. - Do NOT release yourself when done — report DONE and stay idle for review." + When done, DM your lead a DONE message with the evidence for your scope. + Do not call remove_agent on yourself — the lead releases you once the work + is accepted, so it can send you review findings to fix." ) ``` 8. Wait for an ACK from every worker with `check_inbox(as: "relay-lead")`. A missing ACK means that worker is not working — re-DM it. diff --git a/plugins/claude-relay-plugin/skills/relay-pipeline/SKILL.md b/plugins/claude-relay-plugin/skills/relay-pipeline/SKILL.md index 85df127..c80ada2 100644 --- a/plugins/claude-relay-plugin/skills/relay-pipeline/SKILL.md +++ b/plugins/claude-relay-plugin/skills/relay-pipeline/SKILL.md @@ -16,6 +16,7 @@ Workers are spawned with Claude Code's built-in **Agent tool**. The relay is onl - Use `subagent_type: "relay-worker"`. Only `relay-worker` subagents get the Agent Relay MCP server, the inbox-polling hooks, and the worker protocol. Other subagent types (`researcher`, `general-purpose`, …) cannot talk over the relay. - Run pipeline stages in **foreground mode** (the default) so each stage finishes before the next starts. - Workers inherit the workspace automatically — the relay MCP server resolves the workspace pinned to this project. **Do not put the workspace key in a worker prompt.** It is an administrative credential, and copying it into N prompts puts it in N transcripts. If a worker reports no workspace, fix the pin (step 2) rather than pasting the key. +- **One relay team per checkout.** The pin is per-project and last-writer-wins, so two leads running teams from the same directory will fight over it and a worker can register into the other lead's workspace. Run concurrent teams from separate checkouts or git worktrees. The ACK gate below is what catches this: a worker that landed in the wrong workspace finds no assignment and cannot ACK. - The `SubagentStart` hook injects the relay bootstrap (register, check inbox, ACK, DONE) into every worker. - Use the relay MCP tools (`send_dm`, `check_inbox`) to receive each stage's handoff. - Do not add setup scripts or dependencies. Use the plugin's existing hooks, MCP tools, and `relay-worker` agent definition. @@ -24,11 +25,11 @@ Workers are spawned with Claude Code's built-in **Agent tool**. The relay is onl 1. Pick a stable coordinator name — `relay-lead`. Pass `as: "relay-lead"` on **every** relay tool call you make, so your messages, inbox reads, and reactions stay attributed to the lead. 2. **Set up the workspace.** Call `register_agent` with `relay-lead`. If it fails with "Workspace key not configured", call `create_workspace`, then `register_agent` again. Both `create_workspace` and `set_workspace_key` pin the workspace to this project, which is how workers pick it up. -3. **Give the user a link to follow along.** Call `get_observer_url` and print the URL it returns. It is backed by a read-only token that expires, so it is safe to share. Never build an observer URL from the workspace key, and never print the key. +3. **Give the user a link to follow along.** Call `get_observer_url` and print the URL it returns — or run `agent-relay observer` if your session does not expose that tool. Either way the link is backed by a read-only token that expires, so it is safe to share. Never build an observer URL from the workspace key, and never print the key. 4. Break the task into ordered stages. Every stage needs a concrete handoff artifact for the next one: a summary, a decision, a file path, a diff, or a verified output. 5. Keep the stage count low and explicit — prefer 2–5 with distinct responsibilities. 6. Start stage 1. Spawn its worker with the Agent tool in foreground mode: - ``` + ```text Agent( subagent_type: "relay-worker", prompt: "You are relay-stage-1. Your lead is relay-lead. @@ -36,13 +37,14 @@ Workers are spawned with Claude Code's built-in **Agent tool**. The relay is onl can be attributed to another agent. Your task: [stage 1 scope]. Files: [relevant files]. - When done, DM your lead a DONE message containing: [handoff artifact description]. - Do NOT release yourself when done — stay idle in case the stage needs a fix." + When done, DM your lead a DONE message containing: [handoff artifact description], + then end your turn so the lead can continue. + Do not call remove_agent on yourself — the lead releases you." ) ``` 7. Wait for stage 1's DONE with `check_inbox(as: "relay-lead")`. Never start downstream work on an assumption about what the stage produced. 8. For each later stage, spawn a worker carrying the original task context, the upstream DONE summary and handoff artifact, and any files, decisions, or constraints the earlier stages produced: - ``` + ```text Agent( subagent_type: "relay-worker", prompt: "You are relay-stage-2. Your lead is relay-lead. @@ -50,7 +52,10 @@ Workers are spawned with Claude Code's built-in **Agent tool**. The relay is onl Previous stage completed: [DONE summary from stage 1]. Your task: [stage 2 scope, using stage 1's output]. Files: [relevant files]. - Do NOT release yourself when done — stay idle in case the stage needs a fix." + When done, DM your lead a DONE message containing: [this stage's handoff artifact — + or, if this is the final stage, the deliverable plus the evidence that proves it works], + then end your turn so the lead can continue. + Do not call remove_agent on yourself — the lead releases you." ) ``` 9. Keep a live stage table in your notes: stage, scope, ACK, blocked, DONE, handoff artifact. @@ -60,6 +65,8 @@ Workers are spawned with Claude Code's built-in **Agent tool**. The relay is onl ## Rules - Use a pipeline only for genuine dependencies. If the stages can run independently, switch to fan-out. +- Stages run in the foreground, so a stage worker **must end its turn** after sending DONE. Telling it to stay idle deadlocks the run: the blocking Agent call never returns, so the lead can never read the DONE or spawn the next stage. (Team and fan-out workers are backgrounded and do stay idle — that instruction belongs there, not here.) +- Releasing a relay identity (`remove_agent`) is separate from ending a turn. Workers never do the former; the lead does it once the whole pipeline is accepted. - Handoffs must be explicit. A downstream worker should never have to guess what mattered upstream. - If a stage fails or is blocked, stop the pipeline, resolve the blocker, and resume from that stage. - Workers cannot spawn their own subagents — only the lead spawns. diff --git a/plugins/claude-relay-plugin/skills/relay-team/SKILL.md b/plugins/claude-relay-plugin/skills/relay-team/SKILL.md index a3d86ce..8c0143c 100644 --- a/plugins/claude-relay-plugin/skills/relay-team/SKILL.md +++ b/plugins/claude-relay-plugin/skills/relay-team/SKILL.md @@ -16,6 +16,7 @@ Workers are spawned with Claude Code's built-in **Agent tool**. The relay is onl - Use `subagent_type: "relay-worker"`. Only `relay-worker` subagents get the Agent Relay MCP server, the inbox-polling hooks, and the worker protocol. Other subagent types (`researcher`, `general-purpose`, …) cannot talk over the relay. - Run workers in **background mode** (`run_in_background: true`) so they work concurrently. - Workers inherit the workspace automatically — the relay MCP server resolves the workspace pinned to this project. **Do not put the workspace key in a worker prompt.** It is an administrative credential, and copying it into N prompts puts it in N transcripts. If a worker reports no workspace, fix the pin (step 2) rather than pasting the key. +- **One relay team per checkout.** The pin is per-project and last-writer-wins, so two leads running teams from the same directory will fight over it and a worker can register into the other lead's workspace. Run concurrent teams from separate checkouts or git worktrees. The ACK gate below is what catches this: a worker that landed in the wrong workspace finds no assignment and cannot ACK. - The `SubagentStart` hook injects the relay bootstrap (register, check inbox, ACK, DONE) into every worker. - Use the relay MCP tools (`send_dm`, `post_message`, `check_inbox`) to talk to workers once they are running. - Do not add setup scripts or dependencies. Use the plugin's existing hooks, MCP tools, and `relay-worker` agent definition. @@ -24,11 +25,11 @@ Workers are spawned with Claude Code's built-in **Agent tool**. The relay is onl 1. Pick a stable coordinator name — `relay-lead`. Pass `as: "relay-lead"` on **every** relay tool call you make, so your messages, inbox reads, and reactions stay attributed to the lead. 2. **Set up the workspace.** Call `register_agent` with `relay-lead`. If it fails with "Workspace key not configured", call `create_workspace`, then `register_agent` again. Both `create_workspace` and `set_workspace_key` pin the workspace to this project, which is how workers pick it up. -3. **Give the user a link to follow along.** Call `get_observer_url` and print the URL it returns. It is backed by a read-only token that expires, so it is safe to share. Never build an observer URL from the workspace key, and never print the key. +3. **Give the user a link to follow along.** Call `get_observer_url` and print the URL it returns — or run `agent-relay observer` if your session does not expose that tool. Either way the link is backed by a read-only token that expires, so it is safe to share. Never build an observer URL from the workspace key, and never print the key. 4. Read the task, inspect the relevant code, and decide whether parallel work is justified. Prefer 1 worker for tightly coupled work, 2–5 for genuinely separable work. 5. Break the task into non-overlapping scopes. Each worker needs a concrete deliverable, the relevant files, and an explicit success condition. 6. Spawn each worker with the Agent tool: - ``` + ```text Agent( subagent_type: "relay-worker", run_in_background: true, @@ -38,7 +39,9 @@ Workers are spawned with Claude Code's built-in **Agent tool**. The relay is onl Your task: [specific scope and deliverables]. Files: [list of files/directories]. Success condition: [what done looks like]. - Do NOT release yourself when done — report DONE and stay idle for review." + When done, DM your lead a DONE message with the evidence for your scope. + Do not call remove_agent on yourself — the lead releases you once the work + is accepted, so it can send you review findings to fix." ) ``` 7. After spawning, DM each worker any extra context it needs.