|
| 1 | +# A2A Workarounds — Cross-Agent Coordination Without Native Support |
| 2 | + |
| 3 | +> **READ BY:** all agents, all sessions. |
| 4 | +> **Status:** FINDING (2026-04-24). Tested in-session with 6+ concurrent agents. |
| 5 | +> **Context:** Claude Code agents are isolated processes. No shared memory, |
| 6 | +> no MCP channel between them, no role-switching within a session. |
| 7 | +> These workarounds restore coordination using existing primitives. |
| 8 | +
|
| 9 | +--- |
| 10 | + |
| 11 | +## The Problem |
| 12 | + |
| 13 | +Claude Code's `Agent()` tool spawns isolated subprocesses. Each agent: |
| 14 | +- Gets a fresh context window (no memory of the conversation) |
| 15 | +- Cannot call other agents' tools |
| 16 | +- Cannot read other agents' in-flight state |
| 17 | +- Returns a single result blob to the main thread |
| 18 | + |
| 19 | +This breaks three patterns that worked in earlier Claude/Gemini setups: |
| 20 | +1. **Role teleportation** — switching persona in-context with zero loss |
| 21 | +2. **Mid-flight coordination** — agent A tells agent B what it found |
| 22 | +3. **Cross-session handoff** — session A's work feeds session B in real-time |
| 23 | + |
| 24 | +--- |
| 25 | + |
| 26 | +## Workaround 1: File Blackboard (`AGENT_LOG.md`) |
| 27 | + |
| 28 | +**Replaces:** Mid-flight coordination (partially). |
| 29 | +**How:** Append-only log file that all agents read before starting |
| 30 | +and write to after committing. |
| 31 | + |
| 32 | +### Setup |
| 33 | + |
| 34 | +Already live at `.claude/board/AGENT_LOG.md`. Permission pre-allowed |
| 35 | +in `.claude/settings.json`: |
| 36 | + |
| 37 | +```json |
| 38 | +"Bash(cat >> .claude/board/AGENT_LOG.md:*)" |
| 39 | +``` |
| 40 | + |
| 41 | +### Agent prompt template (include in every spawn) |
| 42 | + |
| 43 | +``` |
| 44 | +Before starting work, read `.claude/board/AGENT_LOG.md` to see what |
| 45 | +other agents already shipped or found. |
| 46 | +
|
| 47 | +After committing, append your entry: |
| 48 | +
|
| 49 | +cat >> .claude/board/AGENT_LOG.md <<'EOF' |
| 50 | +
|
| 51 | +## YYYY-MM-DDTHH:MM — description (model, branch) |
| 52 | +
|
| 53 | +**D-ids:** ... |
| 54 | +**Commit:** `abc1234` |
| 55 | +**Tests:** N pass (M new) |
| 56 | +**Outcome:** One-line summary. |
| 57 | +EOF |
| 58 | +``` |
| 59 | + |
| 60 | +### Limitations |
| 61 | + |
| 62 | +- Not real-time: agent B only sees what agent A committed, not |
| 63 | + what A is currently working on. |
| 64 | +- Git staging: if agent A and B both append without committing, |
| 65 | + only the last `git add` wins. Mitigation: commit immediately |
| 66 | + after append. |
| 67 | +- Ordering: entries are appended at bottom (cat >>), but convention |
| 68 | + is newest-first. Main thread can reorder during board-hygiene. |
| 69 | + |
| 70 | +--- |
| 71 | + |
| 72 | +## Workaround 2: Branch Pub/Sub (`subscribe_pr_activity`) |
| 73 | + |
| 74 | +**Replaces:** Cross-session handoff. |
| 75 | +**How:** Open a coordination PR. Both sessions subscribe. Push events |
| 76 | +arrive as `<github-webhook-activity>` tags. |
| 77 | + |
| 78 | +### Setup |
| 79 | + |
| 80 | +```bash |
| 81 | +# Session A (creates the bus): |
| 82 | +git checkout -b claude/blackboard |
| 83 | +echo "# Coordination Blackboard" > .claude/board/AGENT_LOG.md |
| 84 | +git add .claude/board/AGENT_LOG.md |
| 85 | +git commit -m "init coordination blackboard" |
| 86 | +git push -u origin claude/blackboard |
| 87 | +# Open PR: |
| 88 | +mcp__github__create_pull_request( |
| 89 | + owner="AdaWorldAPI", repo="lance-graph", |
| 90 | + title="A2A coordination blackboard", |
| 91 | + head="claude/blackboard", base="main", |
| 92 | + body="Cross-session pub/sub bus. Do not merge.", |
| 93 | + draft=true |
| 94 | +) |
| 95 | +# Subscribe: |
| 96 | +mcp__github__subscribe_pr_activity(owner="AdaWorldAPI", repo="lance-graph", pullNumber=NNN) |
| 97 | + |
| 98 | +# Session B (joins): |
| 99 | +mcp__github__subscribe_pr_activity(owner="AdaWorldAPI", repo="lance-graph", pullNumber=NNN) |
| 100 | +git fetch origin claude/blackboard |
| 101 | +git checkout claude/blackboard |
| 102 | +# Read AGENT_LOG.md → see what session A did |
| 103 | +``` |
| 104 | + |
| 105 | +### Coordination loop |
| 106 | + |
| 107 | +``` |
| 108 | +Session A: Session B: |
| 109 | + [does work] |
| 110 | + cat >> AGENT_LOG.md <<'EOF' |
| 111 | + ...entry... |
| 112 | + EOF |
| 113 | + git add && git commit && git push |
| 114 | + ← <github-webhook-activity> push event |
| 115 | + git pull origin claude/blackboard |
| 116 | + cat AGENT_LOG.md # read A's entry |
| 117 | + [builds on A's findings] |
| 118 | + cat >> AGENT_LOG.md <<'EOF' |
| 119 | + ...entry... |
| 120 | + EOF |
| 121 | + git add && git commit && git push |
| 122 | + ← <github-webhook-activity> push event |
| 123 | + git pull |
| 124 | + # reads B's entry, continues |
| 125 | +``` |
| 126 | + |
| 127 | +### Why it works |
| 128 | + |
| 129 | +- `subscribe_pr_activity` is already in the MCP toolkit — zero infra. |
| 130 | +- GitHub webhooks fire on any push, regardless of content. |
| 131 | +- Append-only files merge cleanly (no conflict on concurrent appends |
| 132 | + if entries are at different positions). |
| 133 | +- The draft PR never merges — it's the bus, not a deliverable. |
| 134 | + |
| 135 | +### Limitations |
| 136 | + |
| 137 | +- GitHub webhook latency: seconds to low minutes. |
| 138 | +- Rate limits: GitHub API limits apply (5000/hour authenticated). |
| 139 | +- Requires network: doesn't work offline. |
| 140 | +- PR must stay open: closing it kills the subscription. |
| 141 | + |
| 142 | +--- |
| 143 | + |
| 144 | +## Workaround 3: Role Teleportation via Agent Cards |
| 145 | + |
| 146 | +**Replaces:** In-context role switching. |
| 147 | +**How:** Load an agent card's knowledge docs, adopt its perspective, |
| 148 | +do the work — all on the main thread. No subprocess spawned. |
| 149 | + |
| 150 | +### When to use |
| 151 | + |
| 152 | +- The task requires seeing the FULL conversation context (not a summary). |
| 153 | +- The task is accumulation (multi-source synthesis), not grindwork. |
| 154 | +- The role switch is temporary (do 10 minutes of codec work, then |
| 155 | + switch back to architecture). |
| 156 | + |
| 157 | +### How |
| 158 | + |
| 159 | +``` |
| 160 | +# On the main thread, not via Agent(): |
| 161 | +1. Read `.claude/agents/family-codec-smith.md` |
| 162 | +2. Load its Tier-1 knowledge docs (encoding-ecosystem.md, etc.) |
| 163 | +3. Do the codec work with full session context intact |
| 164 | +4. When done, switch: read `.claude/agents/truth-architect.md` |
| 165 | +5. Review the codec work from the architect's perspective |
| 166 | +6. Back to main thread — nothing lost |
| 167 | +``` |
| 168 | + |
| 169 | +### When NOT to use |
| 170 | + |
| 171 | +- The task is mechanical grindwork (file scaffolding, known-spec |
| 172 | + implementation) → spawn a Sonnet agent instead. |
| 173 | +- The task is truly independent (no context dependency) → parallel |
| 174 | + Agent() spawns are faster. |
| 175 | +- The task is long-running and would block the main thread → |
| 176 | + background Agent() is better. |
| 177 | + |
| 178 | +### Limitations |
| 179 | + |
| 180 | +- Main thread is single-threaded: no parallelism. |
| 181 | +- Context window fills: role-switching adds knowledge doc content |
| 182 | + to the conversation, consuming context budget. |
| 183 | +- No isolation: mistakes made "as codec-smith" are visible to the |
| 184 | + truth-architect review (which is actually a feature, not a bug). |
| 185 | + |
| 186 | +--- |
| 187 | + |
| 188 | +## Workaround 4: Structured Handover Files |
| 189 | + |
| 190 | +**Replaces:** Session-to-session context transfer. |
| 191 | +**How:** Write a structured handover file that the next session |
| 192 | +reads at startup via the SessionStart hook. |
| 193 | + |
| 194 | +### Format |
| 195 | + |
| 196 | +```markdown |
| 197 | +# Handover — YYYY-MM-DD-HHMM — <from-session> to <next-session> |
| 198 | + |
| 199 | +## What I did |
| 200 | +- [bullet list of completed work with commit hashes] |
| 201 | + |
| 202 | +## FINDING |
| 203 | +- [verified facts that the next session can rely on] |
| 204 | + |
| 205 | +## CONJECTURE |
| 206 | +- [unverified ideas that need probing] |
| 207 | + |
| 208 | +## Blockers |
| 209 | +- [things I couldn't resolve] |
| 210 | + |
| 211 | +## Open questions |
| 212 | +- [decisions the next session should make] |
| 213 | +``` |
| 214 | + |
| 215 | +### Where |
| 216 | + |
| 217 | +`.claude/handovers/YYYY-MM-DD-HHMM-<topic>.md` |
| 218 | + |
| 219 | +The SessionStart hook (`.claude/hooks/session-start.sh`) can be |
| 220 | +extended to cat the latest handover file into the session context. |
| 221 | + |
| 222 | +--- |
| 223 | + |
| 224 | +## Decision Matrix |
| 225 | + |
| 226 | +| Need | Workaround | Cost | |
| 227 | +|---|---|---| |
| 228 | +| Agent A's findings feed agent B (same session) | File Blackboard (#1) | Low: cat >> + git add | |
| 229 | +| Session A's work feeds session B (real-time) | Branch Pub/Sub (#2) | Medium: PR + subscribe | |
| 230 | +| Full-context role switch (no loss) | Teleportation (#3) | Zero: just read the card | |
| 231 | +| Session-to-session knowledge transfer | Handover Files (#4) | Low: write once, read at startup | |
| 232 | +| Parallel independent grindwork | Standard Agent() spawns | Low: fire and forget | |
| 233 | +| Multi-source synthesis needing judgment | Teleportation (#3) on Opus main thread | Zero | |
| 234 | + |
| 235 | +--- |
| 236 | + |
| 237 | +## Relation to Runtime A2A (Layer 1) |
| 238 | + |
| 239 | +These workarounds mirror the runtime `Blackboard` from |
| 240 | +`lance_graph_contract::a2a_blackboard`: |
| 241 | + |
| 242 | +| Runtime (Layer 1) | Session (Layer 2 workaround) | |
| 243 | +|---|---| |
| 244 | +| `Blackboard.entries` | `AGENT_LOG.md` entries | |
| 245 | +| `BlackboardEntry.expert_id` | Agent description + model | |
| 246 | +| `BlackboardEntry.capability` | D-ids | |
| 247 | +| `BlackboardEntry.result` | Commit hash + outcome | |
| 248 | +| `BlackboardEntry.confidence` | Test pass count | |
| 249 | +| `Blackboard.round` | Git commit sequence | |
| 250 | +| Experts read prior rounds | Agents read prior log entries | |
| 251 | + |
| 252 | +The structural isomorphism is intentional: the same coordination |
| 253 | +pattern works at both layers because the problem is the same — |
| 254 | +independent experts composing results on a shared substrate. |
| 255 | + |
| 256 | +--- |
| 257 | + |
| 258 | +## Future: Native A2A MCP Server |
| 259 | + |
| 260 | +When Claude Code or a third party ships an A2A MCP server with |
| 261 | +`post_entry` / `read_entries` / `subscribe` endpoints, these |
| 262 | +workarounds can be replaced. The contract types already exist |
| 263 | +(`BlackboardEntry`, `ExpertCapability`, `Blackboard`). The MCP |
| 264 | +server is a thin serde layer over them. |
| 265 | + |
| 266 | +Until then: `cat >> AGENT_LOG.md <<'EOF'`. |
0 commit comments