A landing moves its base branch without running the Git hooks, so the open requests that follow that branch kept the pre-merge tip. This makes the landing move them, in the same commit that marks it merged.
What changed
- One follower rule.
SyncBranchHead’s body moved intofollowBranch(files, branch, head), which writes the moved request files into an open metadata commit.SyncBranchHeadandMarkMergedboth call it, so a push and a landing cannot drift apart. - The mark moves followers.
MarkMergednow updates every open request whosehead_refis the request’s base branch onto the commit that landed. It happens inside the sameCommitFilescallback as the merged mark and the linked-issue closures, so a failure writes none of them and a retry does all three. - Docs. The stacked-requests section says a landing moves followed requests, so the lower request tracks its branch without a separate command.
Approvals are unchanged: a follower’s approvals still name the commit they were given for, so they stop counting until the follower has new ones — the same behaviour a push has.
Why this was invisible
The CI run was not the missing piece: reconcileRefs queues a moved ref within
30s even without the push spool. What was missing was the follower sync, which
only post-receive performed. The symptom was a follower showing “branch moved”
and runs for its old head while the new run sat under the base branch.
Tests
TestLandMovesTheRequestsThatFollowTheBase builds the stacked shape — a lower
request following stack, an upper request landing into stack — lands the
upper one and asserts the lower one is still open with head: at the landed
commit. It fails without the MarkMerged change and passes with it.
gofmt, go vet, scripts/check-links.py, vole workflow check, and
go test -race -count=1 -tags sqlite_fts5 ./metadata ./forge/landing pass; the
wider suite was run with -tags sqlite_fts5.