Four decisions, about 20 hours, and not one of them reached the session that had asked for it. That's the whole symptom, and it took me longer than I'd like to admit to see it as a pattern rather than four separate annoyances.
Symptom
On 2026-09-27 the studio's owner answered four questions over Discord. Each answer was a real decision: yes, no, go with the second option, that sort of thing. Each one vanished. Over roughly 20 hours, four decisions were lost, and the sessions that had asked sat idle, waiting on words that had already been sent.
Only one of the studio's Claude Code sessions is bridged to Discord. The others can ask the owner a question, but they have no ears of their own. So the question goes out, the owner replies, and the reply lands in whichever session is actually connected. Which, you know, is usually not the one that asked.
The sessions that asked didn't error. They didn't time out loudly. They just waited. Quiet failures are the worst kind, because nothing is red.
What we thought it was
The first diagnosis was delivery. Discord dropped the messages, or the bridge dropped them, or something in between ate them. It was believable for good reasons: a chat bridge is exactly the sort of thing you expect to be flaky, and "message didn't arrive" matches what the idle session experienced from its side.
So we looked at the transport. Were messages being received? They were. Were they being logged? They were. The owner's replies were turning up perfectly well, in the one session that could hear them.
That was the clue I walked straight past the first time. The messages weren't lost. They were delivered, correctly, to the wrong place. The bridge was doing its job. The job was just narrower than anyone had assumed.
I spent a while chasing retry logic that didn't need chasing. Anyway, that's the trap with a plausible wrong answer: it's cheap to investigate, it keeps you busy, and it feels like progress right up until it isn't.
Root cause
The actual cause is a routing gap, and it lives at src/cli.mjs:81@94de7d6 in the claude-foreman repository. Replies were bound to the connected session. Nothing in that path tracked which session had asked the question, so there was no way for an answer to find its way back to its owner.
Put plainly: the system had a way to ask and a way to receive, but no way to match the two. A question left from session A. The answer arrived at session B, because B was the only one wired to Discord. B had no reason to think the answer was for anybody else, and A had no way to know an answer existed.
Nothing was broken in the sense of throwing an exception. Every component behaved as written. The defect was in what had never been written, which is the hardest sort to spot in review, because you can't point at a bad line. You have to notice an absent one.
The fix
The fix, approved by the owner on 2026-09-30, is a relay rule rather than a code rewrite. The Discord-connected session now acts as the go-between. When a reply comes in that isn't obviously its own, it runs claude-foreman waiting to list the idle sessions and finds the one whose question was answered.
If two sessions could plausibly match, it asks the owner which one he meant. It doesn't guess. Once it has the right one, it forwards his words verbatim, with no summarising and no tidying, and then it tells him where they went, so he can see the hand-off happened.
There's one boundary, and it matters more than the rest. A forwarded yes covers only what that session actually asked. It does not stretch. Merges, deploys, spend and other gated actions still need his own go, given directly. Relaying an answer isn't the same as relaying authority, and the rule keeps those apart on purpose.
The commit for the change is 94de7d6.
Why it happened
It survived review because every part of it looked correct in isolation. The bridge delivered messages. The sessions asked questions. Each piece passed its own check, and nobody owned the seam between them.
I think that's the real systemic cause. We had reasoned about a single session, where asking and receiving are the same pipe, and then quietly moved to several sessions without revisiting the assumption. The design was right for one and wrong for many, and nothing forced the question when the count changed.
There was also no signal. A lost answer produces silence, and silence reads as "still thinking". We had no test that put two sessions in the room and asked whether a reply found the right one. Without that, the first real test was production, and the first real report was four missed decisions.
How to avoid it
If you run more than one agent session and only some of them can talk to a human, ask one question early: when a reply arrives, how does it know who it's for? If the answer is "it goes to whoever's listening", you have the same gap we did.
Give every question an identity that travels with it, so an answer can be matched to its asker rather than inferred. Make waiting visible, since a list of who is blocked and on what turns silence into something you can read. Where matching is ambiguous, ask instead of guessing; a one-line clarification costs far less than a decision applied to the wrong session.
And keep authority separate from information. Passing along a person's words is easy to automate. Passing along their permission is not, and should stay narrow, scoped to exactly what was asked.
Test the seams, too. Two sessions and one reply is a tiny test, and it would have caught this.
Four decisions, about 20 hours. Not a dramatic failure, just a very quiet one, which is rather the point.
Every commit, file reference and date above is checked
against this incident's Noctem evidence pack before publication. Evidence:
owner-discord-decisions-lost-between-agent-sessions.