Repository navigation
ci: refuse new migrations that are not newer than the base branch - #256
Conversation
GitHub does not re-run pull_request checks when the base branch moves, so a PR whose new migration was newer than main when its Docs check ran could fall behind while it waited and still merge green (#242). - New migration-recheck workflow on push to main (and workflow_dispatch): tools/ci/migration-recheck.mjs lists open PRs into main, reads their heads as git objects (never checked out or executed), and re-runs the Docs check of every PR whose added migrations are no longer newer than main, so the required check fails. A re-run replays the original run's workflow file, so for runs that predate the Migration order step, refused re-runs, or runs still going after six minutes it posts a failing "Migration order (base moved)" commit status on the PR head instead. - migration-order.mjs: suggest a rename to the next whole hour above main's newest and above every migration the PR adds; shared git helpers. - Unit tests for the pure logic; AGENTS.md documents the rename. Closes #251 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A commit status under its own context is not a required check, so the fallback for runs that cannot be re-run usefully did not block anything. Create a failing "Docs check" check run on the PR head instead (checks: write), the name the branch protection requires. A run whose job recorded no steps (cancelled before it started) is re-run rather than treated as predating the Migration order step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Skip the fallback when the newest "Docs check" check run on the PR head is already the failing "Migration order (base moved)" one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
If your organization's extra usage balance is empty, an organization admin can add extra usage credits at claude.ai/admin-settings/usage. If its monthly spend limit was reached, an admin can raise it on the same page. If neither applies, contact Anthropic support.
Once extra usage is available, someone with write access to this repository can comment @claude review on this pull request to trigger a review.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 31 minutes. View limit details
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a new migration re-check workflow (tools/ci/migration-recheck.mjs) to automatically re-evaluate open pull requests when the base branch moves, ensuring migration timestamps remain sequential. It also updates migration-order.mjs to suggest precise renames for stale migrations and adds corresponding unit tests. Feedback on the changes highlights a critical issue in tools/ci/migration-recheck.mjs where the gh api call uses bracket notation for nested parameters (such as output[title]), which is unsupported by the GitHub CLI's -f flag and will cause API failures; a fix is suggested to serialize the nested output object as JSON and pass it using the -F flag.
| const fields = [`name=${REQUIRED_CHECK}`, `head_sha=${pr.sha}`, 'status=completed', 'conclusion=failure', `output[title]=${FALLBACK_TITLE}`, `output[summary]=${staleSummary(pr)}`, ...(runUrl ? [`details_url=${runUrl}`] : [])]; | ||
| const [ok, text] = post([`repos/${repo}/check-runs`, ...fields.flatMap((field) => ['-f', field])], `failing "${REQUIRED_CHECK}" check run`); |
There was a problem hiding this comment.
The gh api command does not support nested object parameters using bracket notation (like output[title]=... and output[summary]=...) when using the -f / --field flag. Passing these will result in a flat payload with literal keys like "output[title]" instead of a nested output object, causing the GitHub API to reject the request or ignore the output.\n\nTo pass nested objects correctly with gh api, you should serialize the nested object as a JSON string and pass it using the -F / --raw-field flag, which parses the value as raw JSON.
const outputJson = JSON.stringify({ title: FALLBACK_TITLE, summary: staleSummary(pr) });
const fields = [
'-f', `name=${REQUIRED_CHECK}`,
'-f', `head_sha=${pr.sha}`,
'-f', 'status=completed',
'-f', 'conclusion=failure',
'-F', `output=${outputJson}`,
...(runUrl ? ['-f', `details_url=${runUrl}`] : [])
];
const [ok, text] = post([`repos/${repo}/check-runs`, ...fields], `failing "${REQUIRED_CHECK}" check run`);
#249 added the "Migration order" step to the required Docs check job. It compares the migrations a PR adds with the live base branch. GitHub does not re-run PR checks when
mainmoves, so a PR that was green when its check ran could still merge behindmain(the #242 case). This PR covers the remaining acceptance criterion of #251, re-checking when the base moves.Design
New workflow
migration-recheck.yml. It runs onpushtomain, plusworkflow_dispatchfor a manual run. It runstools/ci/migration-recheck.mjs, which:main(one paginated REST call);refs/pull/N/head) and diffs each against its merge base withmain, the same asmigration-order.mjs. Renames count as added. PRs without migrations pass;main's newest, re-runs its latest Docs check workflow run. The re-run replays the old merge commit, but its Migration order step fetches the livemain, so the required check fails until the author renames the file.Fallback for when a re-run cannot fail the check. This covers three cases:
In each of these, the script creates a failing check run named "Docs check" on the PR head (
checks: write), titled "Migration order (base moved)" and listing the files. GITHUB_TOKEN creates it as the GitHub Actions app, which is the app the required "Docs check" is bound to. The next push to the PR replaces it with a real run. If the newest "Docs check" check run on the head is already that fallback, later pushes tomaindo not add another one.Skipped cases: runs that already fail, runs waiting for a maintainer's approval (
action_required), PRs with no run yet (their first run reads the live base), and heads that moved during the check (that push gets its own run).Safety:
mainonly. There is nopull_request_target, and PR heads are never checked out or executed: onlygit fetch,merge-base,ls-treeanddiff --name-onlytouch them.git/ghcalls useexecFileSyncwith argument arrays, so there is no shell. PR numbers and SHAs are validated, and log lines cannot start a workflow command.actions: writefor the re-run andchecks: writefor the fallback. The top level stayscontents: read.persist-credentials: false.Why not "require branches to be up to date"? That setting is currently off (strict = false). Turning it on would also work, but every PR would have to be updated whenever
mainmoves, and it is an owner setting. This PR changes no branch protection, and the owner does not need to change any required check.Other changes
migration-order.mjsnow suggests a concrete rename: the next whole hour abovemain's newest and above every migration the PR adds, one hour apart for several stale files. The git helpers are shared with the re-check script.mainmoves, so the re-check does. When it fails, rename your migration to a version abovemain's newest and push.Verification
pnpm exec vitest run tools/test: 24 passed, including new tests for the version suggestion, stale-PR selection, the re-run decision and the fallback summary.pnpm -s lint,pnpm -s typecheck,pnpm docs:check,oxlintontools/ciandtools/test, andactionlinton both workflows: all clean.node tools/ci/migration-recheck.mjs --dry-run main origin/main), after feat(kernel): positions, agents, tasks and wake-ups (ADR-0029 Phase 1, T18) #245 merged20261018050000while this PR was open, found:20261018030000,20261018040000) were green when their checks ran and are now behind. They take the re-run path.node tools/ci/migration-order.mjs origin/main e75e6b2reproduces fix(auth): invites fail closed without auth.users.email_confirmed_at (#208) #242: it fails20261018010000_...against20261018020000and suggests20261018030000_....Known gap: a run from before #249 that was cancelled before its job started recorded no steps. The script re-runs it, and the re-run replays the old workflow, which passes again. This can happen only to runs from before #249.
After merge (owner or agent)
Two things can only be confirmed on GitHub. Check them on the first push to
mainafter merge:The first push will act on the currently stale open PRs, so watch the job summary. A failure of either shows as a red migration-recheck run on
main.Closes #251
🤖 Generated with Claude Code