feat(bridge): nudge on idle question + acknowledge typed answers (GENAI-151) - #23
Merged
Merged
Conversation
…AI-151) Interactive question round-trips could hang silently to the job deadline when a reviewer's answer was lost in transit — e.g. a bridge reply delivered to a competing Socket Mode consumer of the same Slack app is dropped, so question.Ask never wakes. Two resilience improvements, both working in daemon and orchestrator-mediated deployments (outbound is always pod-side): - Idle "still waiting" nudge: QuestionRouter re-posts a short status to the bound peers of a session with an outstanding question after an idle gap (default 5m, capped at 3, configurable via router.questionNudgeIntervalSeconds / questionNudgeMax; negative interval disables). Re-surfaces a lost answer instead of hanging. - Typed-answer acknowledgment: a free-text / @mention / modal answer gets a brief "got it" confirmation; a button click is skipped (it already self-renders a "checked Answered" widget). Driven by a new Inbound.Source field; unknown/absent source is treated as already-acked so a button from an older orchestrator is never double-acknowledged. Adds 10 tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Interactive
question-tool round-trips over the chat bridge can hang silently to the job's hard deadline when a reviewer's answer is lost in transit. GENAI-151: when more than one Socket Mode connection is open for the same Slack app (e.g. a localopencodedev instance sharing the dev orchestrator's bridge app token), Slack load-balances inbound across them, so ~half the button clicks land on a connection that has no binding for the job and are dropped —question.Asknever wakes, and the step dies atJOB_TIMEOUTwith no signal to the user.The one-owner-per-app topology issue is fixed in config/ops. This PR makes the runtime resilient to a lost/misrouted answer, in both daemon and orchestrator-mediated deployments (outbound is always pod-side).
What
QuestionRouter): a sweeper re-posts a short status to the session's bound peers when a question has been outstanding past an idle gap, re-surfacing a lost answer instead of hanging.router.questionNudgeIntervalSeconds/router.questionNudgeMax; a negative interval disables it.@mention/ custom-modal answer now gets a brief "👍 Got it — recorded your answer: … Working on it…" reply so the reviewer knows it landed. A button click is skipped — it already self-renders a "✓ Answered" widget.bridge.Inbound.Sourcefield (button/modal/message/appmention). Unknown/absent source ⇒ treated as already-acked, so a button forwarded by an older orchestrator (nosourceon the wire) is never double-acknowledged. The orchestrator side that stampssourcefor the mediated path is a companion c2-agent MR.Tests
10 new tests: the ack-suppression truth table (
AnswerWasAcknowledgedByTransport),maybeAckAnswer(acks typed, skips button/unknown), and the nudger (fires after interval, respects spacing/cap/disable/custom-interval, exits on ctx-cancel).🤖 Generated with Claude Code