Skip to content

Latest commit

 

History

History
191 lines (138 loc) · 9.42 KB

File metadata and controls

191 lines (138 loc) · 9.42 KB

GitHub Queue Contract

PatchRelay, review-quill, and merge-steward are three intentionally decoupled services. GitHub is the protocol boundary between all of them.

This document is the contract for that boundary. For the mental model behind it (three roles, four primitives, the carry-forward rule, the eviction rule) see concepts.md.

Shared Primitives

  • Repository: repoFullName
  • Base branch: baseBranch
  • Pull request identity: prNumber, branch name, headSha, baseSha
  • Review state: approved, changes requested, commented
  • Check state: passed, failed, pending
  • Merge state: open, closed, merged
  • Change identity: patch_id. See Identity algorithm below.

Shared Control Artifacts

The bus carries six named artifacts:

Artifact Default name Writer Readers
Eviction check_run merge-steward/queue Lander Author
Integration candidate ref mq-spec-<entry-id> Lander Operators, CI
No-cache PR label review:no-cache Author / human Reviewer
Queued-for-deploy Linear sub-label queued-for-deploy Author Operators
Queue-testing PR label queue:testing Lander Author, operators
Queue-merging PR label queue:merging Lander Author, operators

Eviction check_run (Lander → Author)

Merge Steward emits this on queue eviction. PatchRelay interprets it as a queue-repair request rather than ordinary CI failure. The check carries structured incident detail in output.text plus an incident details URL so PatchRelay preserves richer repair context.

No-cache PR label (Author → Reviewer)

A PR carrying this label opts out of carry-forward — review-quill always runs a fresh review even when the patch is unchanged. Useful for release / changelog PRs that need a fresh body rendering.

Queued-for-deploy Linear sub-label (Author → operators)

When a project's Linear workflow does not include an In Merge Queue state, PatchRelay leaves the issue in Reviewing and adds this label so operators can distinguish "reviewing, awaiting verdict" from "reviewing, queued for landing." Removed when the issue leaves the merge-queue fallback (Done, eviction back to Implementing, or moves to a real In Merge Queue / Deploying state).

Queue sub-state PR labels (Lander → Author, operators)

Merge Steward keeps these two labels in sync with a PR's live position in the queue, so the queue phase is visible on the GitHub PR itself and readable by PatchRelay for its Linear "In Merge Queue" status. They are mutually exclusive and edge-triggered (applied/cleared only on phase change), and cleared once the entry leaves the active queue (merged, evicted, or dequeued).

Label Set when Meaning
queue:testing entry is validating Candidate checks are running / awaiting its turn
queue:merging entry is merging Head of queue, merge in progress

The Lander never touches any other label, so the admission label (queue), priority label (queue:priority), and human-applied labels are left intact.

Ownership

  • PatchRelay owns:

    • branch implementation
    • keeping each issue branch independent from other open PR heads
    • review fixes
    • branch-local CI repair
    • queue repair after steward eviction
    • Linear-facing operator and session UX
  • Merge Steward owns:

    • queue admission
    • branch freshness
    • validation retries
    • merge execution
    • eviction classification and incident creation

Required GitHub Events

  • PatchRelay:

    • pull_request
    • pull_request_review
    • check_suite
    • check_run
    • push
  • Merge Steward:

    • pull_request
    • pull_request_review
    • check_suite
    • push

Failure Contract

  • Ordinary branch CI failure:

    • produced by normal PR checks
    • PatchRelay routes to ci_repair
  • Requested changes:

    • produced by a GitHub pull request review whose state is CHANGES_REQUESTED
    • PatchRelay records the blocking review head SHA and routes delegated issues to review_fix
    • before launching every repair, PatchRelay refreshes review context from GitHub directly: latest requested-changes review id, review body, inline review comments, review commit SHA, current PR head SHA, and reviewer login when available
    • cached Linear or prior-run context may enrich the prompt, but it cannot skip the GitHub refresh; if the refresh is degraded, the repair prompt must say so before launch
    • the repair must push a new remote PR head before PatchRelay can return the issue to review or queue
    • the Linear completion response reports the review round, resulting head when known, and structured addressed/deferred/not-applicable sections
    • if the run finishes while the remote PR head is still the blocking review head, PatchRelay must fail the run and surface a system failure instead of handing the same SHA back to the reviewer
  • Queue eviction:

    • produced by Merge Steward as the configured eviction check run
    • PatchRelay routes to queue_repair
    • PatchRelay persists queue-failure provenance so reconciliation can preserve the distinction after webhook delivery
    • the eviction check run should carry structured incident detail in output.text plus an incident details URL so PatchRelay can preserve richer repair context
    • an exact candidate sharing ancestry with another open PR outside main is a policy eviction; the repair removes that ancestry before a new head can re-enter review and queue

Observability Contract

  • PatchRelay should expose:

    • configured eviction check name
    • last observed queue/failure signal
  • Merge Steward should expose:

    • incident detail for evicted entries
    • emitted eviction check run name
    • current required checks / admission facts from GitHub truth

Identity algorithm

A reviewed change has one identity hash. Any service implementing the pipeline below exactly produces an interoperable identity — same inputs, same byte sequence, same hash.

PATCH_ID(branch, base) :=
  git diff $(git merge-base <base> <branch>)..<branch> \
    | git patch-id --stable \
    | awk '{print $1}'

Notes:

  • git patch-id --stable (not bare git patch-id) — the --stable flag canonicalises per-file order so commit reorders within a range produce the same id.
  • <base> for PATCH_ID is the PR's base ref as GitHub reports it. For a stacked PR, that's the parent PR's branch — not always main.

Review carry-forward

review-quill caches approved verdicts so a head SHA change that preserves both the patch and its effective immutable diff base does not trigger a fresh review run. A rebase onto a different base is a different review input and receives a fresh review.

Review Quill always reads the PR head against GitHub's structured PR base (base.ref and the captured base.sha). The carry-forward key is patch_id plus the effective immutable diff-base SHA. For a stacked PR, the parent branch is therefore the diff base; Review Quill does not synthesize or review Merge Steward's integration commit.

A PR carrying the configured no-cache label (default review:no-cache) is always re-reviewed even when the patch is unchanged.

Carry-forward only fires for stored verdicts that include the rendered review body and event. Rows from before the carry-forward migration have NULL bodies and naturally fall through to a fresh review (rollout safety).

Configurable names per service

Each service exposes its own configuration shape rather than a single shared field, so configuration aligns with each service's existing convention. Defaults across services agree byte-for-byte; overriding a name on one side requires the same override on the other.

patchrelay — under github.* in workflow-types.ts:

github: {
  mergeQueueCheckName?: string;     // default: "merge-steward/queue"
  queuedForDeployLabel?: string;    // default: "queued-for-deploy"
  deployWorkflowName?: string;      // opt-in: GH Actions workflow that deploys main post-merge
}

When deployWorkflowName is set, a merged issue enters the deploying state and patchrelay watches that workflow's runs on the base branch: success → Done, failure → escalated (operator attention), and a 20-minute timeout backstops a deploy that never arrives (the change is already on main). When unset, a merge advances straight to Done.

Resolved through resolveMergeQueueProtocol() (src/merge-queue-protocol.ts); internal code reads the resolver, never project.github.* directly.

review-quill — per-repository config (packages/review-quill/src/types.ts):

noCacheLabel?: string;

review-quill does not need the eviction name or queued-for-deploy label.

merge-steward — flat config (packages/merge-steward/src/types.ts):

evictionCheckName?: string;       // default: "merge-steward/queue"
queueTestingLabel?: string;       // default: "queue:testing"
queueMergingLabel?: string;       // default: "queue:merging"

What this unlocks

  • Replace the Lander with Mergify. Set the author-side eviction artifact names to Mergify's conventions. PatchRelay reacts to its eviction signal; Review Quill continues to review the ordinary GitHub PR surface.
  • Replace the Reviewer with Copilot Code Review. Turn off review-quill. Merge-steward and patchrelay don't notice — they read GitHub's prReviewState, which any reviewer populates.
  • Replace the Author with a human. PatchRelay isn't running. Merge-steward and review-quill operate on the human-authored PR normally; only the Linear status sync goes missing (a patchrelay-specific feature).