Skip to content

Stop the release version constants failing the API compatibility gate - #941

Merged
jeremy merged 2 commits into
mainfrom
sdk-apidiff-version-consts
Sep 20, 2026
Merged

jeremy merged 2 commits into
mainfrom
sdk-apidiff-version-consts

Conversation

@jeremy

@jeremy jeremy commented Sep 20, 2026 •

Copy link
Copy Markdown
Member

Cutting v0.20.0 (#940) failed the required API Compatibility check with all 12 required contexts green. The entire finding:

- ./pkg/basecamp.DefaultUserAgent: value changed from "basecamp-sdk-go/0.19.0 (api:2026-09-02)" to "basecamp-sdk-go/0.20.0 (api:2026-09-02)"
- ./pkg/basecamp.Version: value changed from "0.19.0" to "0.20.0"

scripts/bump-version.sh writes const Version; DefaultUserAgent is "basecamp-sdk-go/" + Version + " (api:" + APIVersion + ")", so it follows by construction. Every release changes both, so every release PR fails this gate, and the printed remedy is to apply breaking and re-run.

Why this is worth changing rather than living with

apidiff is not wrong. It flags an exported constant's value change because a consumer can use one in a constant expression. For these two that's true and beside the point — a consumer pinning the literal is asserting "compiled against exactly 0.19.0", which no release can preserve. Changing is what the constant is for.

The argument for fixing it is the gate's own integrity. A required check that fails on every release trains "bump PR, slap the label on" as reflex, and it does so on the one PR where a real break is least likely to be noticed: the diff is machine-generated and reviewers skim it. breaking should mean a real break was looked at and accepted. It cannot mean that while it is also the routine cost of shipping.

What changed

The two lines are partitioned out of the verdict only. They are still printed, under their own heading, and the All API Changes report is untouched — nothing becomes invisible.

The exemption is deliberately narrow: only value changed, only those two symbols.

grep's exit 1 ("no lines matched") is an ordinary outcome for both partitions and must not abort under set -e; exit 2+ is a tool failure and must not be read as an empty partition, so it aborts loudly — the same distinction run_apidiff already draws a few lines above.

Verification

Ran the partition against the real #940 output and five neighbours:

case verdict
the two v0.20.0 version lines alone not breaking
those lines plus Client.GetProject: removed BREAKING
Version: removed BREAKING
Version: changed from untyped string to int BREAKING
APIVersion: value changed (a different constant) BREAKING
clean 0-byte report not breaking

The third through fifth rows are the ones that matter: the exemption keys on the symbol and the kind of change, so it cannot swallow a removal, a retype, or any other constant.

actionlint clean; zizmor (same online audit CI runs) reports 0 findings.

Not addressed here

Removing breaking after a green run still leaves that run green — the existing comment says so, and closing it needs a label-event trigger. Unchanged by this.


Summary by cubic

Stops release version bumps from failing the API compatibility gate. The expected Version and derived DefaultUserAgent changes no longer count as breaking, while unrelated API changes still block the check.

Behavior

  • Exempts DefaultUserAgent only when its old and new values exactly follow the Version change and retain the same API version.
  • Keeps removals, type changes, unrelated constant changes, and other breaking APIs in the verdict.
  • Prints exempted lines under a separate heading without changing the full “All API Changes” report.

Written for commit 0d3046e. Summary will update on new commits.

Review in cubic

Every release bump changes `Version`, and `DefaultUserAgent` is
`"basecamp-sdk-go/" + Version + " (api:" + APIVersion + ")"` so it follows.
apidiff classifies an exported constant's value change as incompatible,
which for these two is true and beside the point — a consumer pinning the
literal is asserting "compiled against exactly 0.19.0", and no release can
preserve that. Changing is what the constant is for.

The reason to fix it is the gate's integrity, not convenience. Cutting
v0.20.0 (#940) failed this required check with 12/12 contexts green and
both reported lines version constants; the printed remedy is to apply
`breaking` and re-run. Every release hits that, so the label becomes a
reflex on the one PR where a real break is least likely to be read — the
diff is machine-generated and skimmed. The label should mean a real break
was looked at and accepted, which it cannot mean while it is also the
routine cost of shipping.

The two lines are partitioned out of the verdict only. They are still
printed, under their own heading, and the full "All API Changes" report is
untouched. The exemption is narrow: only `value changed`, only those two
symbols. Removing `Version`, changing its type or renaming it still fails,
as does any other symbol's value change.

grep's exit 1 ("no lines matched") is an ordinary outcome for both
partitions and must not abort under `set -e`; 2 or above is a tool failure
and must not read as an empty partition, so it aborts loudly — the same
distinction run_apidiff already draws.

Verified against the real #940 output plus five neighbours: the two version
lines alone pass; those lines beside a removed method still fail; `Version`
removed, `Version` retyped and another constant's value change each still
fail; a clean 0-byte report stays clean. actionlint and zizmor clean.
Copilot AI balanced review requested due to automatic review settings September 20, 2026 07:44
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-20T07:51:29.058910Z 0d3046e New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown
Contributor

Sensitive Change Detection (shadow mode)

This PR modifies control-plane files:

  • .github/workflows/test.yml

Shadow mode — this check is informational only. When activated, changes to these paths will require approval from a maintainer.

@github-actions github-actions Bot added the github-actions Pull requests that update GitHub Actions label Sep 20, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The filter also suppresses arbitrary DefaultUserAgent value changes unrelated to releases.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Narrows Go API compatibility verdicts so routine release-version changes remain visible without failing the release gate.

Changes:

  • Separates release constants from other incompatible changes.
  • Preserves both dedicated and complete API reports.
  • Handles grep no-match and failure statuses distinctly.

[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

File Description
.github/​workflows/​test.yml Partitions version-constant findings from the breaking verdict.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/test.yml Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5674ccd7b4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/test.yml Outdated
Both reviewers found the same hole, independently: keying the exemption on
the symbol name waived every `DefaultUserAgent` value change, not only the
one a release bump causes. Rewriting the header to "unexpected-client", or
dropping the `(api:...)` component, emits the same generic `value changed`
line and would have passed the gate silently.

The user-agent line is now exempt only when it is exactly what this bump's
`Version` change implies: same `basecamp-sdk-go/` prefix, same API-version
component on both sides, version component moving from the old value to the
new one. A user-agent change with no `Version` change beside it is never
exempt.

Naming the format here as well as in client.go is deliberate. If the format
changes this stops matching and the gate fails, which is the direction a
duplicated fact should fail in.

awk rather than grep because the decision is no longer a line-level regex —
it reads the `Version` line to decide about the user-agent line — and awk
avoids the heredoc indentation trap inside a YAML block scalar.

Verified against seven cases including both reviewers' counterexamples: the
real v0.20.0 bump passes; user agent to "unexpected-client" with a version
bump fails; the API component dropped fails; a user-agent change with no
version bump fails; a bump beside a removed method fails; `Version: removed`
fails; a clean 0-byte report stays clean.

My own verification missed this. I tested the symbol axis and the
change-kind axis and never the value axis — the exemption was only ever
sound for values derived from the bump, and I never asserted the derivation.
@jeremy

jeremy commented Sep 20, 2026

Copy link
Copy Markdown
Member Author

Review threads: 2 resolved (2 fixed). Head is 0d3046e, all 12 required contexts green, MERGEABLE/CLEAN.

Both reviewers found the same defect independently, and they were right about it.

  • #discussion_r4056440284 and #discussion_r4056441039 — the first cut keyed the exemption on the symbol name, so it waived every DefaultUserAgent value change rather than only the one a release bump causes. Rewriting the header to "unexpected-client", or dropping the (api:...) component, emits the same generic value changed from line and would have passed the gate silently. That is a genuine exported-constant break waived by a check meant to catch exactly that.

Fixed in 0d3046e by keying on the value instead. The user-agent line is exempt only when it is exactly what this bump's Version change implies: same basecamp-sdk-go/ prefix, same API-version component on both sides, version component moving from the old value to the new one. A user-agent change with no Version line beside it is never exempt, and Version itself is exempt only as a value changed — removing, retyping or renaming it still fails.

Switched from grep to awk because the decision is no longer a line-level regex: it reads the Version line to judge the user-agent line.

Verification

Seven cases, including both reviewers' counterexamples verbatim:

case verdict
the real v0.20.0 bump not breaking
user agent → "unexpected-client" with a version bump BREAKING
(api:…) component dropped, with a version bump BREAKING
user-agent change with no version bump BREAKING
bump beside Client.GetProject: removed BREAKING
Version: removed BREAKING
clean 0-byte report not breaking

actionlint and zizmor clean.

Worth recording

My own verification missed this. I tested the symbol axis and the change-kind axis and never the value axis — and wrote "narrow on purpose" in the commit message while it wasn't narrow. The exemption was only ever sound for values derived from the bump, and the first version never asserted the derivation it depended on.

Reviewer state

Codex completed a clean pass on 0d3046e at 07:51Z. Copilot last reported on 5674ccd, the commit before the fix; its re-review cannot be requested by API, so it has not seen the correction. Both of its findings are the ones addressed above.

@jeremy
jeremy merged commit 58e0654 into main Sep 20, 2026
50 checks passed
@jeremy
jeremy deleted the sdk-apidiff-version-consts branch September 20, 2026 07:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

github-actions Pull requests that update GitHub Actions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants