Commit be1839e
authored
channels: mount Slack HTTP receiver on shared Bun.serve (#103)
* channels: mount Slack HTTP receiver on shared Bun.serve
The HTTP-mode Slack channel was binding ExpressReceiver to the same
port as Phantom's main Bun.serve listener. The bind silently failed,
the router caught the rejection, and /slack/* never came up; live
canary traffic verified `POST /slack/events` returned 404 and
`/health.channels.slack` was false.
Refactor SlackHttpChannel into a Bolt App driven by a tiny
BunReceiver whose start/stop are no-ops. The three Slack ingress
paths now mount on the existing Bun.serve via slack-http-routes.ts;
the channel exposes handleEvent, handleInteractivity, and
handleCommand, each running the existing slack-gateway-verifier
guard before dispatching the parsed body into app.processEvent.
The verifier file is unchanged. Slash commands (urlencoded with
top-level team_id) are picked up by a small extractor in the
dispatcher so the third Slack endpoint can satisfy team-id binding
without touching the verifier.
Tests cover the verifier guard happy and failure paths via real
Request objects, plus a hermetic Bun.serve test that proves the
routes are alive end-to-end.
* channels: gate slack-http dispatch on channel.isConnected() (Codex P1)
The provider is wired in src/index.ts during channel setup before
router.connectAll() runs, so inbound POSTs arriving in the startup
window dispatched into a half-initialized channel. The gate also
covers the post-disconnect() path (state flips to "disconnected")
and the auth-failure path (state flips to "error").
Both 503 paths return JSON. Slack retries 503 up to 3 times within
5 minutes per the inbound contract, so the gate resolves naturally.
+3 tests pinning the not-yet-connected case, the post-disconnect
sibling case (audit doc inline), and the same gate on /slack/interactivity.
* channels: race processEvent against ack in dispatchToBolt (Codex round 2 P1+P2)
Previously dispatchToBolt awaited input.app.processEvent before returning,
but Bolt's processEvent resolves only when listener middleware finishes,
not when ack() fires. Phantom's Slack listener calls runtime.handleMessage
which routinely outlasts Slack's ~3s ack window, so /slack/events stayed
open past the deadline and triggered Slack-side retries. The catch path
also called ackFn() with no args, which resolves to 200 and silently
suppressed listener failures.
Replace the await + try/catch with Promise.race over three tagged
outcomes: ack winner returns the listener's response immediately,
processEvent rejecting before ack surfaces as 500 so Slack retries, and
processEvent resolving without any ack falls back to a 200 with an
operator warning (defense against a hypothetical Bolt regression). The
async listener work continues in the background, matching the previous
HTTPReceiver processBeforeResponse=false semantics.
Tests: +3 covering long-running listener (handler returns 200 while
listener continues), processEvent rejects before ack (handler 500),
and processEvent resolves without ack (handler 200 + warning). Total
1971 pass, typecheck and lint clean. File at 294 lines (under 300
channels/ budget).1 parent 337a282 commit be1839e
9 files changed
Lines changed: 1026 additions & 294 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
125 | 125 | | |
126 | 126 | | |
127 | 127 | | |
128 | | - | |
129 | 128 | | |
130 | 129 | | |
131 | 130 | | |
| |||
137 | 136 | | |
138 | 137 | | |
139 | 138 | | |
140 | | - | |
141 | 139 | | |
142 | 140 | | |
143 | 141 | | |
| |||
146 | 144 | | |
147 | 145 | | |
148 | 146 | | |
149 | | - | |
150 | 147 | | |
151 | 148 | | |
152 | 149 | | |
| |||
158 | 155 | | |
159 | 156 | | |
160 | 157 | | |
161 | | - | |
162 | 158 | | |
163 | 159 | | |
164 | 160 | | |
| |||
171 | 167 | | |
172 | 168 | | |
173 | 169 | | |
174 | | - | |
175 | 170 | | |
176 | 171 | | |
177 | 172 | | |
| |||
190 | 185 | | |
191 | 186 | | |
192 | 187 | | |
193 | | - | |
194 | 188 | | |
195 | 189 | | |
196 | 190 | | |
| |||
211 | 205 | | |
212 | 206 | | |
213 | 207 | | |
214 | | - | |
215 | 208 | | |
216 | 209 | | |
217 | 210 | | |
| |||
232 | 225 | | |
233 | 226 | | |
234 | 227 | | |
235 | | - | |
236 | 228 | | |
237 | 229 | | |
238 | 230 | | |
| |||
0 commit comments