feat(github): hold sibling PR checks and comment when an apply preflights a target - #940
feat(github): hold sibling PR checks and comment when an apply preflights a target#940aparajon wants to merge 3 commits into
Conversation
4df18d8 to
8506ecb
Compare
8ab2a4c to
3973ef6
Compare
8506ecb to
d72df1f
Compare
3973ef6 to
d30dc67
Compare
…ghts a target The check refresh processor consumes the new preflight request kind: before an apply changes a target schema, every open sibling PR with stored check state on that (environment, database type, database) target gets its check conditionally flipped to blocked (apply-in-flight) and a single explanatory PR comment, so a merge cannot land on a verdict the apply is about to invalidate. Checks owned by an in-progress apply and closed PRs are skipped; the flip is optimistic on the head SHA so a PR that moves mid-fan-out is never stomped, and the comment is idempotent via a hidden per-apply marker. Settle fan-outs release the holds by re-planning against the live schema, and defer without re-planning while a later preflighted apply is still active on the target. A release sweep backfills a settle for any terminal apply whose preflight held checks but whose settle was never recorded, so a hold can never outlive its apply. Consumer registration moves from handler construction to StartCheckRefreshProcessor: a registered consumer now means a processor is actually running to drain requests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fan-out Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…host render The hold phase flips sibling stored checks and stamps holds_recorded_at using storage alone, so the operator gate can start the apply during a code-host outage; the render phase (aggregate Check Run, hold comment) stays retryable without re-blocking the apply, and a re-arm sweep keeps a terminally failed render retrying while its apply is active. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
d30dc67 to
5b5c1e6
Compare
|
🤖 Review findings - created by Kiran's code review agent - for pull/940, d30dc67. Verdict: 9 findings — 2 blocking (fail-open aggregate publish, broken comment dedup), 4 non-blocking, 3 suggestions. Blocking
Non-blocking
General suggestions
The one thing that could have broken, verifiedThe preflight-hold contract itself — "when the preflight request is marked completed, every sibling PR is un-mergeable" — because the #941 gate will trust that completion before starting an apply. Verified it is fail-open on both of its surfaces and sound on every other axis. Visible surface: the aggregate Check Run write's error is discarded at merge_gate.go:714 (finding 1), so completion does not imply the merge button is blocked. Stored surface: the hold has no write-path guard protecting it — it writes Verified correct
This review was generated by Claude Code (claude-fable-5). |
Why this matters
A green check on a sibling PR is a promise that its plan still matches the target schema. The moment an apply starts changing that schema, the promise is stale — but today nothing on the PR says so, and the merge gate processor only knows how to re-plan after the apply finishes. This PR teaches the processor to consume preflight requests: hold every sibling PR's check action-required and explain why with a PR comment, before the apply's engine work begins. The fan-out runs in two phases so the hold itself never depends on the code host being reachable. Stack 5/7, on top of #939.
What it does
fanOutCheckPreflight):apply_in_flight_on_target), then stampsholds_recorded_at— the signal the operator gate (feat(api): gate apply start on confirmed sibling PR check holds #941) starts the apply on. No code-host call in this phase, so the holds land even when the code host is fully down.RenderCheckHold): tells the PR's author what is changing, on which target, by whom, and what to do next (wait for the apply to settle; checks re-plan automatically). Idempotent via a hidden per-apply marker, so webhook redeliveries and retries never double-post.ReopenTerminalPreflightsForActiveApplies, counted byschemabot.merge_gate.preflight_renders_rearmed_total), so a hold's Check Run and comment keep retrying until the code host recovers — nothing else would retry them once the apply has started.sweepPreflightedAppliesMissingSettle): settles are now the release valve for holds, so any apply that reaches a terminal state with a preflight but no settle (e.g. cancelled while queued, where no drive tail runs) gets a settle backfilled. A hold can never outlive its apply.StartMergeGateProcessor: a registered consumer now means "a processor is running and will drain requests", which the operator gate in the follow-up PR relies on.How it moves us toward the northstar
The PR's checks become an honest, live rendering of the target's state: "an apply is changing this schema right now" is visible where merges are decided, not buried in an operator log. The final PR in the stack turns this into a hard gate on apply start.
The chain: #867 (storage) → #868 (drive-tail recording) → #866 (settle re-plan processor) → #939 (request kinds + hold storage) → #940 (preflight hold fan-out) → #941 (apply-start gate) → #942 (plan-time holds). Merges bottom-up; each PR retargets to
mainas its base merges.🤖 Generated with Claude Code