Ships live in #172. Closed by #170's delta, filed so the gap stays visible while #172 is in production.
handleAiResponse deliberately returns success: false when a response was neither delivered to the reporter nor handed to a human — the fail-loud case an operator needs to see. The re-answer gate added in the same PR neutralises it.
The sequence
- Attempt 1 generates the answer and commits the
BOT message row.
- Platform post-back fails, and no escalation gets enqueued.
- The handler returns
success: false so the failure is visible.
worker.ts marks the job PENDING for retry.
- Attempt 2 loads the ticket, sees attempt 1's
BOT row, and takes the already-answered skip path — which returns success: true.
worker.ts writes status: COMPLETED, progress: 100.
The error column still holds attempt 1's message, but the job status reads green. An operator scanning for failed jobs sees nothing.
Why the gate can't just be loosened
The gate is the requirement — it is what stops the bot answering twice. The fix is to distinguish answered from delivered: the skip path should only declare success when the prior response actually reached the reporter.
#170's delta does this properly, with Message.responseState (PENDING/DELIVERED/ESCALATED) plus delivery-confirmation markers, so an undelivered PENDING response routes into recovery rather than a silent success. That machinery is not in #172.
Exposure while #172 is in production
Requires a platform post-back failure and a failed escalation enqueue in the same run. Both are already logged, so the evidence exists in logs even though the job row looks clean. Low frequency, silent when it happens.
Close this when #170 merges — or fix it directly in main if #170 is going to sit.
Ships live in #172. Closed by #170's delta, filed so the gap stays visible while #172 is in production.
handleAiResponsedeliberately returnssuccess: falsewhen a response was neither delivered to the reporter nor handed to a human — the fail-loud case an operator needs to see. The re-answer gate added in the same PR neutralises it.The sequence
BOTmessage row.success: falseso the failure is visible.worker.tsmarks the jobPENDINGfor retry.BOTrow, and takes the already-answered skip path — which returnssuccess: true.worker.tswritesstatus: COMPLETED, progress: 100.The
errorcolumn still holds attempt 1's message, but the job status reads green. An operator scanning for failed jobs sees nothing.Why the gate can't just be loosened
The gate is the requirement — it is what stops the bot answering twice. The fix is to distinguish answered from delivered: the skip path should only declare success when the prior response actually reached the reporter.
#170's delta does this properly, with
Message.responseState(PENDING/DELIVERED/ESCALATED) plus delivery-confirmation markers, so an undeliveredPENDINGresponse routes into recovery rather than a silent success. That machinery is not in #172.Exposure while #172 is in production
Requires a platform post-back failure and a failed escalation enqueue in the same run. Both are already logged, so the evidence exists in logs even though the job row looks clean. Low frequency, silent when it happens.
Close this when #170 merges — or fix it directly in
mainif #170 is going to sit.