Skip to content

feat(bridge): nudge on idle question + acknowledge typed answers (GENAI-151) - #23

Merged
obukhovaa merged 2 commits into
mainfrom
GENAI-151-interactive-question-nudge-and-ack
Aug 7, 2026
Merged

feat(bridge): nudge on idle question + acknowledge typed answers (GENAI-151)#23
obukhovaa merged 2 commits into
mainfrom
GENAI-151-interactive-question-nudge-and-ack

Conversation

@obukhovaa

Copy link
Copy Markdown
Owner

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 local opencode dev 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.Ask never wakes, and the step dies at JOB_TIMEOUT with 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

  1. Idle "still waiting" nudge (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.
    • Defaults: 5 min interval, capped at 3 nudges. Configurable via router.questionNudgeIntervalSeconds / router.questionNudgeMax; a negative interval disables it.
  2. Typed-answer acknowledgment: a free-text / @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.
    • Driven by a new bridge.Inbound.Source field (button/modal/message/appmention). Unknown/absent source ⇒ treated as already-acked, so a button forwarded by an older orchestrator (no source on the wire) is never double-acknowledged. The orchestrator side that stamps source for 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

obukhovaa and others added 2 commits August 6, 2026 21:48
…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>
@obukhovaa
obukhovaa merged commit 7947cd4 into main Aug 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant