Why
A protected branch could receive a non-fast-forward landing whose tree was never
built. The gate asked for checks on the request head, but Prepare made a merge
commit at landing time and nothing queued a run for it. A pushed merge commit with
the right two parents was also accepted from its shape alone, so “merge, then push”
could carry content nobody had approved or tested. This closes #64.
What changed
- A non-fast-forward landing now publishes its merge commit at
refs/vole/candidates/<request>/<base>-<head>and runs the workflows at that tree, as the base branch. The gate requires green results for that commit, and a fast-forward still lands the head with its existing runs. - Combined results are queued automatically: on a head push, and on a base move for requests that are approved, armed, or viewed by an account that can discuss. A short reconciler interval means a burst of work-in-progress pushes settles on the head that is actually there.
- A pushed merge commit must now carry exactly the tree a clean merge of the branch tip and the reviewed head produces. A merge with extra content, or with conflicts resolved by hand, is refused and says which tree was expected.
- Approvals still name the head and the base branch, so a moved base retests the combined result without a re-approval or a rebase.
- Merge when ready: if the combined result is still running, the Merge click records that intent and the worker lands the request when it passes. Every landing re-runs the full gate, so an armed request is a wait, not an authorisation. It stops on a head change, a close, lost permission, a conflict, or a failed combined result.
- Contributor combined results reuse the exact head’s Allow CI decision, stay in the contributor cache namespace, and never deploy the site. The combined result is tested only after that head is allowed.
- The request page shows the combined result and its runs, the Merge control offers
Merge when ready, and
vole change showprints the combined state and the landing state.vole change mergearms the same wait from the terminal. - Candidate refs are cleaned up once their runs finish and the request moves, closes, or merges. Workflows used for required checks come from the landing tree.
Verification
go test ./...vole workflow run verify(format, vet, build, govulncheck, packages, markdown links, tests)- New tests cover the tree check for pushed merges, resolved-conflict refusal,
candidate publication and reuse, candidate runs in
ForSHA, the auto-merge store, contributor cache trust, and an end-to-end non-fast-forward landing that waits for the combined result and then lands it.
Notes
- The old “merge, then push” recommendation is gone from
docs/change-requests.md; the Merge button andvole change mergeare the supported path. - Conflicts remain a human decision. The server never resolves them; it asks the author to update the branch.
- If base moves make combined-result CI the bottleneck, a merge train is the next step. This change does not add one.
Closes #64