Skip to content

feat: trigger Scout on issue comments and model threads as conversations#27

Open
dsblank wants to merge 2 commits into
mainfrom
feat/comment-triggers-conversation-threads
Open

feat: trigger Scout on issue comments and model threads as conversations#27
dsblank wants to merge 2 commits into
mainfrom
feat/comment-triggers-conversation-threads

Conversation

@dsblank

@dsblank dsblank commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

What & why

Scout previously ran only when an issue was opened. This adds a second trigger — new comments — and reworks how an issue is presented to the model so a comment thread is read as a real conversation.

Changes

Trigger & gating (triage.py, workflows)

  • Runs on issue_comment: [created] in addition to issues: [opened].
  • Before invoking the model, _should_skip_comment_event() skips: comments on PRs, Scout's own comments (detected by the hidden <!-- scout-feedback --> marker → loop prevention), and bot accounts.
  • Optional SCOUT_COMMENT_TRIGGER_MENTION=true restricts comment runs to those that @scout.
  • Reacts 👀 on the triggering comment (falls back to the issue).

Conversation modeling (agent.py)

  • build_issue_messagebuild_conversation(): issue + comments become alternating user/assistant turns. Human turns are prefixed [author (association)]:; Scout's prior comments map to assistant turns. Consecutive same-role turns are merged so the Messages API sees valid alternation.

Author association (providers/github.py, simulator.py)

  • get_issue_data() now returns author_association and per-comment association + role. Marker detection runs on the full body before truncation.

Opik threads (agent.py)

  • All runs for an issue already share thread_id; each run now records just its own turn (latest comment in → reply out) so the thread reads as a clean dialogue instead of an ever-growing transcript. The model still sees the full conversation.

Provider parity (the simulator must mirror production or evals are meaningless)

  • Both providers now keep the most recent 20 comments (was the first 20 — would drop the triggering comment on long threads) and truncate comment bodies to 500 chars. Parity tests assert identical results against both backends.

Tests / scenarios / docs

  • New add_comment() simulator builder, a multi-party comment-thread starter scenario, and parity tests. System prompt + both READMEs updated. 144 tests pass; ruff + mypy clean.

Workflow YAMLs touched / affected

  • .github/workflows/test.yml — added issue_comment: [created] trigger, updated concurrency group and ISSUE_NUMBER to resolve from either event (dogfoods the comment path on this repo).
  • README canonical triage workflow — added the issue_comment trigger (this is what consumers copy).
  • action.yml / actions/feedback/action.yml — no change needed; triggers live in the caller workflow and the new env var propagates through step-level env:.

Breaking changes

  • build_issue_message() is replaced by build_conversation() (returns (messages, latest_turn)).
  • get_issue_data() dict gains author_association and per-comment association/role.
  • GitHubProvider.add_comment_reaction(issue_number, comment_id, reaction) added.

🤖 Generated with Claude Code

Douglas Blank and others added 2 commits June 4, 2026 15:06
Scout now runs on new issue comments, not only on issue open, and reads the
whole thread as a multi-turn conversation.

- Trigger: workflows listen on `issue_comment: [created]` in addition to
  `issues: [opened]`. main() gates comment runs — skips PR comments, Scout's
  own comments (hidden marker), and bots; optional SCOUT_COMMENT_TRIGGER_MENTION
  restricts to @scout mentions. Reacts on the triggering comment.
- Conversation: build_conversation() renders the issue + thread as alternating
  user/assistant turns. Human turns are prefixed [author (association)] so the
  model can weigh maintainer input; Scout's prior comments become assistant
  turns. Consecutive same-role turns are merged for valid alternation.
- Author association: get_issue_data() now returns author_association and
  per-comment association + role on both providers.
- Opik threads: each run records just its turn (latest comment in -> reply out)
  under the existing per-issue thread_id, so a thread reads as a clean dialogue.
- Provider parity: both providers now keep the most recent 20 comments
  (was first 20 — would drop the triggering comment) and truncate comment
  bodies to 500 chars, so evals exercise the production code path.
- Simulator/scenarios/tests/docs updated: add_comment() builder, a multi-party
  comment-thread starter scenario, and parity tests asserted against both
  providers. System prompt and READMEs document the new behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Scout runs on GITHUB_TOKEN and now reacts to comments and reads threads. When
the token lacks the required permissions, the pre-agent GitHub calls (provider
setup, reaction, get_issue_data) previously crashed with a raw traceback.

- Wrap provider construction and get_issue_data so a 401/403 exits non-zero
  with a clear message naming the permissions the workflow must grant
  (issues: write, contents: read).
- _react() makes the 👀 acknowledgement best-effort: a missing reaction
  permission is logged, not fatal, and a failed comment reaction falls back to
  the issue. Triage proceeds regardless.
- The top-level handler now renders GithubException with the same actionable
  message instead of a traceback (e.g. a permission error posting the comment).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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