I wired three AI agents to coordinate in Slack — then fixed the bug that broke it.
Getting multiple agents to talk in one channel sounds simple until you do it. I hit the classic problems — wrong identities, mis-authenticated tokens, over-broad ingress, and a threading bug that buried every reply. I ran the diagnosis down to the exact function, patched it over SSH from another machine with a test and a commit, and verified with live messages.
A fleet of agents is useless if they can't coordinate.
Three agents. One channel. Distinct identities.
I needed the orchestrator, the worker, and the Cowork-session agent to talk to each other as separate identities — so one could delegate QC and another could coordinate work.
But running multiple agents on shared hosts created tangled identities and routing: agents identifying as the wrong entity, tokens authenticating as the wrong bot, over-broad channel ingress.
The worst was a threading bug: replying to a top-level channel message also spawned a Slack thread, so the other agents couldn’t follow the conversation. Coordination broke at the exact layer it needed to work.
Fix identity first, then the threading bug — externally.
Two passes. First identity and routing; then the bug that actually broke conversation.
Pass one — identity & routing: I tightened Slack ingress from an over-broad open policy to an explicit allowlist requiring @mentions per channel, ran down a token authenticating as the wrong agent, and stood up a clean dedicated Slack app after the old one was deactivated.
Pass two — the threading bug: rather than let the worker patch its own running code, I had the orchestrator diagnose root cause on the live code, patch the worker externally over SSH from another machine, add a targeted test, restart the gateway service, and verify with real messages.
You don't do runtime surgery from the patient's own chair. I patched the worker from another machine — backup, patch, test, restart, verify — not by letting it edit itself mid-run.
— Operating principle · external SSH patchHow a session key became a Slack thread.
A top-level message was keyed by its own timestamp as a synthetic thread_id — a session key. On send, that value was passed to Slack as thread_ts, so Slack correctly created a thread. The resolver only suppressed threading when it received both the synthetic id and the original reply-to id — but the send path sent metadata only, so it defaulted to threading.
A top-level message was keyed by its own timestamp as a synthetic thread_id (a session key). On send, that value reached Slack as thread_ts, so Slack created a thread. _resolve_thread_ts() only suppressed threading when it saw both the synthetic id and the original reply-to id — but the send path sent metadata only. The fix put the original message id in the metadata and updated the resolver to set thread_ts to None for synthetic top-level sends, while still preserving real thread replies.
Agents & Identities
Slack Config
Bug Surface
Repair Method
Slack API Internals
Verification
A precise, bounded fix.
Descriptive facts from the internal record. This is qualitative infrastructure debugging — there are no percentage-improvement metrics, and none are invented.
Reproduction vs. post-patch.
The fix was verified by reproduction, then by live human-origin messages in the channel.
Top-level send → Slack threads it.
input: top-level, metadata only
resolver: synthetic id present,
anchor missing → default
sent as: thread_ts = <synthetic>
result: Slack creates thread ✗Top-level send with anchor → no thread.
input: top-level, metadata + anchor
resolver: synthetic id + anchor
present → suppress
sent as: thread_ts = None
result: top-level reply, no thread ✓The outcome is binary, not a growth chart.
Threading stopped and three-way coordination was confirmed by live tests. This is qualitative infrastructure debugging — precise root cause, disciplined external patch, verified fix. There are no percentage-improvement metrics here, and none should be inferred.
Diagnose, patch externally, verify.
The orchestrator inspected the worker's running code and pinned the defect to _resolve_thread_ts() — a synthetic thread_id being passed to Slack as thread_ts.
Instead of letting the worker edit its own running code, the fix was applied over SSH from a separate node — backup touched files first.
A new test covered the exact behavior — synthetic top-level send stays top-level. Result: 1 passed, 54 deselected.
The gateway service restarted cleanly and re-authenticated to Slack as the worker bot.
Real messages in the shared channel confirmed the worker replying top-level with no thread — three-way coordination working end to end.
I set the goal and directed the fix.
Three agents coordinating as distinct identities in Slack — I made the operational-safety call to patch externally, not in place.
I directed both the identity/routing repairs and the threading-bug fix, and I ran the verification with live messages. The diagnosis chain — the worker self-inspecting, Claude confirming, the orchestrator verifying on live code and applying the patch — was a coordination I orchestrated and approved. The value here is the debugging competence: precise root cause, disciplined external patch, verified fix, on a genuinely hard multi-agent problem.