feat(twitch): preserve accepted chat through local crashes#110852
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6c08fbed8a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| shutdown.abort(stoppedError()); | ||
| await drainTask; | ||
| await Promise.allSettled(activeDeliveries); | ||
| await Promise.allSettled(deferredClaims.values()); |
There was a problem hiding this comment.
Do not await unresolved deferred claims on stop
When a Twitch message is queued behind an active reply lane, core marks the source deferred (see markFollowupRunEnqueued in src/auto-reply/reply/queue/types.ts) and adoption/abandon may happen much later. This new stop path aborts the ingress and then waits on deferredClaims, but those promises are only resolved by the wrapped onAdopted/onAbandoned callbacks; if the queued turn is not admitted before shutdown, monitor.stop()/gateway abort can hang indefinitely instead of allowing the claimed row to be recovered by the next process.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 34038604fa
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return; | ||
| } | ||
|
|
||
| void ingress.accept(message).catch((err: unknown) => { |
There was a problem hiding this comment.
Filter denied Twitch chat before durable admission
When the bot is connected to a public or busy channel where requireMention, allowFrom, roles, or bot-echo filtering reject most messages, this now sends every raw chat line through ingress.accept() before those gates run in the ingress deliver callback. That means unmentioned/denied messages are synchronously written to the durable SQLite queue and may persist raw chat across crashes, then drain serially just to be discarded; before this change the same filters ran before dispatch without a durable write. Prefiltering obvious non-dispatch messages before admission, while still rechecking on replay, avoids the disk churn/backlog and raw-message retention for traffic the plugin will never handle.
Useful? React with 👍 / 👎.
…110852) * feat(twitch): add durable ingress queue * fix(twitch): satisfy ingress type boundaries
What Problem This Solves
Twitch chat messages accepted from IRC could be lost if the local process stopped after socket acceptance but before inbound dispatch completed.
Why This Change Was Made
Adds a Twitch-owned durable ingress queue that persists the raw callback envelope before dispatch, drains one channel lane serially, threads the adoption lifecycle into inbound handling, and recovers pending local work after restart.
Never-replay caveat: this transport does not redeliver after socket acceptance; dedupe tombstones are near-inert. The durable queue's value is crash-window durability between accept and dispatch, with local restart recovery.
User Impact
Twitch users get local recovery for accepted chat messages across a process crash in the accept-to-dispatch window. This does not provide end-to-end at-least-once delivery from Twitch and cannot recover a message that was never durably admitted locally.
Evidence
node scripts/run-vitest.mjs extensions/twitch: 20 files, 211 tests passed.node scripts/run-vitest.mjs extensions/irc/src/client.test.ts extensions/irc/src/monitor.test.ts: 2 files, 18 real-socket tests passed.oxfmt 0.58.0 --checkpassed on all 11 touched files.git diff --check origin/main...HEADpassed.origin/main: clean, no accepted/actionable findings.