Skip to content

Add executable release/* promotion path + CI-tip gate (#4000 follow-up)#4122

Merged
justin808 merged 36 commits into
mainfrom
jg/4000-release-promotion-path
Jun 24, 2026
Merged

Add executable release/* promotion path + CI-tip gate (#4000 follow-up)#4122
justin808 merged 36 commits into
mainfrom
jg/4000-release-promotion-path

Conversation

@justin808

@justin808 justin808 commented Jun 19, 2026

Copy link
Copy Markdown
Member

Summary

Follow-up to #4000 / #4018 for the release-train branching model. This PR makes the documented in-place RC-to-stable promotion path executable: a stable release can be promoted from the matching release/X.Y.Z branch, and the release CI gate validates the branch being released instead of always checking origin/main.

Why

Before this PR, the release-train runbook described a path that the release task could not safely execute. The task was still shaped around main, which made final promotion from the last good RC either blocked or dependent on manual bypasses. This PR keeps the scope to that release-critical path rather than adding forward-port automation.

Implementation

  • Allows stable rake release[X.Y.Z] from main or exactly release/X.Y.Z, while rejecting cross-line release branches.
  • Validates CI on origin/release/X.Y.Z when releasing from a release branch, including forced release-branch fetches and non-overridable local/remote HEAD identity checks outside dry-run mode.
  • Requires release-branch stable promotion to start from the accepted remote RC tag or metadata-only finalization commits.
  • Supports idempotent retry when the final stable tag already points at the current release branch HEAD.
  • Adds release-branch npm publish args so stable publishes from release/* remain usable.
  • Adds release/** push triggers, deleted-push guards, and selector full-matrix behavior for release branch CI.
  • Updates release-train docs and contributor CI notes for the new behavior.

Review Closeout

  • Blocking full-CI trigger review thread resolved after cad5895c5.
  • Current-head Claude review items fixed in 70403c774; remaining advisory nits were auto-deferred and resolved to avoid another nit-only review cycle.
  • Priority P1/P2 findings have explicit fixed dispositions in the merge ledger evidence.
  • Required workflow follow-up issue: Follow-up: Exercise GitHub Actions changes from PR #4122 #4172
  • Decision points: 0 during this cleanup pass.
  • Merge authority: delegated by maintainer in the 2026-06-24 Codex session, conditioned on documenting merge reasons in this PR description.

Validation

  • actionlint on all edited workflow files: passed.
  • Ruby YAML parse of the 10 edited workflow/action YAML files: passed.
  • cd react_on_rails && bundle exec rspec spec/react_on_rails/release_rake_helpers_spec.rb: 197 examples, 0 failures.
  • cd react_on_rails && BUNDLE_GEMFILE=../Gemfile bundle exec rubocop --cache false ../rakelib/release.rake spec/react_on_rails/release_rake_helpers_spec.rb --format simple: 2 files inspected, no offenses.
  • pnpm start format.listDifferent: passed.
  • script/ci-changes-detector origin/main: selected full suite, no benchmarks.
  • Pre-commit hooks: passed.
  • Pre-push hooks: branch RuboCop and online Markdown links passed.
  • .agents/skills/pr-batch/bin/pr-ci-readiness 4122 --repo shakacode/react_on_rails: READY.
  • script/pr-merge-ledger 4122 --repo shakacode/react_on_rails --changelog-classification not_user_visible --finding-dispositions /tmp/pr-4122-finding-dispositions.json --strict --pretty: complete_allowed: true.

AI review note: local Codex review found release-promotion issues earlier in the cleanup and those were fixed. Later codex review --base origin/main was blocked by the Codex review usage limit, and claude ultrareview origin/main --timeout 10 fallback was blocked by repeated HTTP 502s. GitHub Claude review comments on the current head have been handled or auto-deferred with rationale.

Changelog

not_user_visible - release tooling, CI routing, and contributor documentation only.

Confidence Note

Refs #4000, #4018


Note

High Risk
Changes release promotion, CI gating, and npm publish behavior on release branches—release-critical paths with large release.rake surface area; mitigated by extensive specs but post-merge workflow exercise is still tracked separately.

Overview
Makes the release-train in-place RC → stable path real: stable rake release[X.Y.Z] can run from main or the matching release/X.Y.Z branch, with guards so you cannot promote across release lines or without the accepted remote RC.

rakelib/release.rake now validates CI on origin/release/X.Y.Z (not always main), enforces local/remote release-branch HEAD alignment (non-overridable outside dry-run), walks back metadata-only finalization commits for the gate, and tightens version policy for release-branch finals (e.g. newer prerelease tags on another line). NPM publish args support stable publishes from release/*.

GitHub Actions add release/** push triggers on hosted workflows, skip deleted branch pushes, and extend hosted-ci-selectors so release-branch pushes count as release targets for hosted CI and broad matrices.

Contributor docs (AGENTS.md, runbook, CI optimization) are updated to match. Spec coverage in release_rake_helpers_spec.rb is expanded heavily.

Reviewed by Cursor Bugbot for commit 70403c7. Bugbot is set up for automated code reviews on this repo. Configure here.

Merge Authority Update - 2026-06-24

Maintainer merge authority was delegated in the current Codex session with the condition that merge reasons are documented in the PR description. Reasons for merge:

  • Release tracker Release gate: react_on_rails 17.0.0 #3823 is in Mode: development; this PR targets main, so the beta/main gate applies.
  • Current head 4b3714c0c1363bb40927c2eb593c66f01407bb01 is non-draft, mergeable, and mergeStateStatus is CLEAN.
  • pr-ci-readiness is READY; full gh pr checks shows current-head CI complete with no failures or pending checks.
  • GraphQL review-thread check reports zero unresolved review threads.
  • Strict merge ledger rerun on 2026-06-24 reports complete_allowed: true with no violations or unknown fields (/tmp/pr-4122-merge-ledger-20260624.json).
  • Changelog classification remains not_user_visible because this is release tooling, CI routing, and contributor documentation.
  • Post-merge GitHub Actions exercise remains tracked in Follow-up: Exercise GitHub Actions changes from PR #4122 #4172, which is the required follow-up for workflow behavior that can only be proven after merge.
  • The earlier SECURITY_PREFLIGHT_BLOCKED finding was explicitly acknowledged/waived by the maintainer before this closeout resumed.

Confidence note:

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The PR extends the release gate in rakelib/release.rake to evaluate CI status against the tip of origin/release/X.Y.Z when releasing from a release branch instead of always using origin/main, adds stable_release_branch_allowed? and release_ci_branch helpers, threads ci_branch through all related helpers and violation formatters, and gates the address-review skill's autonomous optional-nit handling on RELEASE_PHASE derived from the PR base branch. Documentation and specs are updated throughout.

Changes

CI-branch-aware release gate

Layer / File(s) Summary
Branch policy and CI-branch selection helpers
rakelib/release.rake
Adds uri require for URI encoding, introduces stable_release_branch_allowed? (restricts stable releases to main or release/<target>), and release_ci_branch (selects release/<X.Y.Z> tip vs main for CI evaluation). Also adjusts npm_publish_base_args to conditionally include --publish-branch when releasing from release/.
Core CI gate method parameterization
rakelib/release.rake
Updates fetch_main_ci_checks, required_check_names_for_main, format_main_ci_status_violation, and validate_main_ci_status! to accept ci_branch, fetching origin/<ci_branch> tip SHA and querying branch-protection checks for the selected branch with URI-encoded names.
:release task guard and violation call sites
rakelib/release.rake
Replaces the prior stable-from-main-only guard with stable_release_branch_allowed?, computes ci_branch via release_ci_branch, passes it into validate_main_ci_status!, updates all format_main_ci_status_violation call sites, refactors npm publish argument setup, and revises rake release help text to describe stable-release and CI-evaluation behavior for standard vs release-branch promotions.
Specs: ci_branch override coverage
react_on_rails/spec/react_on_rails/release_rake_helpers_spec.rb
Adds tests verifying that validate_main_ci_status! reports violations referencing origin/release/<branch>, fetch_main_ci_checks resolves the release-branch SHA, required_check_names_for_main URI-encodes the branch name, npm_publish_base_args handles prerelease and release-branch publishing, stable_release_branch_allowed? accepts/rejects stable release sources, and release_ci_branch maps branches correctly.
Docs: CI branch gating and stable promotion
AGENTS.md, internal/contributor-info/release-train-runbook.md
Updates the "Final" promotion rule, RC cut step, stable promotion step, close-out forward-port note, and gate-skill behavior section to reflect branch-aware CI validation and stable_release_branch_allowed? guard.

Release-phase-aware address-review skill

Layer / File(s) Summary
Skill: Phase resolution and nit-handling gate
.agents/skills/address-review/SKILL.md
Updates the Maintainer Attention Contract to restrict autonomous optional-nit handling to RELEASE_PHASE=beta, adds Step 2.5 to resolve RELEASE_PHASE via gh pr view from the PR base branch, revises Step 7 and quick-action descriptions to reflect beta-only autonomy, updates action f pre-reply subflow to gate autonomous optional handling, and updates triage-output examples with conditional phase language.
Workflow: Phase-conditional optional routing
.agents/workflows/address-review.md
Updates optional-item routing to branch on resolved RELEASE_PHASE, adds matching Step 2.5 to resolve the phase from PR base branch, rewords quick-action descriptions for f and f+i to indicate low-risk optional nits are handled only when RELEASE_PHASE=beta, updates f action execution flow to gate autonomous OPTIONAL processing on RELEASE_PHASE=beta, and revises triage-output examples with conditional phase language.

Sequence Diagrams

sequenceDiagram
  participant ReleaseTask as :release task
  participant Validate as validate_main_ci_status!
  participant Fetch as fetch_main_ci_checks
  participant GitHub as GitHub API
  ReleaseTask->>Validate: ci_branch: "release/X.Y.Z" or "main"
  Validate->>Fetch: ci_branch: "release/X.Y.Z"
  Fetch->>GitHub: git fetch origin/release/X.Y.Z
  Fetch->>GitHub: check-runs for resolved SHA
  GitHub-->>Fetch: check run results
  Fetch-->>Validate: checks
  Validate->>GitHub: branch-protection required checks for ci_branch (URI-encoded)
  GitHub-->>Validate: required check names
  Validate-->>ReleaseTask: pass or abort with origin/release/X.Y.Z in message
Loading
flowchart LR
  A["address-review Step 2.5:<br/>gh pr view --json baseRefName"] --> B{base branch?}
  B -->|"release/*"| C["RELEASE_PHASE = rc/final"]
  B -->|"main / other"| D["RELEASE_PHASE = beta"]
  C --> E["Suppress autonomous<br/>optional-nit rule"]
  D --> F["Allow autonomous<br/>optional-nit handling"]
  E --> G["Action f:<br/>skip autonomous OPTIONAL step"]
  F --> H["Action f:<br/>run autonomous OPTIONAL step"]
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • shakacode/react_on_rails#3987: Both PRs modify the address-review action subflows for f/f+i to change how low-risk optional nits are handled autonomously (retrieved narrows autonomy scope to f/initial f+i, while main further gates that autonomy to RELEASE_PHASE=beta and suppresses it for rc/final).
  • shakacode/react_on_rails#4018: Both PRs implement the same release-phase–tiered gating model (rc/final on release/* vs beta on main), with the main PR wiring that phase resolution into address-review's optional-nit autonomy decisions while the retrieved PR defines the shared Release Phase Gate/merge-gate contract used across other skills.
  • shakacode/react_on_rails#4070: Both PRs modify rakelib/release.rake's CI gate logic—particularly fetch_main_ci_checks/how the CI evaluation SHA/checks are determined—so the main PR's release gating changes overlap with the retrieved PR's release CI commit-selection changes.

Suggested labels

enhancement, force-full-hosted-ci, P2

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: adding executable release/* promotion path and CI-tip gate, which directly corresponds to GAP 1 implementation in the PR objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg/4000-release-promotion-path

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issues.

Reviewed by Cursor Bugbot for commit 19c7059. Configure here.

Comment thread .agents/skills/address-review/SKILL.md Outdated
Comment thread .agents/workflows/address-review.md Outdated
Comment thread .agents/skills/address-review/SKILL.md 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: 19c7059c27

ℹ️ 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 rakelib/release.rake Outdated
Comment thread .agents/skills/address-review/SKILL.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
react_on_rails/spec/react_on_rails/release_rake_helpers_spec.rb (1)

2328-2332: ⚡ Quick win

Assert the encoded branch segment in this endpoint stub.

This should expect release%2F17.0.0 rather than raw release/17.0.0, so the spec protects the intended API contract.

🤖 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 `@react_on_rails/spec/react_on_rails/release_rake_helpers_spec.rb` around lines
2328 - 2332, The test stub for the Open3.capture2e method is using a raw branch
name "release/17.0.0" in the endpoint URL, but it should use the URL-encoded
version "release%2F17.0.0" to match the actual API contract. Update the endpoint
path in the .with() call of the Open3.capture2e stub to replace
"repos/shakacode/react_on_rails/branches/release/17.0.0/protection/required_status_checks"
with
"repos/shakacode/react_on_rails/branches/release%2F17.0.0/protection/required_status_checks"
so the test properly asserts the expected encoded branch segment.
🤖 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 @.agents/skills/address-review/SKILL.md:
- Around line 96-114: The BASE_REF assignment with `|| echo ""` masks command
failures, causing any gh pr view error to silently default to
RELEASE_PHASE="beta" even on release/* branches. This creates a "fail closed"
problem where transient auth/network issues incorrectly enable autonomous
optional-nit handling on stabilizing branches. Separate the gh pr view command
execution to explicitly detect when it fails, then implement the conditional
logic described in the requirements: only default to beta if the base is
confirmed to be main; otherwise, report that the phase could not be resolved and
ask the user to confirm the phase before proceeding with nit-autonomy rules.

In @.agents/workflows/address-review.md:
- Around line 91-98: The BASE_REF assignment using gh pr view with a fallback to
empty string silently masks lookup failures, causing the case statement to
default to RELEASE_PHASE=beta even when the PR targets a release/* branch. This
bypasses the stricter rc/final requirements. Replace the || echo "" fallback
with explicit error handling that exits or fails when the gh pr view command
fails or returns empty, ensuring the workflow does not silently downgrade
release/* PRs to the more permissive beta phase.

In `@rakelib/release.rake`:
- Around line 848-851: The `ci_branch` parameter in the
`required_check_names_for_main` method is being directly interpolated into the
GitHub API path without URL encoding, causing branch names containing forward
slashes (like `release/17.0.0`) to break the endpoint path. Use `CGI.escape()`
to properly encode the `ci_branch` before it is interpolated into the `api_path`
string to ensure special characters are handled correctly by the GitHub REST
API.

---

Nitpick comments:
In `@react_on_rails/spec/react_on_rails/release_rake_helpers_spec.rb`:
- Around line 2328-2332: The test stub for the Open3.capture2e method is using a
raw branch name "release/17.0.0" in the endpoint URL, but it should use the
URL-encoded version "release%2F17.0.0" to match the actual API contract. Update
the endpoint path in the .with() call of the Open3.capture2e stub to replace
"repos/shakacode/react_on_rails/branches/release/17.0.0/protection/required_status_checks"
with
"repos/shakacode/react_on_rails/branches/release%2F17.0.0/protection/required_status_checks"
so the test properly asserts the expected encoded branch segment.
🪄 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: eb83fadf-3eff-4a33-a08e-1d1340b09401

📥 Commits

Reviewing files that changed from the base of the PR and between cfca4dc and 19c7059.

📒 Files selected for processing (6)
  • .agents/skills/address-review/SKILL.md
  • .agents/workflows/address-review.md
  • AGENTS.md
  • internal/contributor-info/release-train-runbook.md
  • rakelib/release.rake
  • react_on_rails/spec/react_on_rails/release_rake_helpers_spec.rb

Comment thread .agents/skills/address-review/SKILL.md Outdated
Comment thread .agents/workflows/address-review.md Outdated
Comment thread rakelib/release.rake Outdated
@greptile-apps

greptile-apps Bot commented Jun 19, 2026

Copy link
Copy Markdown

Greptile Summary

This PR closes two release-critical gaps in the release-train workflow: it allows rake release[X.Y.Z] to run from the matching release/X.Y.Z branch (not just main), and corrects the CI gate so it validates the tip of the branch actually being released rather than always checking origin/main.

  • stable_release_branch_allowed? enforces exact-version branch matching so 17.0.0 cannot be promoted from release/16.7.1; release_ci_branch selects the CI ref; both are threaded through validate_main_ci_status! via a backward-compatible branch_kwargs pattern that leaves the main path's call signature unchanged for existing tests.
  • address-review Step 2.5 derives RELEASE_PHASE from the PR base branch at invocation time, suppressing beta-tier autonomous optional-nit handling for release/* bases and requiring adversarial-pr-review + zero open MUST-FIX before signaling merge-ready.
  • Runbook and AGENTS.md updated to replace both maintainer-decision disclosures with the now-resolved paths.

Confidence Score: 4/5

Safe to merge; the core release-path logic is correct and well-tested. The one finding is a fallback that is actually more conservative than intended, not less.

The branch-allowed guard and CI branch selection work correctly end-to-end. The only issue is in required_check_names_for_main: when called with a release/X.Y.Z ci_branch, the GitHub API path contains an unencoded /, so the call silently returns nil via the existing 404 fail-safe. For stable releases this is harmless (all checks are evaluated regardless), and for RC prerelease cuts it is slightly more conservative than intended (all runs evaluated instead of just the branch-protection-required subset). The address-review phase-derivation logic and the documentation updates are clean.

rakelib/release.rake — specifically the required_check_names_for_main function at line 850 where the branch name should be URL-encoded before embedding in the API path.

Important Files Changed

Filename Overview
rakelib/release.rake Adds stable_release_branch_allowed? and release_ci_branch helpers; threads ci_branch kwarg through fetch_main_ci_checks, required_check_names_for_main, format_main_ci_status_violation, and validate_main_ci_status!. Branch name with slashes is not URL-encoded in the GitHub API path for required_check_names_for_main, but the fail-safe fallback keeps behavior safe.
react_on_rails/spec/react_on_rails/release_rake_helpers_spec.rb Adds 12 new examples covering stable_release_branch_allowed?, release_ci_branch, validate_main_ci_status! with ci_branch override, fetch_main_ci_checks with release branch, and required_check_names_for_main with ci_branch. Coverage is thorough; global before stub for required_check_names_for_main correctly handles the new tests.
internal/contributor-info/release-train-runbook.md Replaces both maintainer-decision disclosures (step-1 CI caveat and step-4 stable-release blocker) with the now-resolved paths; adds a forward-port automation follow-up note. Documentation accurately describes the new behavior.
AGENTS.md Single-sentence update removing the "both need follow-up" caveat and replacing it with a description of the now-implemented branch-aware CI gate and stable-release branch allowlist.
.agents/skills/address-review/SKILL.md Adds Step 2.5 that derives RELEASE_PHASE from the PR base branch at invocation time, suppressing autonomous optional-nit handling on release/* bases. Logic is consistent with address-review.md mirror.
.agents/workflows/address-review.md Mirrors the Step 2.5 phase-resolution logic from SKILL.md. The case statement is functionally equivalent (uses * wildcard instead of explicit `main

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["rake release[X.Y.Z]"] --> B{is_prerelease?}
    B -- "yes (rc, beta, etc.)" --> C[any branch allowed]
    B -- "no (stable)" --> D{stable_release_branch_allowed?}
    D -- "main OR release/X.Y.Z" --> E[allowed]
    D -- "other branch" --> F[abort ❌]
    E --> G["release_ci_branch(current_branch)"]
    C --> G
    G -- "starts with release/" --> H["ci_branch = release/X.Y.Z"]
    G -- "other" --> I["ci_branch = main"]
    H --> J["validate_main_ci_status!(ci_branch: release/X.Y.Z)"]
    I --> K["validate_main_ci_status!(ci_branch: main)"]
    J --> L["fetch origin/release/X.Y.Z tip SHA"]
    K --> M["fetch origin/main tip SHA"]
    L --> N["required_check_names_for_main(ci_branch: release/X.Y.Z)"]
    M --> O["required_check_names_for_main(ci_branch: main)"]
    N --> P{API path URL-encoded?}
    P -- "No (current)" --> Q["gh api returns 404 → nil fallback\n(evaluate ALL checks)"]
    P -- "Yes (fix)" --> R["correct required-checks filter"]
    Q --> S[CI gate passes / aborts]
    R --> S
    O --> S
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["rake release[X.Y.Z]"] --> B{is_prerelease?}
    B -- "yes (rc, beta, etc.)" --> C[any branch allowed]
    B -- "no (stable)" --> D{stable_release_branch_allowed?}
    D -- "main OR release/X.Y.Z" --> E[allowed]
    D -- "other branch" --> F[abort ❌]
    E --> G["release_ci_branch(current_branch)"]
    C --> G
    G -- "starts with release/" --> H["ci_branch = release/X.Y.Z"]
    G -- "other" --> I["ci_branch = main"]
    H --> J["validate_main_ci_status!(ci_branch: release/X.Y.Z)"]
    I --> K["validate_main_ci_status!(ci_branch: main)"]
    J --> L["fetch origin/release/X.Y.Z tip SHA"]
    K --> M["fetch origin/main tip SHA"]
    L --> N["required_check_names_for_main(ci_branch: release/X.Y.Z)"]
    M --> O["required_check_names_for_main(ci_branch: main)"]
    N --> P{API path URL-encoded?}
    P -- "No (current)" --> Q["gh api returns 404 → nil fallback\n(evaluate ALL checks)"]
    P -- "Yes (fix)" --> R["correct required-checks filter"]
    Q --> S[CI gate passes / aborts]
    R --> S
    O --> S
Loading

Reviews (1): Last reviewed commit: "Add executable release/* promotion path ..." | Re-trigger Greptile

Comment thread rakelib/release.rake Outdated
@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Code Review

Overview

This PR closes two documented release-train gaps: (1) allowing rake release[X.Y.Z] to run from a matching release/X.Y.Z branch (stable_release_branch_allowed?), and (2) making the CI gate check the tip of whichever branch is actually being released (release_ci_branch + ci_branch threading). The address-review skill gains Step 2.5 to derive the release phase from the PR base branch. The implementation is focused and the 12 new RSpec examples cover the new helpers well.


Correctness

  • stable_release_branch_allowed?: clean exact-match whitelist; prevents cross-version promotion (e.g. 17.0.0 from release/16.7.1 is rejected).
  • release_ci_branch: the .to_s guard handles nil input without exception. Simple and correct.
  • CI gate integration: all six format_main_ci_status_violation call sites inside validate_main_ci_status! now pass branch: ci_branch, and the two early-exit paths in fetch_main_ci_checks use ci_branch interpolation. No missed sites.
  • Pre-release path: pre-releases skip stable_release_branch_allowed? entirely. release_ci_branch returns 'main' for non-release/* feature branches, preserving existing behaviour.

Issues / Suggestions

  1. branch_kwargs test-compatibility shim (release.rake:1029)

branch_kwargs = ci_branch == 'main' ? {} : { ci_branch: }

This avoids touching ~130 existing test stubs (which match fetch_main_ci_checks without ci_branch:) by not splatting the kwarg when it equals the default. It works today, but if the default of ci_branch: is ever changed, the main path will silently pass the old value rather than the new default. The comment in the code explains the reasoning, but this is worth a follow-up to either update the stubs or add a TODO noting the fragility. Inline comment added below.

  1. 'treat as final' comment is contradictory (SKILL.md:98, workflow:93)

The value set is 'rc' but the comment says 'treat as final'. The surrounding prose explains the floor/ceiling distinction (rc is the floor; the tracker controls whether it is actually final), but the inline comment alone reads as a bug. A clearer phrasing would be: '# floor=rc; treat as final when tracker is in final-release mode'. Inline comment added below.

  1. SKILL.md and workflow copy diverge in Step 2.5 (minor)

SKILL.md uses three case arms (release/*, main|'', ); the workflow copy uses two (release/, *). They are semantically identical today, but they will drift with future edits to one file. Since the PR description says these are mirrored, they should be byte-for-byte identical in this snippet.

  1. No test for the updated abort message

The error message was changed from 'Release must be run from the main branch!' to a multi-line message that now includes Target version: and the release-branch option. No new or updated spec examples assert on this string. Not a blocker (tests are green), but a regression guard would be useful.


Security

No concerns. ci_branch is always either 'main' or a branch name that passed the stable_release_branch_allowed? whitelist before release_ci_branch is invoked. Pre-release paths always return 'main'. All git/gh calls pass ci_branch as a separate array element (no shell interpolation). The GitHub API path interpolation goes through gh api and the branch name is whitelist-constrained.


Test Coverage

Helper-level coverage is solid: stable_release_branch_allowed? (4 cases), release_ci_branch (3 cases), fetch_main_ci_checks with override (2 cases), validate_main_ci_status! with override (2 cases), required_check_names_for_main with override (1 case). The one gap is the task-level integration (the wiring of release_ci_branch(current_branch) into validate_main_ci_status!) which is verified by reading the code but not by a spec.


Overall: the change is sound and fills the documented gaps correctly. The items above are all minor.

Comment thread rakelib/release.rake Outdated
Comment thread .agents/skills/address-review/SKILL.md Outdated
Comment thread .agents/workflows/address-review.md Outdated
@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Code Review — PR #4122

Overall: ✅ Solid implementation, minor nits below.

What this PR does

Closes two release-critical gaps in the release-train workflow:

  1. Executable release/* final-promotion pathrake release[X.Y.Z] now accepts the matching release/X.Y.Z branch in addition to main, with an exact version guard so release/16.7.1 cannot promote 17.0.0. The CI gate now evaluates the tip of whichever branch you're releasing from, not always origin/main.
  2. Phase wiring in address-review — Step 2.5 derives RELEASE_PHASE from the PR base branch, suppressing the autonomous optional-nit rule on release/* bases.

Strengths

  • stable_release_branch_allowed? and release_ci_branch are small, focused, and easy to test in isolation. Clean keyword-argument API.
  • Backward-compat design: defaulting ci_branch: "main" keeps the main path byte-identical and leaves all 130 existing stubs/tests untouched. The branch_kwargs = ci_branch == "main" ? {} : { ci_branch: } pattern is non-obvious but the comment explains the rationale clearly.
  • URI.encode_www_form_component correctly encodes / as %2F in the GitHub API path. Branch names can't contain spaces in git, so the +-for-space encoding (form vs path encoding) is a non-issue in practice.
  • Test coverage is thorough: all four stable_release_branch_allowed? cases, three release_ci_branch cases, fetch + violation message for ci_branch override, and URL encoding for required_check_names_for_main.
  • Documentation (runbook + AGENTS.md) cleanly replaces the prior "blocker / caveat" language with the now-supported mechanism. The forward-port automation note is appropriately scoped as a deliberate non-blocking follow-up.
  • Step 2.5 shell script in address-review: fail-closed behavior on unknown release/* phase is exactly right.

Minor issues

  1. Off-by-one indentation in .agents/workflows/address-review.md quick-action menu block — the f and f+i lines were edited with 5 leading spaces instead of the surrounding 6, leaving them visually misaligned with f+o and the rest of the table. Flagged inline.

  2. format_main_ci_status_violation signature name vs responsibility: the function is still named format_main_ci_status_violation but now formats messages for arbitrary branches. This is a naming debt to pay at some point — not a blocker for this PR since all call sites pass branch: correctly.

  3. The branch_kwargs indirection is pragmatic, but consider adding a note in a follow-up that when the test suite is eventually refactored to use shared contexts the indirection can be dropped and ci_branch: can always be passed explicitly.


Functionality looks correct. The release-promotion invariant (promote the last good RC in-place, no re-cut from main) is properly enforced and the CI gate is now honest. Ready to merge after the indentation nit is addressed.

Comment thread .agents/workflows/address-review.md Outdated
justin808 added a commit that referenced this pull request Jun 21, 2026
The release-promotion review fix left the `f` and `f+i` lines at 5 leading
spaces while `f+o`…`m` use 6, misaligning the quick-action table. Bump both to
6 spaces (claude-review on PR #4122).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@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: 4941b0bb54

ℹ️ 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 .agents/skills/address-review/SKILL.md Outdated
Comment thread rakelib/release.rake Outdated
Comment thread rakelib/release.rake
Comment thread .agents/skills/address-review/SKILL.md Outdated
@cursor

cursor Bot commented Jun 21, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_d66fe1fe-77d1-47f8-a651-f1f860a12e43)

@claude

claude Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

test

@justin808

Copy link
Copy Markdown
Member Author

+ci-force-full

@github-actions github-actions Bot added force-full-hosted-ci Bypass optimized hosted CI selection and run all hosted suites ready-for-hosted-ci Run optimized hosted GitHub CI for this PR labels Jun 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Force-Full Hosted CI Requested

Triggered 9 workflow(s) for 4941b0bb540a.
Mode: force-full hosted CI (bypasses optimized change selection).
Added ready-for-hosted-ci and force-full-hosted-ci, so future commits will bypass optimized hosted CI selection until +ci-stop-full is used.

View progress in the Actions tab.

@claude

claude Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Code Review — PR #4122: Executable release/* promotion path + CI-tip gate

Overall: well-scoped, backward-compatible, and well-tested. The two-helper approach (stable_release_branch_allowed? + release_ci_branch) is clean and the URI-encoding fix for branch-protection API paths is a correct detail that's easy to miss. 12 new test cases cover the key variants. A few things worth discussing before merge:


1. branch_kwargs conditional — maintenance trap (rakelib/release.rake:1034)

branch_kwargs = ci_branch == "main" ? {} : { ci_branch: }

The comment explains the motivation (preserving exact call signatures so existing test stubs don't break), but this is an unusual pattern that creates a silent coupling between the default values of ci_branch: in three different function signatures. If any downstream function's default is renamed or changed, the "omit-if-main" logic silently diverges.

A more explicit and future-proof alternative: just always pass ci_branch: to fetch_main_ci_checks and required_check_names_for_main, and update the existing test stubs to include ci_branch: "main". The change is mechanical and eliminates the coupling entirely. If the intent is test-stub backward-compat for a large stub surface, a shared helper or hash_including matcher could achieve the same without the conditional.

As written it works and the comment is clear, but this is worth flagging for the next person who touches those function signatures.


2. ci_branch vs branch parameter naming inconsistency (minor)

fetch_main_ci_checks and required_check_names_for_main accept ci_branch:, but format_main_ci_status_violation accepts branch:. All callers are consistent (always pass branch: ci_branch to the formatter), but the split naming adds a small mental-load cost when reading the function list. Consider either:

  • Renaming format_main_ci_status_violation's param to ci_branch: for consistency, or
  • Leaving it as branch: since the formatter is more "display-context" than "operation-context" — just noting it.

Not a blocker.


3. SKILL.md Step 2.5 — minor prose/code inconsistency

At the end of the bullet list after the bash snippet (around lines 59–70), the note says:

If gh pr view fails, report that the phase could not be resolved and default to beta only when the base is known to be main

But the bash snippet unconditionally exit 1s on gh pr view failure — there's no path where we fall back to beta from a failure (which is the right behavior). A reader might infer a fallback path where there is none. The bullets could say "stop and ask for confirmation" instead of implying a conditional default.


4. The stable_release_branch_allowed? gem-version-to-branch match assumption

The function constructs "release/#{target_gem_version}" where target_gem_version is in Ruby gem notation (dots, e.g. 17.0.0). This works for the documented case. Worth noting that Ruby-only patch versions (17.0.0.1) and future naming patterns that deviate from the release/<gem_version> convention would silently fall through to the abort path with a confusing error message. The existing abort message now includes Target version: which helps, so this is low risk — just noting for completeness.


Tests

Coverage is solid. One observation on the RC-cut test (spec line ~634–653): it stubs required_check_names_for_main with monorepo_root: and ci_branch: "release/17.0.0" but without repo_slug: — this correctly relies on repo_slug being nil in that call path (since the mocked fetch_main_ci_checks return doesn't include :repo_slug). This is fine but not obvious; a one-line comment in the spec would help the next person who reads the stub.


Summary: Approachable to merge as-is. The branch_kwargs conditional is the main thing I'd push back on mildly — it's functional but fragile. If the test-stub compat is the only blocker for always passing ci_branch:, that migration is cheap and would remove the coupling comment.

Comment thread rakelib/release.rake Outdated
Comment thread .agents/skills/address-review/SKILL.md Outdated
@cursor

cursor Bot commented Jun 21, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_30ecfe0f-f8da-420f-9ca6-68190c09a0c7)

@claude

claude Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

PR Review: Release Branch Promotion Path\n\nOverall this is a well-structured PR. The implementation is thorough: multiple layers of defense (identity check, RC tag ancestry walk, metadata-only commit classifier, version policy scoping), 197 tests, and careful error messages that tell the releaser exactly what to do next. The CI selector changes and deleted-push guards are handled correctly across all workflows. A few findings below.\n\nMedium: Net::HTTP.get_response has no timeout\n\nfetch_rubygems_versions (new) uses Net::HTTP.get_response without an open_timeout or read_timeout. If rubygems.org is slow or hangs, the release task will block indefinitely at the idempotency pre-check with no retry and no warning. The existing publish-verify path uses npm view (a CLI with system timeout defaults), so this is a new failure mode. Fix: replace get_response with Net::HTTP.start(..., open_timeout: 10, read_timeout: 15) { |http| http.get(uri.request_uri) }.\n\nLow: --publish-branch is a no-op alongside --no-git-checks\n\nIn npm_publish_base_args, a stable release-branch publish gets both --no-git-checks (skips all git validations) and --publish-branch branch (configures which branch pnpm would check -- already disabled). Harmless but dead code; a future reader may assume it provides an active guard.\n\nAdvisory: RELEASE_FINALIZATION_METADATA_PATHS omits CHANGELOG.md without explanation\n\nThe comment says Keep in sync with every file the release task rewrites but CHANGELOG.md is intentionally excluded (changelog-only commits pass through commit_non_runtime_only? instead). A one-line note would prevent a maintainer from adding it here and breaking the classifier -- release_finalization_metadata_content_only? has no .md handler and would raise UnhandledReleaseFinalizationMetadataPathError.\n\nAdvisory: RC cut on an old release line is silently blocked when a newer stable exists\n\nWith release_branch_tag_scope: true, tagged_version_order_candidates excludes other-line prereleases but keeps all stable versions. If 17.1.0 is already tagged and someone runs rake release[17.0.0.rc.4] from release/17.0.0 (security backport), the version policy fires with must be greater than latest tagged version 17.1.0. The existing test only covers a newer prerelease (17.1.0.beta.1), not a newer stable. Likely intentional given PR scope, but undocumented.\n\nNit: release_branch_tag_scope defaults to the parameter release_branch_final_promotion\n\nThe default release_branch_tag_scope: release_branch_final_promotion is valid Ruby but uncommon enough that a reader might think it is a mistake. A brief comment on the method would clarify intent.\n\n---\n\nTest suite quality is high, especially the release_finalization_metadata_commit? tests (runtime paths, non-modification entries, dependency-only lock changes, unhandled-path raise, and package-path sync guard). The identity check (ensure_release_branch_head_matches_remote!) has no direct unit tests but is exercised end-to-end through validate_main_ci_status!; acceptable for release tooling.

Comment thread rakelib/release.rake Outdated
Comment thread rakelib/release.rake
Comment thread rakelib/release.rake
Comment thread rakelib/release.rake
Comment thread rakelib/release.rake
@claude

claude Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Adversarial PR Review — #4122

PR: Add executable release/* promotion path + CI-tip gate
Head: 4f0b3921 (current; prior AI reviews are on stale commit 19c7059c and are advisory only)
Base: main | State: OPEN | Merge state: BLOCKED (no formal review approval)
Review decision: (none) — no maintainer GitHub review object present


Summary

This PR makes the release-train RC-to-stable promotion path runnable from a release/X.Y.Z branch, extends CI workflows to trigger on release/** pushes, and adds a mandatory local/remote HEAD identity check so the CI gate always validates the exact commit being tagged. The implementation is carefully scoped, ships 197 passing specs, and passes actionlint and RuboCop. No blocking issues found.


BLOCKING

None.


DISCUSS

1. normalized_release_gemfile_lock may classify promotion commits as runtime-bearing if Gemfile.lock includes non-version changes

rakelib/release.rakenormalized_release_gemfile_lock / release_finalization_metadata_content_only?

The regex only replaces react_on_rails and react_on_rails_pro version specifiers. If the Gemfile.lock produced during RC-to-final bump also changes the BUNDLED WITH line (Bundler version upgrade) or updates a transitive dependency, the before/after normalization comparison fails and the commit is classified as runtime-bearing. The CI walkback then stops at the finalization commit rather than the RC commit.

In practice this is conservative: CI is evaluated on the finalization commit (the one that will be tagged), not a stale ancestor. The risk is that if no CI run has completed on the finalization SHA specifically, the gate returns no_checks and the release blocks. Maintainers should know that any Gemfile.lock change beyond the gem version lines will prevent CI-walkback from treating the finalization commit as skippable.

Recommendation: Not a blocker. The existing runbook hint ("wait for at least one CI run to complete before retrying") covers this. Consider adding a note that broader Gemfile.lock changes also stop the walkback on the finalization commit.


FOLLOWUP

2. fetch_rubygems_versions does not follow HTTP redirects

rakelib/release.rake:2089-2100

Net::HTTP.start returns the raw response without following 3xx redirects. A redirect response fails is_a?(Net::HTTPSuccess) in rubygem_version_published?, so the idempotent-retry skip is bypassed and publish_gem_with_retry proceeds to gem push. The rescue StandardError ensures graceful degradation (the duplicate push fails from rubygems.org, not silently). In practice rubygems.org does not redirect its versions API. Low probability, worth noting for future maintainers.

3. No unit test for ensure_release_branch_head_matches_remote! when git rev-parse HEAD itself fails

The mismatch path (local != remote SHA) is well-tested at spec lines 2360-2397. The HEAD-unresolvable failure path is untested — it delegates to handle_release_branch_identity_violation! so the behavior is consistent, but a targeted test would make the non-overridable invariant explicit for this edge case.


NON_BLOCKING_DECISION

4. ensure_release_branch_head_matches_remote! has no override path outside dry-run

rakelib/release.rake:1030-1058

The function deliberately does not accept allow_override. The code comment explains this: bypassing it would allow tagging a commit whose remote CI belongs to a different SHA. Correct call for the release invariant. The only escape for a legitimate edge case (detached HEAD, etc.) is dry-run mode. Flagging for awareness; no change needed.

5. --publish-branch + --no-git-checks for stable release-branch npm publishes

rakelib/release.rakenpm_publish_base_args

Both flags are emitted for stable publishes from release/*. --no-git-checks disables pnpm's branch guard entirely; --publish-branch is then redundant as a safety mechanism but documents intent in the publish log. The code comment explains this explicitly. Safe and intentional.


Review-Thread Disposition

  • Cursor Bugbot (stale, 19c7059c, 3 issues) and Codex review (same stale commit): both advisory per the adversarial-review contract. No new inline comments from these systems on current head 4f0b3921.
  • No maintainer formal review object present (reviewDecision is empty). Maintainer approval required before merge.
  • mergeStateStatus: BLOCKED confirms the PR cannot merge without a formal review.

Validation Evidence

  • 197 RSpec examples, 0 failures (per PR body)
  • actionlint + Ruby YAML parse on all edited workflow files: passed
  • RuboCop: 0 offenses
  • PR CI readiness: READY
  • Strict merge ledger: complete_allowed: true

Merge authority: not delegated — requires maintainer decision.

Comment thread rakelib/release.rake
Comment thread rakelib/release.rake

@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: 4f0b3921ab

ℹ️ 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 rakelib/release.rake
…tion-path

* origin/main:
  Publish RSC architecture comparison (RoR Pro vs Next.js); remove docs-internal (#4158)
  fix(test): target real rsc-css precedence in RSC FOUC release gate (#4168)
Comment thread rakelib/release.rake
Comment thread rakelib/release.rake
Comment thread rakelib/release.rake
@claude

claude Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Code Review — Release promotion path + CI-tip gate

Overall: This is a well-engineered, well-tested PR. The release-branch promotion path is guarded at multiple layers (branch name check → cross-line guard → RC tag ancestry + metadata-only walk → local/remote HEAD identity → CI gate on the correct ref), and the test suite covers the new branches thoroughly. The design choices are sound. A few advisory notes below.


Correctness

ensure_release_branch_head_matches_remote! is non-overridable outside dry-run — this is the right call. The normal CI override intentionally cannot bypass the local/remote HEAD identity check (noted in the comment at the function definition). A release that tags a local-only commit while validating a different remote SHA would be a silent integrity hole; the enforcement is correct.

ci_branch_fetch_refspec force-refspec for release branches — using +refs/heads/release/X.Y.Z:refs/remotes/origin/release/X.Y.Z is correct. Release branches can be amended during stabilisation; a force-fetch is needed to get the current tip rather than a stale cached remote-tracking ref.

release_branch_commits_after_rc_tag uses rev-list without --first-parentgit rev-list A..B follows all parents. Since the runbook does not allow merge commits on release branches, this is fine in practice. If a future maintainer ever merges a fix commit onto a release branch, the walk would see commits from both parents and would correctly classify a runtime-bearing merge as non-metadata-only.

sha^ in release_finalization_metadata_commit? — for the root commit or a missing parent, git diff-tree fails and the function returns false (treats commit as runtime-bearing). Correct conservative fallback.

package_json_version_only_change? deep equality via ==JSON.parse produces plain Ruby hashes/arrays, so == does a deep recursive comparison. This correctly rejects commits that change any field other than "version".


Workflow / CI

The release/** push trigger additions are consistent across all ten workflow files, and the branch-deletion guard (github.event.deleted != true) is correctly placed before any checkout step. The pro-integration-tests and pro-test-package-and-gem already had a Dependabot guard; the compound if: expression correctly ANDs both conditions.

The selector action change narrows the isReleaseTarget push path to refs/heads/release/* only (not releases/* or release-*), which is intentional and matches the release train's release/X.Y.Z convention.


Inline comments

Three advisory notes are left inline (all low-risk):

  • Line 604UnhandledReleaseFinalizationMetadataPathError can escape as an uncaught Ruby exception if a new path type is added to RELEASE_FINALIZATION_METADATA_PATHS without a handler; the error message is readable but a task-level rescue would be cleaner.
  • Line 1374Hash#except is Ruby ≥ 3.0; verify the project floor.
  • Line 1384 — the Gemfile.lock normaliser regex covers exactly react_on_rails and react_on_rails_pro; a third internal gem would need a regex extension to keep the metadata-only classification working.

None of these are blocking. The release-critical logic is correct.

Comment thread rakelib/release.rake
Comment thread .github/actions/hosted-ci-selectors/action.yml
Comment thread rakelib/release.rake
@claude

claude Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Code Review {4122

Overall Assessment

This is a well-structured PR with solid test coverage (197 examples). The release-branch promotion path is implemented carefully, with layered safety checks (branch-name–version match, remote RC tag ancestry, metadata-only walk-back, local/remote HEAD alignment, idempotent registry skip). The logic in release.rake is complex, but each guard has clear intent and test coverage.


Summary of findings (see inline comments for detail)

Finding Severity File Action
stable_release_retry_for_current_head? never called from production code P1 rakelib/release.rake:738 Remove or add a caller
isReleaseBranchPush narrower than isReleasePullRequest with no comment explaining the asymmetry P2 .github/actions/hosted-ci-selectors/action.yml:55 Add inline comment
UnhandledReleaseFinalizationMetadataPathError escapes uncaught, produces backtrace instead of clean abort Advisory rakelib/release.rake:1329 Optional rescue-and-abort for UX consistency
Hash#except requires Ruby 3.0+ Nit rakelib/release.rake:1374 Verify min-version contract if gemspec ever tightens

The core promotion logic (CI gate, RC ancestry check, metadata-only walk, idempotent retry, version-policy scope) is sound and the test coverage for the new paths is thorough.

@justin808

Copy link
Copy Markdown
Member Author

State: ready-no-merge-authority. Merge authority was not delegated for this batch, so I am not merging.

Confidence note:

  • Validated: ruby -c rakelib/release.rake; ruby -c react_on_rails/spec/react_on_rails/release_rake_helpers_spec.rb; cd react_on_rails && bundle exec rspec spec/react_on_rails/release_rake_helpers_spec.rb (212 examples, 0 failures); targeted RuboCop on rakelib/release.rake and the focused spec (no offenses); git diff --check; .agents/bin/agent-workflow-seam-doctor; script/ci-changes-detector origin/main (full suite, no benchmarks); timeout 900 codex review --uncommitted (no discrete correctness findings after verification); hosted PR checks (50 pass/skipped, 0 failed, 0 pending); unresolved review threads (0); script/pr-merge-ledger 4122 --strict --changelog-classification not_user_visible --finding-dispositions /tmp/pr4122-finding-dispositions.json (complete_allowed: true); follow-up issue Follow-up: Exercise GitHub Actions changes from PR #4122 #4172 verified open.
  • Evidence: current head 4b3714c0c1363bb40927c2eb593c66f01407bb01; hosted checks on this PR are green/skipped; Follow-up: Exercise GitHub Actions changes from PR #4122 #4172 tracks the required post-merge GitHub Actions exercise; all current review threads were replied to and resolved.
  • UNKNOWN: none.
  • Residual risk: low; release automation behavior is covered by focused helper specs and the workflow/action surface is covered by hosted validation, with the required post-merge exercise tracked in Follow-up: Exercise GitHub Actions changes from PR #4122 #4172.

@justin808 justin808 added this pull request to the merge queue Jun 24, 2026
Merged via the queue into main with commit 1e30909 Jun 24, 2026
62 checks passed
@justin808 justin808 deleted the jg/4000-release-promotion-path branch June 24, 2026 23:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-hosted-ci Run optimized hosted GitHub CI for this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant