Pre-existing on main — not introduced by #225. Verified this function and every function it calls in this report already exist on main; #225 does not touch any of them. Filed separately because it surfaced while auditing #226 (the GitHub-fallback eligibility survey), but it is an independent defect with its own trigger, unrelated to fallback eligibility.
Summary
A human's Slack reply to a triage-escalation question is silently dropped after a daemon restart, because the Slack watcher that would receive it is never rearmed. This is not about GitHub API fallback eligibility — a triage escalation resolved entirely through the normal Relayfile projection has the same problem.
The watcher rearm function only covers two record shapes
#rearmSlackReplyWatchers runs once at startup and re-establishes Slack reply watchers for issues that had one before the process stopped. It iterates three sources, and a pre-dispatch triage escalation is absent from all three:
batch.inFlight — the in-memory dispatch tracker. A triage escalation exists specifically before dispatch (the routing decision needed human input before the issue was ever dispatched), so it was never inserted here.
#state.listConversationSessions, filtered to provider === 'slack' — this branch looks promising (a triage-escalation thread does create a conversation session), but it gates the actual rearm on const record = batch.getIssue(session.issue) and only proceeds if (record && ...). Since the escalation is not in-flight, record is undefined and the rearm is skipped.
#state.listWaitingClarifications — a different durable concept (an agent mid-implementation blocked on a question), not a pre-dispatch triage escalation.
The escalation's decision is durable — persisted via #state.setGithubIssueCommentWatch (kind: 'triage') when the escalation is first posted — but nothing in the rearm function reads that store.
Consequence
- Factory dispatches, triage escalates on ambiguous routing, an escalation question is posted and mirrored to Slack.
- The daemon restarts before a human replies (deploy, crash, planned restart — any restart).
- A human replies on the Slack thread.
- No watcher is listening for that thread anymore. The reply is not processed. Nothing surfaces the drop to the operator or the stakeholder — there is no error, just silence.
Suggested direction
Add a fourth branch to #rearmSlackReplyWatchers that iterates the durable comment-watch store, filters to kind === 'triage' entries with a decision and a known Slack thread, and rearms using the same escalationWatchRecord-style construction already used elsewhere for this exact decision shape. Worth checking whether the GitHub-side equivalent (comment polling) has a matching gap or whether it's naturally covered by durable polling instead of an in-memory watcher — the two providers may not have the same defect shape.
Scope note
This was found while doing a systematic call-site audit for #226 (GitHub API fallback eligibility). It is unrelated to that mechanism — the failure here is "no listener is rearmed at all," not "the listener reads the wrong thing." Please don't conflate fixes for the two.
Pre-existing on
main— not introduced by #225. Verified this function and every function it calls in this report already exist onmain; #225 does not touch any of them. Filed separately because it surfaced while auditing #226 (the GitHub-fallback eligibility survey), but it is an independent defect with its own trigger, unrelated to fallback eligibility.Summary
A human's Slack reply to a triage-escalation question is silently dropped after a daemon restart, because the Slack watcher that would receive it is never rearmed. This is not about GitHub API fallback eligibility — a triage escalation resolved entirely through the normal Relayfile projection has the same problem.
The watcher rearm function only covers two record shapes
#rearmSlackReplyWatchersruns once at startup and re-establishes Slack reply watchers for issues that had one before the process stopped. It iterates three sources, and a pre-dispatch triage escalation is absent from all three:batch.inFlight— the in-memory dispatch tracker. A triage escalation exists specifically before dispatch (the routing decision needed human input before the issue was ever dispatched), so it was never inserted here.#state.listConversationSessions, filtered toprovider === 'slack'— this branch looks promising (a triage-escalation thread does create a conversation session), but it gates the actual rearm onconst record = batch.getIssue(session.issue)and only proceedsif (record && ...). Since the escalation is not in-flight,recordisundefinedand the rearm is skipped.#state.listWaitingClarifications— a different durable concept (an agent mid-implementation blocked on a question), not a pre-dispatch triage escalation.The escalation's decision is durable — persisted via
#state.setGithubIssueCommentWatch(kind: 'triage') when the escalation is first posted — but nothing in the rearm function reads that store.Consequence
Suggested direction
Add a fourth branch to
#rearmSlackReplyWatchersthat iterates the durable comment-watch store, filters tokind === 'triage'entries with adecisionand a known Slack thread, and rearms using the sameescalationWatchRecord-style construction already used elsewhere for this exact decision shape. Worth checking whether the GitHub-side equivalent (comment polling) has a matching gap or whether it's naturally covered by durable polling instead of an in-memory watcher — the two providers may not have the same defect shape.Scope note
This was found while doing a systematic call-site audit for #226 (GitHub API fallback eligibility). It is unrelated to that mechanism — the failure here is "no listener is rearmed at all," not "the listener reads the wrong thing." Please don't conflate fixes for the two.