Skip to main content

Module land

Module land 

Source
Expand description

Landing the winner: watch the pull request, fix what it complains about, and merge it.

Opening the pull request used to be where magi stopped and the operator started: watch the checks, read what the review bots found, push a fix, wait again, merge. That loop is mechanical, it takes an hour of wall-clock time per pull request, and doing it by hand six times in one session is how a queue that drains unattended stops being unattended. So it lives here.

§Shape

PrState is one observation of a pull request and decide is the whole policy as a pure function of it. Nothing in decide talks to gh, which is what makes “green with an unresolved comment is a fix, not a merge” an assertion in a test rather than a claim in a comment. land is the only part that performs I/O: observe, decide, act, repeat.

§What it refuses to do

Merging is the one irreversible thing magi can do to a repository, so the loop is built to stop rather than to guess:

  • A red pull request is never merged. When the budget runs out the pull request is left open with a comment naming what is still failing, because a magi that force-merges a red pull request is worse than one that stops.
  • A pull request whose checks cannot be read at all (gh reported no rollup) is not merged either. Landing is for repositories with CI; with no signal there is nothing to be green.
  • A pull request a human merged or closed underneath us is Step::Done - the person won, and their decision is not an error.

§Why --subject is not optional

A candidate branch holds one commit whose subject is magi: candidate A (uncommitted work), and gh pr merge --squash prefers a single commit’s message over the pull request title. Merging without merge_argv’s explicit --subject therefore writes a main history that says nothing about what landed. AGENTS.md records the trap; this module is where it is prevented.

Structs§

ExternalMerge
A pull request the operator merged outside of land::land’s own loop, found by asking GitHub about the run’s own winning branch rather than requiring the operator to go and find the URL themselves.
PrState
One observation of a pull request.
Refusal
Why closable said no, and whether asking again later could say yes.
ReviewComment
One outstanding review comment, human or bot.

Enums§

Approval
What the owner’s answer to the approval question means.
Blocking
Whether a failing check actually stands between the pull request and main, according to the forge.
Checks
The aggregate verdict of a pull request’s checks.
OpenPr
What gh pr list --head <branch> --base <base> --state open found.
PrLifecycle
Where a pull request is in its life.
Step
What the loop decided to do next. Pure, so the policy is testable.

Constants§

APPROVAL_NODE
Graph node recorded on the approval question.
APPROVE
The choice that lets the merge happen, verbatim as the owner taps it.
CHECKS_GRACE
How long the checks may stay unreadable before landing gives up on them.
DIFF_MAX_LINES
Unified diff lines carried in the panel before it is truncated.
HOLD
The choice that leaves the pull request open.
MARKER
Marker carried by every comment magi posts on a pull request.
POLL
How often the pull request is re-read while its checks are still running.
WAIT_CEILING
How long one wait may last before landing gives up on the checks finishing.

Functions§

approval
Read the owner’s answer, where None is an unanswered question.
approval_panel
The approval panel’s html: what is about to land, and the evidence for it.
branch_is_ancestor
Whether branch is, right now, an ancestor of base_branch in the local git graph — the weaker, URL-less signal that a branch landed somewhere.
closable
Pure half of close_superseded_pr: read gh pr view --json headRefName,baseRefName,state,isCrossRepository and say whether closing is safe, Err carrying the reason when it is not.
close_superseded_pr
Close the open pull request for branch, if there is one and it is provably this run’s, with a comment naming what supersedes it. Ok(Ok(url)) is the URL closed; Ok(Err(why)) is a final “nothing to close” (no pull request, not this run’s, no forge); Err is anything a later attempt could resolve (a failed gh call, a head that moved since it was checked), which the caller must not treat as settled.
correct_manual_merge
Confirm url is actually a merged pull request, then rewrite state’s status and merge exactly as the automatic land loop (land::land) would have written them had magi opened and merged this pull request itself.
decide
Decide the next step. No I/O.
deputy_brief
What a merge-approval question’s deputy is told: the pull request, the run, what each answer does, and - when the run’s record is readable - the contested hand-off the question was filed over.
disable_automerge_argv
Take auto-merge back. magi no longer arms auto-merge, but a run recorded by an older build may still have one standing on the forge, so the disarm paths (and RunState::land_armed_head) stay. Run before anything that changes the head, so an armed merge never outlives the commit that was approved for it.
find_external_merge
Ask GitHub whether this run’s winning candidate branch was actually merged somewhere land::land’s own loop never saw — the gap magi fold --merged exists to close, minus the operator having to find the URL by hand.
find_open_pr
Open pull requests whose head is branch and whose base is base. A failing gh is an error carrying its own output, never “none”: guessing there is how a duplicate gets created.
is_noise
Is this comment body machinery rather than a finding?
land
Run the loop against a real pull request until it merges or the budget runs out.
lifecycle
Read just a pull request’s lifecycle state - open, merged, or closed - with none of the checks/reviews/comments land itself needs to decide what to do next.
merge_argv
The argv magi merges with, minus the program name.
merge_argv_at
merge_argv pinned to the commit the decision was made about.
merge_subject
The squash subject to merge under.
parse_inline_comments
Parse gh api repos/{owner}/{repo}/pulls/<n>/comments into inline review comments. No I/O.
parse_pr
Parse gh pr view --json url,number,state,statusCheckRollup,reviews,comments output into a PrState. No I/O.
pick_open_pr
Pure half of find_open_pr: classify the raw --json number,url,title,baseRefName output. Entries whose base is not base are dropped even though the query already filtered on it, so a stub or an old gh that ignores --base cannot get a pull request into the wrong branch adopted.
repair_stale_pr_states
One-time (and idempotent) repair of records that froze an open pull request: ask the forge about each distinct pull request that a terminal run still calls open - at most max_lookups of them - and rewrite the merged and closed ones. A genuinely open pull request is left alone, and a lookup that fails is “unknown, change nothing”. Returns (records rewritten, lookups that failed).
set_pr_title
gh pr edit <url> --title <title>, for an adopted pull request whose title differs from the one this run computed. Only the title: the body may have been edited by the owner and cannot be compared.
superseded_comment
The comment left on a pull request closed because its change is already on the base.