Concept · Steward
How the Steward merges pull requests
When a pull request counts as ready, whose pull requests the Steward merges, how it tests them, catches them up and merges several together, what it sends back to you, and what a pull request can ask for once merged.
- Article
- 1303
- Applies to
- Steward 0.35.0
- Last reviewed
- For
- For developers
When it merges#
Only in a repository where merging is allowed (Merge my ready PRs when you looked after it, or The Steward merges ready pull requests in Castellan's Settings > Repositories). There, it merges:
- in its rounds, while it's on duty and Merges and releases by itself is on in its settings;
- when you ask: Merge your ready PRs on its page (for the repositories you tick; it asks first), or
merge --yes --teamfrom a terminal. See The Steward's command line and local API.
Whose pull requests#
Only your team's. Its settings' Team is empty to begin with, which means the GitHub account gh is signed in as on this PC: you. Your AI coding assistant opens its pull requests with your account, so that covers them too.
Name accounts under Team and they're the whole team instead (a GitHub App as app/<name>). A pull request anyone else opened is never merged, nor listed.
What "ready" means#
A ready pull request merges once all of these hold:
- it isn't a draft;
- it goes into the repository's branch (one stacked on another branch waits for that one);
- it merges cleanly;
- no check on it is failing or still running (no checks at all counts as green);
- when GitHub runs no checks on it, it has passed the repository's tests on this PC, or you vouched for it (below);
- when it changes the version, the version is new (below).
A pull request that isn't ready waits for a later round, and the page says what holds it. One that waits 24 hours becomes an alarm. See The Steward's alarms.
Tested here, when GitHub runs no checks#
Many repositories have no CI. For a pull request with no checks on GitHub, the Steward runs the repository's Tests command at the pull request's head commit, in a worktree of its own, before it merges it.
- A failure is tried once more at once, so one flaky test doesn't hold a pull request.
- Failing twice is said once, as a failed merge. The pull request then waits quietly until it's pushed to, or its branch moves on.
- A pass is said with the merge: "merged #17 (checks passed here at abc1234)".
- A repository with no Tests command can't be tested here, so such a pull request waits for you.
- A pull request whose checks pass on GitHub isn't tested again here.
Only your team's pull requests are tested here, so only code from your own accounts runs on this PC this way.
You can save it the test. Run the tests yourself and vouch for the commit, and the Steward merges it without testing it again. See Vouching, and pull requests the Steward opens.
Tests only what a change reaches, on in its settings, runs only the test files a change reaches: the ones it changed, the ones whose imports reach a changed file, and the ones that name one. A change to the setup (tsconfig, package.json beyond its version, test fixtures), a deleted file, or a file no code names runs the whole suite. Turn it off to run the whole suite every time.
Versions merge in order, each its own#
A pull request that changes the version waits when that version is:
- already released;
- not above the version on its branch;
- the same one another ready pull request sets ("#17 and #20 both set v0.3.9: each needs a version of its own");
- claimed by other work.
So a merge never leaves a version conflict behind, and two changes never share a version. Ready pull requests merge lowest version first. To keep this from happening at all, claim each version before the work starts: Versions claimed up front.
Caught up, when only its branch moved#
When a ready pull request waits only because its branch moved on (it now conflicts, or it's behind, or its version is no longer new), the Steward catches it up, while Catches PRs up with their branch is on:
- It merges the branch into the pull request: a merge commit on top, so nothing of yours is rewritten.
- It resolves a conflict only where it's safe: in a version file, where one side changed nothing but versions; or in
CHANGELOG.md, where each side only added an entry at its top (the branch's entries are kept, and yours goes above them, under the version it ends up with). - It gives the pull request the next free version, if its own is taken.
- It pushes to the pull request's branch (never forced), fixes the version in its title, and says what it did in a comment.
Then it's tested and merged in the same round. Where the catch-up changed nothing but version lines and the changelog, and the old head had passed here or been vouched for, it isn't tested again. A queue of such pull requests, each caught up once the one below it merges, drains in one round.
Sent back, when it really conflicts#
Any other conflict is yours to resolve: the Steward never guesses at your code. It leaves the branch untouched and comments on the pull request, naming the files and asking for the branch to be merged in and pushed. The round says so, naming the files, and so does the pull request's row on the page until it's fixed. It's sent back once for each pair of heads, and again only when one of them moves.
Merge conflicts, on the Steward's page, lists every pull request that conflicted with its branch in the last two weeks, and whether it was caught up or sent back.
Several merged together#
While Merges ready PRs together is on, when two or more of a repository's ready pull requests (from the repository itself, with no checks on GitHub) wait their turn:
- they're stacked on its branch, lowest version first, each one's version lines and changelog settled as a catch-up would;
- the stack is tested once, at the top;
- they merge together through the top pull request, each keeping its own version and changelog entry.
A pull request that conflicts beyond its version lines ends the stack, and goes its own way. A stack whose tests fail is merged one pull request at a time instead. Turn it off to have each one caught up and tested in turn.
How it merges#
- With a merge commit, naming the head commit it looked at and tested, so a pull request pushed to since is refused, and waits for the next round.
- Your branch isn't deleted: it's yours, and may still be checked out in a worktree.
- Pull requests from forks are never caught up.
What a pull request asks for once merged#
A pull request can ask for a step after it merges: in its description, a fenced code block whose language is steward, holding this JSON:
{"after": ["release"]}release: the version it leaves on the branch is released once it's merged, as How the Steward releases says. Before merging, the Steward reads that version, and holds a pull request whose version is already released ("raise the version in the PR").- The block lists steps the Steward knows, never commands. Whoever can edit the description chooses among them, not what runs.
installandapprove-jobsare for Castellan's own agents: a repository of yours has no install command, so a pull request that asks for them waits.
A block the Steward can't read, two blocks, or a step it doesn't know holds the pull request, and says why: merged without it, what the pull request asked for would silently not happen.
Related articles
Is this page right?
If something on it is wrong or out of date, tell us and we'll fix the page.
Still stuck? Write to support@castellan-software.com and mention article 1303. Every version of Steward, and what changed in it, is in its release notes.