Agentic Workforce Company
Agent Infrastructure · Multi-Agent Debugging

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.

Fixed & verifiedROLE Operator & debuggerSCOPE 3 agent identitiesFIX June 22, 2026
CategoryAgent infrastructure
AgentsMC · Hermes · Claude
SurfaceSlack #general · gateway code
MethodExternal SSH patch · test · verify
StatusFixed · live-confirmed
The Problem

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.

The Approach

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 patch
Root Cause

How 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.

Fig.01 — The Threading Defect & the FixThe Slack threading bug and its fixTop lane, the bug path: a top-level message is keyed by its own timestamp as a synthetic thread_id; on send only that metadata is present, so the resolver function defaults to setting thread_ts, and Slack creates an unwanted thread. Bottom lane, the patched path: the original message id is now included in the send metadata as an anchor; the resolver sees both the synthetic id and the anchor, sets thread_ts to none for a synthetic top-level send, and the reply stays top-level with no thread while real thread replies are preserved.BUG PATH · BEFORETop-level messagekeyed by its own tssynthetic thread_id= session key (internal)DEFECT_resolve_thread_ts()metadata only → defaultsSlack createsa thread ✗THE FIXinclude original message id (anchor) in send metadata; resolver suppresses thread_ts for synthetic top-levelPATCHED PATH · AFTERTop-level + anchororiginal id in metadataRESOLVERsees synthetic + anchorsuppress branch takenthread_ts → Nonereal replies preservedTop-level replyno thread ✓

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

MC (orchestrator)Hermes (worker)Claude (Cowork)distinct user + bot IDs

Slack Config

groupPolicy: open → allowlistper-channel @mentionbot-authored blockedhome-channel routing

Bug Surface

Slack gateway adapter_resolve_thread_ts()thread_tsreply-in-thread

Repair Method

SSH (mini → mini)backup before patchpytestlaunchd restart

Slack API Internals

thread_ts vs tsreply-in-threadgroup policy / allowlist

Verification

regression testlive human-origin messagescommit 81320387d
By the Numbers

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.

Agent identities
3
wired to coordinate in one channel, plus the human owner
Files patched
4+ 1 test
in the gateway, plus one new regression test
Root cause
1function
pinned to _resolve_thread_ts() and one exact behavior
Test result
1passed / 54 desel.
gateway back up; Slack authed as the worker bot
1 commit
81320387d — fix(slack): suppress synthetic top-level channel threads, with a timestamped backup directory
Binary outcome
threading stopped; three-way coordination confirmed by live tests — no manufactured percentages
Prior repairs
May: allowlist tightening, a wrong-identity token binding, a deactivated Slack app replaced
Before / After

Reproduction vs. post-patch.

The fix was verified by reproduction, then by live human-origin messages in the channel.

Reproduction · before

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 ✗
Post-patch · after

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 ✓
▲ Read this honestly

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.

Repair Sequence

Diagnose, patch externally, verify.

Step 1 · Diagnose
Root cause on live code

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.

Step 2 · SSH in
Patch from another machine

Instead of letting the worker edit its own running code, the fix was applied over SSH from a separate node — backup touched files first.

Step 3 · Test
Add a targeted regression test

A new test covered the exact behavior — synthetic top-level send stays top-level. Result: 1 passed, 54 deselected.

Step 4 · Restart
Bring the gateway back under launchd

The gateway service restarted cleanly and re-authenticated to Slack as the worker bot.

Step 5 · Verify
Live human-origin confirmation

Real messages in the shared channel confirmed the worker replying top-level with no thread — three-way coordination working end to end.

My Role

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.

Skills Demonstrated

What this took.

Multi-agent Slack integrationAgent identity / routing debuggingSlack API internals (thread_ts, reply-in-thread)Group policy / allowlist designRoot-cause analysis on live codeSafe external patching over SSHLeast-privilege ingress designBot-loop & home-channel routingRegression testing & verification discipline

Want an operator who can root-cause the hard bug — not just wire the happy path?