feat: add @botname sync command for PR comment-triggered merges - #58
Conversation
When a user posts @botName merge on a GitHub PR conversation comment, the bot merges main into the dev branch, resolving conflicts with AI if needed. This addresses cases where a PR is behind main with no conflicts (so the MergeScanner doesn't trigger) but CI failures fixed on main need to be picked up. The feature uses a single PR reply for feedback, consistent with the existing <!-- addressed: ID --> pattern: - Initial: "Merging from <base> -> <branch>" - Updated on success: "Successfully merged ... ; change <sha>" - Updated on failure: "Failed to merge ... ; <reason>" Detection uses BotRepliedTo (same as feedback pipeline) to prevent re-processing across bot restarts. The scanner filters to conversation comments only and compiles the command regex once per scan cycle. Key changes: - commentfilter: export NormalizeUsername, add BotCommandRe/IsMergeCommand - jobmanager: add CommandSource on Event/Job, MergeCommitSHA on JobResult - scanner/merge: WithMergeCommands option, findMergeCommand with BotRepliedTo detection, refactored checkAndSubmit + submitMergeEvent - executor/merge: postMergeCommandReply/updateMergeCommandReply with commandSourceBaseBranch for correct multi-repo base branch lookup - services/github: PostIssueComment returns (int64, error) for reply ID Assisted-by: Claude Opus 4.6 (1M) <noreply@anthropic.com>
WalkthroughThe merge scanner detects unaddressed bot sync commands, propagates their source comments, and enables merge execution to post and update addressed acknowledgments. Issue-comment APIs now return created IDs, and username normalization is exported and reused. ChangesMerge command workflow
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant MergeScanner
participant Coordinator
participant Pipeline
participant GitHubServiceImpl
MergeScanner->>Coordinator: submit Event with CommandSource
Coordinator->>Pipeline: dispatch Job with CommandSource
Pipeline->>GitHubServiceImpl: create acknowledgment comment
GitHubServiceImpl-->>Pipeline: return comment ID
Pipeline->>GitHubServiceImpl: update acknowledgment with merge result
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 12 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (12 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@executor/merge.go`:
- Around line 42-63: Ensure failures from GetWorkItem and ResolveProject are
acknowledged before returning by installing the merge reply/defer lifecycle
before preflight, or by explicitly posting an addressed failure reply on both
early-return paths. Update the surrounding merge flow using
postMergeCommandReply, updateMergeCommandReply, and handleMergeFailure, and add
regression coverage verifying each preflight failure is acknowledged and not
resubmitted.
In `@jobmanager/coordinator.go`:
- Around line 162-170: Clone the caller-owned command metadata before assigning
it to the queued Job in the job construction path. Update the CommandSource
assignment to store an independent copy of event.CommandSource, preserving the
existing metadata values while preventing later caller mutations or concurrent
reads from affecting execution.
In `@scanner/merge.go`:
- Around line 302-380: Update findMergeCommand to pass the already-normalized
normBot value to commentfilter.BotRepliedTo instead of s.cfg.BotUsername,
preserving the existing normalized author checks. Add a regression test covering
a BotUsername with uppercase characters or a “[bot]” suffix and verify addressed
merge commands are not resubmitted.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 73a375e8-a83e-4aa5-8a51-d12e08aaf200
📒 Files selected for processing (19)
commentfilter/ci_fix_attempt.gocommentfilter/commentfilter.gocommentfilter/commentfilter_test.goexecutor/costcomment.goexecutor/costcomment_test.goexecutor/executor.goexecutor/executortest/stubs.goexecutor/feedback.goexecutor/feedback_test.goexecutor/merge.goexecutor/merge_test.goexecutor/pipeline_test.gojobmanager/coordinator.gojobmanager/coordinator_test.gojobmanager/manager.gomain.goscanner/merge.goscanner/merge_test.goservices/github.go
- Fix BotRepliedTo normalization bug: pass normBot (not raw s.cfg.BotUsername) to BotRepliedTo in findMergeCommand. Add regression test with [bot]-suffixed username. - Clone CommandSource at Submit time: defensive copy in coordinator prevents caller mutations from affecting queued jobs. - Acknowledge preflight failures: post addressed failure reply when GetWorkItem or ResolveProject fail before the reply lifecycle is set up. Prevents the scanner from re-detecting unaddressed commands every cycle. - Rename command from "merge" to "sync": avoids ambiguity about merge direction. "sync" clearly means "bring this branch up to date with main", not "merge this PR into main". - Resolve correct base branch for multi-repo: commandSourceBaseBranch looks up the base branch from the repo the command was posted on instead of defaulting to Repos[0]. Assisted-by: Claude Opus 4.6 (1M) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scanner/merge.go (1)
342-347: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMinor — normalize the filtered comment slice.
var convComments []models.PRCommentremains nil when there are no conversation comments. Initialize it as an empty slice to preserve the repository’s empty-result contract.Proposed fix
- var convComments []models.PRComment + convComments := make([]models.PRComment, 0, len(comments))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scanner/merge.go` around lines 342 - 347, Initialize convComments as a non-nil empty []models.PRComment before filtering in the comments loop, while preserving the existing append behavior for conversation comments.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@scanner/merge.go`:
- Around line 342-347: Initialize convComments as a non-nil empty
[]models.PRComment before filtering in the comments loop, while preserving the
existing append behavior for conversation comments.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: f15990ed-b4f1-408e-bb08-602adadc22c1
📒 Files selected for processing (7)
commentfilter/commentfilter.gocommentfilter/commentfilter_test.goexecutor/merge.goexecutor/merge_test.gojobmanager/coordinator.goscanner/merge.goscanner/merge_test.go
Assisted-by: Claude Opus 4.6 (1M) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scanner/merge.go (1)
364-367: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winAvoid logging the raw command author username.
The author username is a user identifier and may constitute personal data. Remove it from routine logs or apply the project’s approved redaction/retention policy before emitting it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scanner/merge.go` around lines 364 - 367, Update the “Found sync command” logger call in the merge flow to stop emitting c.Author.Username, or apply the project’s approved redaction policy before logging it; preserve the repository and pull-request fields.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@scanner/merge.go`:
- Around line 364-367: Update the “Found sync command” logger call in the merge
flow to stop emitting c.Author.Username, or apply the project’s approved
redaction policy before logging it; preserve the repository and pull-request
fields.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 1af4596c-f812-489d-9041-98bb0c07f111
📒 Files selected for processing (1)
scanner/merge.go
|
@coderabbitai GitHub usernames are public identifiers — they are visible in every PR, commit, issue, and comment on the platform. Logging the author of a sync command is consistent with how this codebase already logs usernames throughout the feedback scanner, idle detection, and comment filtering paths (e.g., |
|
Tip For best results, initiate chat on the files or code changes.
✏️ Learnings added
|
Summary
@botname syncbot command on GitHub PR conversation comments that triggers mergingmaininto the dev branch, with AI-driven conflict resolution if neededmainwith no conflicts (so the MergeScanner doesn't trigger) but CI failures fixed onmainneed to be picked up<!-- addressed: ID -->pattern used by the feedback pipelineDesign
Detection: The
MergeScanner(opt-in viaWithMergeCommands()) checks conversation comments for@botname sync, filtering viaBotRepliedToto skip already-addressed commands. Conflicts take priority — if a PR is unmergeable, the existing conflict path runs without checking for commands.Feedback: The executor posts an initial reply ("Merging from main -> branch") with the addressed marker, then edits it with the outcome (success + commit SHA, failure + reason, or already up-to-date). Single comment, no noise.
Multi-repo:
commandSourceBaseBranchresolves the correct base branch from the repo the command was posted on, notRepos[0].Key changes
commentfilter: exportNormalizeUsername, addBotCommandRe/IsSyncCommandjobmanager: addCommandSourceonEvent/Job,MergeCommitSHAonJobResult; deep-copyCommandSourcein bothSubmitandsnapshotscanner/merge:WithMergeCommandsoption,findMergeCommandwithBotRepliedTodetectionexecutor/merge: reply lifecycle (postMergeCommandReply/updateMergeCommandReply), preflight failure acknowledgment viapostPreflightFailureReplyservices/github:PostIssueCommentreturns(int64, error)for reply IDTest plan
IsSyncCommandtest cases (case sensitivity, word boundaries, [bot] suffix, newlines)CommandSourcepropagation testsmake lintpasses (no new violations)Assisted-by: Claude noreply@anthropic.com
Summary
Adds an opt-in
@botname synccommand that can be discovered in GitHub PR conversation comments to trigger the merge workflow. The merge scanner mergesmaininto the PR’s development branch (using the existing AI-driven conflict resolution path when conflicts occur), but it submits only when it finds an unaddressed sync command (skipping commands already handled via<!-- addressed: <commentID> -->markers). For multi-repository setups it resolves the correct command base/target context, and it propagates the triggering GitHub comment identity through the job pipeline.The executor now manages a “single reply lifecycle”: it posts one addressed reply when a command-triggered merge starts, then updates that same comment with the outcome—success (including merge commit SHA), failure reason, “already up to date,” or an addressed reply for preflight failures. To enable reliable follow-up edits, GitHub comment creation now returns the created comment ID, and that ID is threaded through executor/merge reply logic.
Affected packages
NormalizeUsername), adds regex helpers (BotCommandReandIsSyncCommand) for detecting@<bot> syncwith matching/word-boundary and optional[bot]handling, and updates bot/ignored-bot comparisons to consistently use normalized usernames.scanner.WithMergeCommands()(merge-command detection opt-in). When enabled, it scans PR conversation comments for unaddressed@botname sync, selects the triggeringCommandSource, and includes it in merge events.CommandSourcemodel and propagatesEvent.CommandSourceintoJob.CommandSource(including snapshot cloning). AddsJobResult.MergeCommitSHAfor merge jobs that produce commits.PostIssueCommentnow returns(int64, error)), posts once then edits once via the triggeringCommandSource, reports merge commit SHA on success, and ensures addressed preflight failure acknowledgements.GitHubService.PostIssueCommentto return the created GitHub comment ID ((int64, error)), enabling executor reply editing.scanner.WithMergeCommands().Pipeline impact
Extends the PR merge pipeline by adding command-driven triggering via
@botname syncconversation comments, requiring unaddressed commands, and ensuring the triggering comment ID is carried end-to-end. Execution pipeline updates include a more robust reply lifecycle (post addressed reply once, then update with the merge outcome) and merge-result reporting that includes the resulting merge commit SHA.Infrastructure and configuration
No changes to container management, workspace lifecycle, or crash recovery. Configuration/deployment impact is limited to enabling the new scanner behavior in
main.goviascanner.WithMergeCommands().