Skip to content

feat(github): hold sibling PR checks and comment when an apply preflights a target - #940

Draft
aparajon wants to merge 2 commits into
armand/check-hold-storagefrom
armand/check-hold-fanout
Draft

feat(github): hold sibling PR checks and comment when an apply preflights a target#940
aparajon wants to merge 2 commits into
armand/check-hold-storagefrom
armand/check-hold-fanout

Conversation

@aparajon

@aparajon aparajon commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

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. Stack 5/7, on top of #939.

What it does

  • Preflight fan-out (fanOutCheckPreflight): for each open sibling PR with stored check state on the apply's target, conditionally flips the stored check to blocked (apply_in_flight_on_target), updates the aggregate Check Run, and posts one explanatory PR comment. Checks owned by an in-progress apply and closed PRs are skipped with logged outcomes. The flip is optimistic on the head SHA, so a PR that moves mid-fan-out is never stomped.
  • Hold comment (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.
  • Settle deferral: a settle fan-out completes without re-planning while a later preflighted apply is still active on the same target — the re-plan would mint fresh green checks against a schema mid-change.
  • Release sweep (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.
  • Consumer registration moves from handler construction to 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.
 preflight request ──► hold sibling checks ──► PR comment (once)
                        │ skip: in-progress apply-owned, closed PR,
                        │       moved head SHA
 settle request    ──► later preflighted apply active on target?
                        ├─ yes: complete without re-plan (defer)
                        └─ no:  re-plan siblings, releasing holds
 release sweep     ──► terminal apply w/ preflight, no settle
                        └─ backfill settle (holds never leak)

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 main as its base merges.

🤖 Generated with Claude Code

aparajon and others added 2 commits August 5, 2026 11:13
…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>
@aparajon
aparajon force-pushed the armand/check-hold-storage branch from 8506ecb to d72df1f Compare August 5, 2026 15:14
@aparajon
aparajon force-pushed the armand/check-hold-fanout branch from 3973ef6 to d30dc67 Compare August 5, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant