Announce public API changes from main only - #7500
Conversation
The #feed-sdk-new-api posts were noisy because every PR run announced. Only main posts now, so each change lands in the feed once. On main the merge base is HEAD itself, which would compare the commit against itself and report nothing, so main compares against HEAD^. PRs and release branches keep the merge base, the inline comment and the breaking-change gate. main skips the gate, it has no PR to carry the label and the PR run already enforced it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017vZGFWc7FkaZjuzSDdqzDp
The message now says the change landed on main and links the commit, which already carries the PR link. Dropping the source entirely was not an option: last_announcement bails on an empty source, so a rerun of the same main job would post a second time. The commit sha is a better key than the PR link anyway, it is unique per merge. Also trims the comments added in this branch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017vZGFWc7FkaZjuzSDdqzDp
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d688eb7. Configure here.
| changed = unbroken_modifications(modifications, breaks) | ||
| headline = if breaks.any? | ||
| gate_blocked?(breaks, labels) ? ":warning: *Breaking public API changes*" : ":warning: *Breaking public API changes* (allowed by label)" | ||
| gate_blocked?(breaks, labels) ? ":warning: *Breaking public API landed on main*" : ":warning: *Breaking public API landed on main* (allowed by label)" |
There was a problem hiding this comment.
Breaks always look unapproved
Low Severity
Slack announcements now run only on main, where pr_labels_for_api_gate always returns no labels. slack_summary still picks its breaking headline via gate_blocked?, so intentional pr:breaking-api landings always post the unapproved wording and never the allowed-by-label variant.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit d688eb7. Configure here.
There was a problem hiding this comment.
Since we only merge approved prs, and only post merged to main, then all posts are approved 🤷


Motivation
#feed-sdk-new-apiis noisy: every PR run gets posted so a single change shows up in the feed once per push. Posting from main instead means each change lands there once, when it's actually about to get shipped.AI session context
AI Context
Metadata
facu/api-diff-slack-main-onlyGoal
Stop
#feed-sdk-new-apifrom being spammed by per-PR-run announcements, without weakening the PR-time public API checks.Initial Prompt
"we're posting api changes on #feed-sdk-new-api but it's noisy. Can you make this only run on main? Whenever something gets merged"
(The session opened with a separate question about whether the API check should be skipped when a PR is behind its base branch. Answer: no, the gate is already merge-base anchored, so staleness cannot produce false positives. No change came out of that thread.)
Important Follow-up Prompts
HEAD^as the base on main.check-api-changesjob stays on PRs.resolve_comparison_basewas too verbose; the comments added in this branch were trimmed to one line each.Agent Contribution
check-api-changes-*already runs in both the PR workflow andrelease-or-main(.circleci/default_config.yml:2309,:2757), so no CircleCI change was needed.last_announcementbails on an emptysource, so dropping the link outright would have silently disabled duplicate suppression.Human Decisions
check-api-changesjob on PRs. The agent offered dropping it entirely as an option; rejected.Key Implementation Decisions
HEAD^as the comparison base on main, merge base everywhere else.origin/main's tip on PRs. That is what the merge base already avoids, and it would reintroduce false positives on stale branches.pr_labels_for_api_gatereturns[]without PR context, so once main had a real diff, every intentionalpr:breaking-apichange would have turned main red. PR runs are unchanged and still fail.sourceis the commit link.last_announcementreturns nil on an empty source. The sha is a better dedup key than the PR link, it is unique per merge.Files / Symbols Touched
fastlane/api_diff_helper.rbresolve_comparison_base,resolve_previous_commit,current_branch,main_branch?,slack_summary,BASE_SWIFTINTERFACE_DIR(renamed fromMERGE_BASE_SWIFTINTERFACE_DIR)HEAD^is the right base on main, and whetherCIRCLE_BRANCHis reliable there.fastlane/Fastfileon_main, link the commit.check_api_changes,api_gate_commit_link(replacesapi_gate_pr_link)announcement = if on_mainbranch returns{ fingerprint: nil, notice: nil }so the PR comment keeps its shape.fastlane/api_diff_helper_test.rbtest_comparison_base_on_main_is_the_previous_commit,test_comparison_base_off_main_stays_the_merge_base,test_slack_is_announced_only_on_main,test_the_breaking_change_gate_cannot_redden_main,test_the_announcement_source_is_the_commit, and 3 others.Dependencies / Config / Migrations
Validation
ruby fastlane/api_diff_helper_test.rbagainst the pre-change implementation: 146 runs, 3 failures, 5 errors (RED)ruby fastlane/api_diff_helper_test.rbafter: 146 runs, 497 assertions, 0 failures, 0 errors (GREEN)ruby -c fastlane/Fastfile,ruby -c fastlane/api_diff_helper.rb: Syntax OKslack_summarylocally to check the message shape.origin/main..circleci/default_config.yml, soHEAD^resolves undercheckout.Not capturedat time of writing.Validation Gaps
Fastfile, not by executing it. First real proof is the first main run after merge.CIRCLE_BRANCHis notmainthere, the feed goes silent rather than noisy, which fails safe but silently.Review Focus
CIRCLE_BRANCHreliablymainonrelease-or-mainruns? The whole change hangs on it.HEAD^: does anything merge to main without the swiftinterface baselines regenerated? If so the main-side diff attributes the drift to the wrong commit.release/*also runsrelease-or-mainand now announces nothing. Intended?Risks / Reviewer Notes
api/*.swiftinterface, soHEAD^vsHEADmisattributes the drift.check_api_changesfails any PR whose committed baselines don't match its build, so this requires the check to be bypassed.Not run. Worth a manual check on the first merge after this lands.Non-goals / Out of Scope
check-api-changesfrom PR runs. Explicitly rejected by the author.pr:breaking-apigate on PRs.api_slack_unreachable_notice/slack_credentials_reachable?/SLACK_UNREACHABLE_NOTICEare now near-dead, they only render in a PR comment and PRs no longer announce. Left in place to keep the diff tight, flagged as a follow-up.Omitted Context
Note
Medium Risk
Behavior depends on
CIRCLE_BRANCHbeingmainon post-merge CI and onHEAD^being the right baseline for squash merges; mis-detection silences Slack or misattributes API drift without failing main on intentional breaks.Overview
#feed-sdk-new-api posts only when
check_api_changesruns on main, so each merge gets one Slack message instead of one per PR push. PR runs still diff baselines, update GitHub comments, and enforcepr:breaking-api; they skip Slack and return a stub announcement shape for the comment flow.On main, the public-API gate compares
HEADvsHEAD^(merge base would be empty). Off main it still uses the merge base withorigin/main. Slack copy says changes landed on main, and the link is the commit (not the PR) for dedup on reruns. Breaking-change failures are downgraded to a log on main because there is no PR label context.Reviewed by Cursor Bugbot for commit 023e05b. Bugbot is set up for automated code reviews on this repo. Configure here.