fix: prevent infinite bot comment loop#62
Conversation
The bot was posting duplicate triage reports in an infinite loop because: 1. Response builder wrote '### Simili Triage Report' but command_handler checked for '🤖 Simili Triage Report' — marker mismatch meant the bot never recognized its own comments 2. No author-based filtering existed — even if the marker worked, relying solely on body content is fragile 3. pr_comment events were not handled in command_handler's self-detection Defense-in-depth fix (4 layers): Layer 1 — Hidden HTML marker: Response builder now prepends '<!-- simili-bot-report -->' to every triage comment. This marker survives title rewording. Layer 2 — Multi-marker detection (isBotComment): Checks for HTML marker, legacy emoji marker, AND plain-text header. Used in command_handler and history analysis. Layer 3 — Author-based filtering (isBotAuthor): Gatekeeper and command_handler both check if the comment author is a known bot (heuristic: '[bot]' suffix, 'gh-simili' prefix, or user-configured bot_users list in config). Layer 4 — Config-driven bot_users list: Users can add custom bot usernames to 'bot_users' in simili.yaml for environments using PAT tokens (which don't have '[bot]' suffix). Also fixes: pr_comment events now go through the same self-detection path as issue_comment events. Signed-off-by: Mahsum Aktas <mahsum@mahsumaktas.com>
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughAdded configurable bot user list and multi-marker bot-detection across Gatekeeper, CommandHandler, and response generation; injected a hidden HTML marker into triage summaries to reliably identify bot-generated comments and prevent self-triggered processing loops. Changes
Sequence Diagram(s)sequenceDiagram
participant Event as Incoming Event\n(issue_comment / pr_comment)
participant Gate as Gatekeeper
participant Config as Config
participant Handler as CommandHandler
participant Builder as ResponseBuilder
Event->>Gate: Deliver event with author + body
activate Gate
Gate->>Config: Read BotUsers
Gate->>Gate: isBotAuthor(author, BotUsers)?
alt bot author
Gate-->>Event: Log and skip event
else not bot
Gate-->>Handler: Forward event
end
deactivate Gate
activate Handler
Handler->>Handler: isBotComment(body)?
alt bot comment
Handler-->>Event: Log and skip processing
else user comment
Handler->>Handler: Process commands (e.g., /undo)
Handler->>Handler: Scan history using isBotComment to find prior transfers
Handler->>Builder: Build any response
end
deactivate Handler
activate Builder
Builder->>Builder: buildTriageSummary()
Builder->>Builder: Prepend <!-- simili-bot-report --> then visible header
Builder-->>Event: Post response (contains hidden marker)
deactivate Builder
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
Simili Triage ReportNote Quality Score: 8.8/10 (Good) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@internal/core/config/config.go`:
- Around line 46-50: mergeConfigs currently fails to preserve parent BotUsers
when a child config uses extends; update mergeConfigs to merge the BotUsers
slice from the parent into the child (similar to how other list fields are
handled) by appending parent.BotUsers into cfg.BotUsers and deduplicating
entries (or preserving order) so inherited bot usernames are retained; reference
the BotUsers field on the config struct and the mergeConfigs function to locate
where to add this merge logic.
🧹 Nitpick comments (1)
internal/steps/command_handler.go (1)
33-40: Centralize bot markers to prevent drift.The markers are duplicated across files; consider defining shared constants in
stepsand using them in bothisBotCommentandResponseBuilderto avoid future mismatch.♻️ Suggested refactor
@@ -import ( +import ( "fmt" "log" "strings" @@ ) + +const ( + botReportMarker = "<!-- simili-bot-report -->" + botReportEmoji = "🤖 Simili Triage Report" + botReportHeader = "### Simili Triage Report" +) @@ func isBotComment(body string) bool { - return strings.Contains(body, "<!-- simili-bot-report -->") || - strings.Contains(body, "🤖 Simili Triage Report") || - strings.Contains(body, "### Simili Triage Report") + return strings.Contains(body, botReportMarker) || + strings.Contains(body, botReportEmoji) || + strings.Contains(body, botReportHeader) }And update
internal/steps/response_builder.goto usebotReportMarkerwhen building the header.
|
|
||
| // BotUsers is a list of GitHub usernames whose events should be ignored | ||
| // to prevent infinite comment loops. Built-in heuristics (e.g. "[bot]" suffix, | ||
| // "gh-simili" prefix) always apply in addition to this list. | ||
| BotUsers []string `yaml:"bot_users,omitempty"` |
There was a problem hiding this comment.
Merge BotUsers during config inheritance.
BotUsers is added on Line 46, but mergeConfigs doesn’t copy it, so parent bot_users will be lost when using extends. This can re-enable the comment loop in inherited configs. Consider merging it the same way you handle other list overrides.
🛠️ Proposed fix (mergeConfigs)
@@
// Repositories: child completely overrides if non-empty
if len(child.Repositories) > 0 {
result.Repositories = child.Repositories
}
+
+ // BotUsers: child overrides if non-empty
+ if len(child.BotUsers) > 0 {
+ result.BotUsers = child.BotUsers
+ }🤖 Prompt for AI Agents
In `@internal/core/config/config.go` around lines 46 - 50, mergeConfigs currently
fails to preserve parent BotUsers when a child config uses extends; update
mergeConfigs to merge the BotUsers slice from the parent into the child (similar
to how other list fields are handled) by appending parent.BotUsers into
cfg.BotUsers and deduplicating entries (or preserving order) so inherited bot
usernames are retained; reference the BotUsers field on the config struct and
the mergeConfigs function to locate where to add this merge logic.
Simili Triage ReportNote Quality Score: 9.0/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.8/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.2/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.0/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 8.8/10 (Good) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 8.8/10 (Good) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.0/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 8.8/10 (Good) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 8.8/10 (Good) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 8.5/10 (Good) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 8.8/10 (Good) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.0/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 8.8/10 (Good) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.2/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.2/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 8.8/10 (Good) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 8.8/10 (Good) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.2/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 9.5/10 (Excellent) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 8.8/10 (Good) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 8.8/10 (Good) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
Simili Triage ReportNote Quality Score: 8.8/10 (Good) Classification
Quality Improvements
Similar Threads
Generated by Simili Bot |
The gatekeeper's bot-author check was only effective for issue_comment events because CommentAuthor was only set from comment.user.login. For pull_request.edited (and other PR) events the field stayed empty, causing the guard `if ctx.Issue.CommentAuthor != ""` to short-circuit and skip the isBotAuthor check entirely. As a result, bot-triggered pull_request.edited events (e.g. coderabbitai updating a PR review) passed through the gatekeeper and re-ran the triage pipeline, posting duplicate triage comments and labels (#62 fix partial). Fix: fall back to sender.login in enrichIssueFromGitHubEvent when CommentAuthor is not already set. sender is present in all GitHub webhook payloads and correctly identifies who triggered the event, giving the gatekeeper full coverage across all event types. Signed-off-by: Kavirubc <hapuarachchikaviru@gmail.com>
The gatekeeper's bot-author check was only effective for issue_comment events because CommentAuthor was only set from comment.user.login. For pull_request.edited (and other PR) events the field stayed empty, causing the guard `if ctx.Issue.CommentAuthor != ""` to short-circuit and skip the isBotAuthor check entirely. As a result, bot-triggered pull_request.edited events (e.g. coderabbitai updating a PR review) passed through the gatekeeper and re-ran the triage pipeline, posting duplicate triage comments and labels (#62 fix partial). Fix: fall back to sender.login in enrichIssueFromGitHubEvent when CommentAuthor is not already set. sender is present in all GitHub webhook payloads and correctly identifies who triggered the event, giving the gatekeeper full coverage across all event types. Signed-off-by: Kavirubc <hapuarachchikaviru@gmail.com>
Summary
Fixes the infinite comment loop where
gh-simili-botposts 20+ duplicate triage reports on the same issue/PR (~1 per minute).Root Cause
Marker mismatch — the bot could never recognize its own comments:
response_builder.go### Simili Triage Reportcommand_handler.go🤖 Simili Triage ReportThe emoji
🤖was never in the output, so the self-detection always failed → every bot comment triggered a new workflow run → new comment → infinite loop.Fix: Defense-in-Depth (4 Layers)
response_builder.go<!-- simili-bot-report -->to every triage commentcommand_handler.goisBotComment()checks HTML marker + legacy emoji + plain-text headergatekeeper.go+command_handler.goisBotAuthor()checks[bot]suffix,gh-similiprefix, and configurablebot_userslistconfig.gobot_usersYAML field for environments using PAT tokens (no[bot]suffix)Why 4 layers?
[bot]suffixAdditional Fix
pr_commentevents were not handled in command_handler's self-detection branch. Nowissue_commentandpr_commentboth go through the same path.Config Example
For environments using a PAT token instead of a GitHub App:
Files Changed
response_builder.gocommand_handler.goisBotComment(), author check,pr_commentsupportgatekeeper.goisBotAuthor(), early exit for bot eventsconfig.goBotUsersfieldTesting
go build ./...✅go vet ./...✅go test ./...✅ (all packages pass)Summary by CodeRabbit
New Features
Bug Fixes