fix(release): decide version from the actual diff, not just commit messages#87
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a change-context collector that captures git diffs between the previous release SHA and HEAD and rewrites AI/Codex prompts (publish and monorepo bump flows) to base MAJOR/MINOR/PATCH decisions primarily on diffs and per-package diffs, with truncation handling and explicit prerelease arithmetic. ChangesDiff-driven versioning changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
📄 README may need an updateThis PR introduces changes that might not be reflected in Reason:
|
There was a problem hiding this comment.
Pull request overview
This PR updates the shared determine-publish-version composite action’s Codex prompt to classify semantic version bumps based on the actual diff (and not just conventional-commit markers), enabling major bumps even when commit messages don’t explicitly mark breaking changes.
Changes:
- Rewrites the
AI Determine Versionprompt to be diff-first, while keeping explicitBREAKING CHANGE:/type!:markers authoritative. - Adds conservative guidance to avoid false-positive major bumps and to prefer lower bumps when ambiguous.
- Adds explicit semver bump arithmetic guidance relative to the current version.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
.github/blocks/determine-publish-version/action.yaml (2)
111-114: ⚡ Quick winClarify the conservative default behavior.
The conservative guardrail is good, but the phrasing "When genuinely unclear between two levels, pick the lower one (default to patch)" could be ambiguous:
- Does "pick the lower one" mean choose the smaller bump when comparing any two levels (e.g., minor vs. major → pick minor), or always default to patch?
- What should the AI do if it cannot determine ANY appropriate bump level?
📝 Proposed clarification
Be conservative about MAJOR to avoid false positives: purely additive changes are MINOR, not MAJOR. Only call it breaking if existing public behavior is actually removed or changed incompatibly. When genuinely -unclear between two levels, pick the lower one (default to patch). +unclear between two levels, pick the lower one (e.g., if unclear between +minor and patch, choose patch). If you cannot determine any changes at all, +default to patch.🤖 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 @.github/blocks/determine-publish-version/action.yaml around lines 111 - 114, The guideline string "When genuinely unclear between two levels, pick the lower one (default to patch)" is ambiguous; revise the text in .github/blocks/determine-publish-version/action.yaml to explicitly state that "pick the lower one" means choose the less-severe version bump when comparing two options (e.g., prefer minor over major, patch over minor) and that if the AI cannot determine any appropriate bump level at all it must default to a patch; replace the existing sentence with a clear two-part sentence such as: "If choosing between two levels, select the less-severe bump (e.g., minor over major); if no clear bump can be determined, default to patch." Ensure the edited sentence replaces the original conservative-default line exactly where that guidance appears.
95-109: 💤 Low valueConsider adding project-type-specific guidance for breaking changes (optional).
The decision logic is sound and comprehensive, but the examples of backward-incompatible changes (lines 99-102) are generic. Since this action is shared across Swift/Go/Rust/TS release workflows (per the PR objectives), different project types have different public contracts:
- REST APIs: endpoint changes, request/response schemas
- Libraries: exported symbols, function signatures, package exports
- CLI tools: command syntax, flag changes, output format
- Configuration: config file schema, environment variables
While the current generic examples work, you might reduce false positives by adding project-type-specific markers or examples if the AI can detect the project type.
💡 Example: conditional guidance based on project type
How to choose the bump (apply in order): 1. MAJOR — if any commit message carries an explicit `BREAKING CHANGE:` footer or a `type!:` marker, that is authoritative: bump major. ALSO bump major if the DIFF itself is backward-incompatible even when no commit said so — e.g. a public endpoint/route/exported symbol is removed or renamed, a response/request schema or required field changes shape, default behavior changes in a way that breaks existing callers, or a config/runtime contract changes. You are explicitly allowed to bump the major from the diff alone. + + For libraries: removing/renaming exported functions, changing signatures, + removing public classes/structs, changing module exports. + For APIs: removing/renaming endpoints, changing request/response shape, + adding required fields. + For CLIs: changing command syntax, removing flags, changing output format.🤖 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 @.github/blocks/determine-publish-version/action.yaml around lines 95 - 109, Update the "How to choose the bump" guidance block to include optional project-type-specific examples for what counts as a breaking MAJOR change; keep the existing rules and add short conditional notes referencing the existing markers (`BREAKING CHANGE:`, `type!:` and the word "DIFF") with examples for REST APIs (endpoints, request/response schema), Libraries (exported symbols, function signatures), CLI tools (commands/flags/output format) and Configuration (config schema, env vars) so the AI can narrow detection by project type when evaluating DIFFs.
🤖 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 @.github/blocks/determine-publish-version/action.yaml:
- Around line 91-93: The prompt claims the model will "inspect the diff itself"
but openai/codex-action@v1 doesn't inject git diffs automatically; update the
workflow so the actual git diff/log between the SHAs computed earlier is
generated and passed into the Codex action (for example run a step that runs git
diff <base>..<head> or git log --oneline <base>..<head>, save output to a file
or env var, and set CODEX_PROMPT_FILE or CODEX_PROMPT to include that
file/contents) or alternatively reword the CODEX_PROMPT/CODEX_PROMPT_FILE to
remove any claim that the diff is present; locate the CODEX_PROMPT /
CODEX_PROMPT_FILE usage and the openai/codex-action@v1 invocation and ensure the
diff/log output from the prior step is injected into the action inputs.
---
Nitpick comments:
In @.github/blocks/determine-publish-version/action.yaml:
- Around line 111-114: The guideline string "When genuinely unclear between two
levels, pick the lower one (default to patch)" is ambiguous; revise the text in
.github/blocks/determine-publish-version/action.yaml to explicitly state that
"pick the lower one" means choose the less-severe version bump when comparing
two options (e.g., prefer minor over major, patch over minor) and that if the AI
cannot determine any appropriate bump level at all it must default to a patch;
replace the existing sentence with a clear two-part sentence such as: "If
choosing between two levels, select the less-severe bump (e.g., minor over
major); if no clear bump can be determined, default to patch." Ensure the edited
sentence replaces the original conservative-default line exactly where that
guidance appears.
- Around line 95-109: Update the "How to choose the bump" guidance block to
include optional project-type-specific examples for what counts as a breaking
MAJOR change; keep the existing rules and add short conditional notes
referencing the existing markers (`BREAKING CHANGE:`, `type!:` and the word
"DIFF") with examples for REST APIs (endpoints, request/response schema),
Libraries (exported symbols, function signatures), CLI tools
(commands/flags/output format) and Configuration (config schema, env vars) so
the AI can narrow detection by project type when evaluating DIFFs.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 17f4558e-463c-420b-957d-6f5555eb0ee5
📒 Files selected for processing (1)
.github/blocks/determine-publish-version/action.yaml
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: copilot-pull-request-reviewer
- GitHub Check: check-skills / check-skills
- GitHub Check: check-readme / check-readme
🔇 Additional comments (3)
.github/blocks/determine-publish-version/action.yaml (3)
86-89: LGTM!
116-121: LGTM!
123-123: LGTM!
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/blocks/bump-monorepo-versions/action.yaml (1)
104-125:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd a total prompt budget, not just a per-package diff cap.
CAP=40000bounds one package, butPROMPT_PARTSis still unbounded across all changed packages. A large monorepo release can exceed the step-output/action-input budget or push later packages out of context, which makes the version decisions nondeterministic across package count.Suggested fix
PKG_COUNT=$(echo "$CHANGED_JSON" | jq 'length') PROMPT_PARTS="" + TOTAL_CAP=120000 + TOTAL_LEN=0 @@ - PROMPT_PARTS="${PROMPT_PARTS} + PART=" Package: ${NAME} Current version: ${CURRENT_VER} Path: ${PKG_PATH} Commits: ${COMMITS} Files changed (A=added M=modified D=deleted R=renamed): ${NAME_STATUS} Diff: ${PKG_DIFF} - ---" + ---" + + if [ $(( TOTAL_LEN + ${`#PART`} )) -gt "$TOTAL_CAP" ]; then + PART=" + Package: ${NAME} + Current version: ${CURRENT_VER} + Path: ${PKG_PATH} + Commits: + ${COMMITS} + Files changed (A=added M=modified D=deleted R=renamed): + ${NAME_STATUS} + Diff: + (omitted: total prompt budget exceeded; inspect with git diff ${LAST_SHA}..HEAD -- ${PKG_PATH}) + ---" + fi + + PROMPT_PARTS="${PROMPT_PARTS}${PART}" + TOTAL_LEN=$(( TOTAL_LEN + ${`#PART`} ))🤖 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 @.github/blocks/bump-monorepo-versions/action.yaml around lines 104 - 125, The loop currently enforces a per-package CAP (CAP=40000) on PKG_DIFF but never bounds the cumulative PROMPT_PARTS, risking exceeding action output/input budgets; add a total prompt budget (e.g., TOTAL_CAP) and a running counter (e.g., PROMPT_LEN) that sums lengths of PROMPT_PARTS plus each package block before appending, and if adding the next package would exceed TOTAL_CAP truncate that package's content (or skip later packages) and append a clear truncation notice to PROMPT_PARTS; update the part that builds PROMPT_PARTS (the block referencing CAP, PKG_DIFF, PROMPT_PARTS, NAME, CURRENT_VER, PKG_PATH, COMMITS, NAME_STATUS) to consult PROMPT_LEN/TOTAL_CAP and ensure the final echo to GITHUB_OUTPUT (using EOF_MARKER) only writes the bounded PROMPT_PARTS.
🤖 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 @.github/blocks/bump-monorepo-versions/action.yaml:
- Around line 102-103: The workflow currently swallows git diff errors by
appending "|| true" to the commands that populate NAME_STATUS and PKG_DIFF;
change the logic so failures surface and cause the job to fail instead of
producing empty diffs—remove the "|| true" fallback from the git diff
invocations that set NAME_STATUS and PKG_DIFF, and add explicit error handling
after those commands (e.g., test the exit status and exit 1 with a clear message
if git diff failed) so any git error aborts the workflow and surfaces the
package that caused the failure.
In @.github/blocks/determine-publish-version/action.yaml:
- Around line 166-169: The workflow is doing semver arithmetic directly on
steps.last-release.outputs.version which may include a prerelease suffix (e.g.
2.0.0-rc.3); change the logic to parse the version into base and prerelease
components (strip or parse the prerelease with a semver-aware parser), perform
major/minor/patch increments on the base version only (e.g. baseVersion =
semver.parse(steps.last-release.outputs.version).version), and then reapply
prerelease handling separately when the intent is to publish a prerelease;
update the parser/classifier that consumes steps.last-release.outputs.version to
use the separated baseVersion and prerelease parts instead of doing math on the
raw prerelease tag.
- Around line 83-98: The PREV_SHA fallback to the repo’s oldest commit excludes
the first commit from the ".." ranges; detect the initial-release case by
computing FIRST_SHA=$(git rev-list --max-parents=0 HEAD) and if PREV_SHA ==
FIRST_SHA adjust the git range so the first commit is included (e.g. use
"$PREV_SHA^..$HEAD_SHA" when a parent exists, otherwise fall back to using
full-history variants like git log --no-merges --format='...' "$HEAD_SHA" or
explicitly prepend the FIRST_SHA commit info); update the NAME_STATUS, STAT, LOG
and DIFF assignments that reference PREV_SHA/HEAD_SHA to use this adjusted range
so the initial commit is present in the prompt.
---
Outside diff comments:
In @.github/blocks/bump-monorepo-versions/action.yaml:
- Around line 104-125: The loop currently enforces a per-package CAP (CAP=40000)
on PKG_DIFF but never bounds the cumulative PROMPT_PARTS, risking exceeding
action output/input budgets; add a total prompt budget (e.g., TOTAL_CAP) and a
running counter (e.g., PROMPT_LEN) that sums lengths of PROMPT_PARTS plus each
package block before appending, and if adding the next package would exceed
TOTAL_CAP truncate that package's content (or skip later packages) and append a
clear truncation notice to PROMPT_PARTS; update the part that builds
PROMPT_PARTS (the block referencing CAP, PKG_DIFF, PROMPT_PARTS, NAME,
CURRENT_VER, PKG_PATH, COMMITS, NAME_STATUS) to consult PROMPT_LEN/TOTAL_CAP and
ensure the final echo to GITHUB_OUTPUT (using EOF_MARKER) only writes the
bounded PROMPT_PARTS.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 8e482fb2-ed36-47f4-b989-8918ad7c9233
📒 Files selected for processing (2)
.github/blocks/bump-monorepo-versions/action.yaml.github/blocks/determine-publish-version/action.yaml
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: copilot-pull-request-reviewer
- GitHub Check: check-skills / check-skills
…commit messages The AI version blocks keyed the major bump only on conventional-commit markers (BREAKING CHANGE: / type!), which authors rarely write, so breaking releases shipped as minor/patch (e.g. BlueStatus #11 -> 0.2.0 despite removed endpoints). - Inject the diff into the prompt. openai/codex-action is an agentic `codex exec` that does not auto-attach a diff (on the v0.2.0-rc.11 run the agent only ran `git log`), so a new step pre-computes name-status + churn + the (capped) diff and embeds it; the version is decided on real code now. - Diff-first rules: explicit markers stay authoritative; otherwise the agent may bump the major from a backward-incompatible diff alone. Conservative guardrail keeps purely additive changes minor. - Compute the version and diff range from the last STABLE release, so rc-to-rc publishes stay on one core line instead of advancing off a prerelease tag. - Initial release (no stable) diffs against the empty tree so the root commit is included. - Fail fast (set -euo pipefail, no `|| true` masking) and cap every section so the 1 MB/job GITHUB_OUTPUT budget can't be blown. - effort: high. Same fixes applied to bump-monorepo-versions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6e258d1 to
5533154
Compare
📚 Skills documentation may need an updateThis PR introduces changes that might not be reflected in the skills documentation. Reason: This PR adds two new public BuildSpace CI/CD blocks with agent-relevant inputs, outputs, and semantics (
|
| clip() { if [ "${#1}" -gt "$2" ]; then printf '%s\n(...truncated at %s chars; run git diff for the rest...)' "${1:0:$2}" "$2"; else printf '%s' "$1"; fi; } | ||
| NAME_STATUS=$(clip "$(git diff --name-status "$DIFF_BASE" "$HEAD_SHA")" 60000) | ||
| STAT=$(clip "$(git diff --stat "$DIFF_BASE" "$HEAD_SHA")" 40000) | ||
| LOG=$(clip "$(git log --no-merges --format='- %s%n%b' $LOG_RANGE)" 100000) | ||
| DIFF=$(clip "$(git diff "$DIFF_BASE" "$HEAD_SHA")" 400000) |
| COMMITS=$(git log --oneline "$BASE..HEAD" -- "$PKG_PATH") | ||
| NAME_STATUS=$(git diff --name-status "$BASE..HEAD" -- "$PKG_PATH") | ||
| PKG_DIFF=$(git diff "$BASE..HEAD" -- "$PKG_PATH") | ||
| CAP=100000 | ||
| if [ "${#PKG_DIFF}" -gt "$CAP" ]; then | ||
| PKG_DIFF="${PKG_DIFF:0:$CAP} | ||
| (diff truncated at ${CAP} chars — run: git diff $BASE..HEAD -- ${PKG_PATH} for the full diff)" | ||
| fi |
| PROMPT_PARTS="${PROMPT_PARTS} | ||
| Package: ${NAME} | ||
| Current version: ${CURRENT_VER} | ||
| Path: ${PKG_PATH} | ||
| Commits: |
Problem
The AI version blocks keyed the major bump only on conventional-commit markers (
BREAKING CHANGE:/type!:). Almost nobody writes commits that way, so breaking releases shipped as minor/patch.Confirmed on the v0.2.0-rc.11 run (BlueStatus #11, run
27070096504): the agent (codex exec, gpt-5.5,read-only,effort: none) ran onlygit logover the range — nevergit diff— saw afeat:title, and emitted0.2.0. The breaking changes (removed endpoints, daemon→agent, response-shape changes) were invisible to it.Fix
Decide the version from the actual code diff, guaranteed.
openai/codex-actionis agentic but does not auto-attach a diff, so "inspect the diff" in a prompt is not guaranteed to happen (it didn't). So:determine-publish-version— newGather change contextstep pre-computesgit diff --name-status+--stat+ the full diff (capped, agent can pull truncated files via read-only git) between the SHAs and injects it into the prompt. The model now decides on real code regardless of what it chooses to run.effort: xhigh— judging backward-compat from a diff is a real reasoning task; it runs once per release.bump-monorepo-versions— had the identical commit-message-only logic. Same fix: its existing per-package context gather now includes the per-package diff, diff-first rules,effort: xhigh.Classify Bumpalready maps a1.0.0result to major against the last stable; the-rc.Nsuffix is untouched.Scope / risk
Shared blocks — inherited by every Swift/Go/Rust/TS (+ monorepo) release. Version selection was already AI-driven and non-deterministic; this widens what the agent may conclude (major from diff) and guarantees it sees the diff, rather than changing the mechanism. False-positive major bumps are possible; the guardrail + conservative-when-unclear rule limit them. Tradeoff accepted in discussion.
Verify post-merge
First release run: check the
prepare-releaselog — the prompt now contains the## Full diffblock, andRaw AI version outputshould track the diff's real impact.Resolves the CodeRabbit (diff-not-injected) and Copilot (be-explicit-about-obtaining-diff) review threads.
🤖 Generated with Claude Code
Summary by CodeRabbit
-rc) handling.