Skip to content

ci: detect queue merges by replaying PR timeline queue membership#5483

Merged
brian-hussey merged 2 commits into
mainfrom
ci/slack-direct-merge-queue-detection
Jul 3, 2026
Merged

ci: detect queue merges by replaying PR timeline queue membership#5483
brian-hussey merged 2 commits into
mainfrom
ci/slack-direct-merge-queue-detection

Conversation

@madhu-mohan-jaishankar

@madhu-mohan-jaishankar madhu-mohan-jaishankar commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

🔗 Related Issue

Closes #390


📝 Summary

Fixes false-positive Slack notifications from the direct-merge-to-main detector introduced in #5479 / #5480.

The workflow's job is to alert #contextforge-merge-notifications only when a PR is merged to main while bypassing the merge queue. Earlier attempts tried to distinguish queue merges from bypass merges using single boolean-ish fields — none of which are reliable:

  • merged_by.typemerged_by is the human who queued the PR, not the queue bot.
  • auto_mergednull for queue merges (it only reflects the "Enable auto-merge" toggle).
  • timestamp equality of removed_from_merge_queue vs merged — fragile; a genuine bypass merge could coincidentally share a timestamp with a queue-removal event.

GitHub exposes no single field that says "this PR was merged by the queue." The only authoritative signal is the PR timeline. This PR replays the timeline chronologically, tracking merge-queue membership (added_to_merge_queue → in queue, removed_from_merge_queue → out of queue) and captures that state at the moment the merged event fires:

  • in queue at merge time → the queue merged it → skip (no Slack ping)
  • not in queue at merge time → a direct bypassnotify

For queue merges, GitHub emits merged before removed_from_merge_queue in the timeline, so the replay correctly sees the PR still in the queue. This ordering-based check is robust even when a bypass merge shares a timestamp with a queue-removal event. It also correctly handles the "queued → ejected → merged directly" case (a real bypass), which a naive "was it ever queued?" check would misclassify.


📏 Reviewability

  • This PR has one clear purpose
  • The linked issue is not labeled triage
  • Unrelated bugs or improvements are tracked in separate issues/PRs
  • Tests are included with the code they validate
  • If AI-assisted, I understand and can explain the generated changes

🏷️ Type of Change

  • Bug fix
  • Feature / Enhancement
  • Documentation
  • Refactor
  • Chore (deps, CI, tooling)
  • Other (describe below)

🧪 Verification

Detection logic was validated empirically by replaying the timeline of real merged PRs via gh api and asserting the classification:

PR Merge type merged_in_queue Expected action Result
#5354, #5479, #5480 Merge queue true skip
#5261, #5210 Direct to main (bypass) false notify
6 non-main direct merges Direct false notify

Command used per PR:

gh api "repos/IBM/mcp-context-forge/issues/<PR>/timeline?per_page=100" \
  --jq 'reduce .[] as $e ({in_queue:false, merged_in_queue:false};
          if   $e.event=="added_to_merge_queue"   then .in_queue=true
          elif $e.event=="removed_from_merge_queue" then .in_queue=false
          elif $e.event=="merged" then .merged_in_queue=.in_queue
          else . end) | .merged_in_queue'

Timeline event ordering (added_to_merge_queuemergedremoved_from_merge_queue) was confirmed by inspecting the raw array indices of queue-merged PRs.

Standard repo checks:

Check Command Status
Lint suite make lint ✅ (actionlint + check-yaml pass)
Unit tests make test N/A — CI workflow YAML, no unit-testable code
Coverage ≥ 80% make coverage N/A — no application code changed

This change is a GitHub Actions workflow (YAML + shell). It is exercised via workflow_dispatch and live PR events rather than the Python test suite; the detection logic was validated against real PR timelines as shown above.


✅ Checklist

  • Code formatted (make black isort pre-commit)
  • Tests added/updated for changes
  • Documentation updated (if applicable)
  • No secrets or credentials committed

📓 Notes (optional)

  • Why no single-field check works: GitHub does not provide a boolean for "merged by queue." merged_by is the human who queued the PR, auto_merged reflects an unrelated toggle, and the merge committer is web-flow (GitHub) for both queue and bypass merges — so none of these discriminate. The timeline replay is the only authoritative approach.
  • workflow_dispatch short-circuits the timeline lookup (no PR context) so the Slack integration can be verified end-to-end from any branch.
  • Companion workflow notify-slack-merge-queue-ejection.yml (merged in Ci/slack merge queue fixes #5480) handles the ejection case; both post to the same channel via SLACK_MERGE_NOTIFICATION_WEBHOOK_URL.
  • Post-merge: trigger both workflows via Actions → Run workflow to confirm Slack delivery.

Open the PR at: https://github.com/IBM/mcp-context-forge/compare/main...ci/slack-direct-merge-queue-detection?expand=1&template=bug_fix.mdOpen the PR at: https://github.com/IBM/mcp-context-forge/compare/main...ci/slack-direct-merge-queue-detection?expand=1&template=bug_fix.md

Signed-off-by: Madhu Mohan Jaishankar <madhu.mohan.jaishankar@ibm.com>
Signed-off-by: Madhu Mohan Jaishankar <madhu.mohan.jaishankar@ibm.com>

@brian-hussey brian-hussey left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@brian-hussey brian-hussey added this pull request to the merge queue Jul 3, 2026
Merged via the queue into main with commit 5f8a4c6 Jul 3, 2026
7 checks passed
@brian-hussey brian-hussey deleted the ci/slack-direct-merge-queue-detection branch July 3, 2026 10:03
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.

2 participants