Use when you are a lander told by the coordinator to land one reviewed branch on the main branch, and as a coordinator before queueing a branch for landing and after every landing. Covers <kit>/tools/land.py --submit, the wait-result verdicts, read-only verification, separate cleanup, what to do on a conflict, a red gate, a stuck landing or a safety-check refusal, and the coordinator's queueing, record closing and after-landing notices.
Install
npx skillscat add josiahallard/agentic-project-manager-kit/land-branch Install via the SkillsCat registry.
Land a branch
Landings go through the machine-lock queue as one entry each. The coordinator submits each LAND verdict at once (land.py --submit) and keeps a background gate.py wait-result running; a lander is an optional wrapper that does the same for one branch and reports. Landings go ahead of every ordinary waiting run on their own and merge onto whatever main is at their turn; nothing aligns them.
Lander (optional)
Prerequisites, all from the coordinator: the branch, its reviewed tip sha (full), the verdict in words (LAND, or LAND WITH FOLLOW-UP and where it is recorded), and any extra gate. Missing one: say what you need, do nothing. Read the repo profile's Landing and Shared resources sections (python <kit>/tools/doc_index.py -C <main checkout> --find landing); a repo whose profile names another landing command uses it, and these rules still hold.
- Work from the main checkout on the main branch, never inside a worktree you will remove;
git statusclean; logs go to your scratch folder. - Run the brief's extra gate first, under the lock the profile names, and release it: the landing books its own lock. Pure pass/fail: dispatch the test-runner role.
- Submit as one bare call:
python <kit>/tools/land.py -C <main checkout> <branch> --expect-tip <sha> --submit [--lane <lane>]. A detached process books one machine-lock entry, takes the landing lock, merges the main it finds--no-ff, gates the merged tree, pushes only when green, and undoes a red merge itself. The call prints oneSUBMITTED ... wait: ...line and exits 0; no such line: report the output. - Start the
gate.py wait-result ... --kind land -C <main checkout>command that line prints, as printed, in the background with output to a log (never a pipe, polling loop or Monitor), and wait for its notification.python <kit>/tools/land.py -C <main checkout> --queueshows who holds and who waits. - Read the line the wait prints,
RESULT <verdict> land <branch> at <tip>: <line> (<log>).pass,LANDED ...: landed.landed-ungated,LANDED ...: landed, but commits on top of the merge were not gated; report the log'sWARNING:line (the coordinator runs the full gate on main).fail,CONFLICT <branch> with main <sha>: the author merges main: nothing left behind; report it, the author merges main (never resolve it yourself).failwithNOT LANDED: ...(a red merged gate, a refused push), orrefused,busy,not-run,usage(REFUSED:,BUSY:,NOT RUN:,USAGE:): nothing left behind; report the line and the log's failing lines, the coordinator decides.stuck,STUCK: ...: the lock stays held; report it and the log'sleft:lines verbatim and do nothing else.error: the landing stopped on an exception, or its process died (the landing process died: ...); report it withland.py --queue. - Only a flake on the repo's list (
python <kit>/tools/doc_index.py -C <main checkout> --find flakes) earns one resubmit. - When the brief asks, size-check each doc both sides touched: merged lines = main's plus the branch's net (
git diff --numstat <merge-base> <branch> -- <path>), plus or minus 1; else stop and report. - Verify read-only, whatever the verdict:
git ls-remote <remote> <main>equalsgit rev-parse <main>, then onegit merge-base --is-ancestor <tip> <remote>/<main>per branch (exit 0). Not verified: report. - Records: the branch names its own in its last commit's message, on its own line (
Closes: <ID>), and land.py closes it at landing; one the brief names still open at the pushed main is reported, never closed by you. - Cleanup, only after step 8:
land.pyremoves the landed branch, worktree and remote branch itself. For any part itskeptlines name: the repo's command (python <kit>/tools/doc_index.py -C <main checkout> --find cleanup), elsepython <kit>/tools/land.py -C <main checkout> --sweep-landed(a dry run; read it) thenpython <kit>/tools/land.py -C <main checkout> --sweep-landed --apply, orpython <kit>/tools/worktree_remove.py -C <main checkout> <worktree>. "Filename too long" or "Permission denied": leave it, report. Absolute paths only. - Report one block: branch; old and new main sha; merge commit; test counts; extra gate; pushed yes/no with both shas; worktree removed; any record still open; any judgement you made.
Coordinator: queueing a landing
- Submit every LAND verdict as soon as you give it, from the main checkout:
python <kit>/tools/land.py -C <main checkout> <branch> --expect-tip <reviewed tip> --submit(a batch of up to 3 where--find landingallows; never a high-risk branch). It prints oneSUBMITTED ...line. Start thegate.py wait-result <branch> --tip <sha> --kind land -C <main checkout>command it prints as a background command, one per submission, and act when the host wakes you on its exit; do not poll, loop or sleep. No go to anyone, no expected-main hash, no waiting for a free lane: submitted landings queue and merge in ticket order, each onto the main it finds. A lander role (optional) may do the submit, wait and report for you. - Read the
RESULT <verdict> land ...: <line>it prints (the verdicts: lander step 5).pass-> close records (below) and tell workstreams main moved.failwithCONFLICT ...: the author merges main-> send the branch back to its author to merge main and re-gate, re-review the change, resubmit with the new tip.failwithNOT LANDED: ...-> nothing landed: send it back with the failing lines, as for any red gate.refused,busy,not-run-> report the line and fix its cause.stuckorlanded-ungated-> report the lines verbatim;stuckleaves the lock held for the owner,landed-ungatedneeds the full gate on main.error-> readland.py --queueand the log before anything else. - Never pass anything to jump the queue and never break a landing lock: a landing waits in the machine-lock queue like any run and goes ahead of ordinary ones by itself.
- A branch lands only with your verdict and its full gate green.
land.pyaborts any conflict, docs too: the author merges main and re-gates; the brief carries no conflict rules. - A test pinning files outside its module is mapped to them in the gate's selector (
--find gate), or main goes red at the next landing. - Before a record closes, move what must survive (owner decisions, quoted and dated, to the decisions log; constraints beside the code or the owning doc); open follow-ups become their own records.
- After every landing, tell every open workstream that main moved and what changed in their area.
- A fresh or successor session reads
land.py --queueand the land result records first (skill session-handoff), and never resubmits a branch already queued.
Never
Edit code; close or amend a backlog record; force-push; git add -A; reset --hard; branch -D or --force; git worktree remove or git branch -d from a worktree; chain a write with other commands; land without a verdict or a branch whose tip moved after review; wait in a loop, override or break a lock; kill a process by name; remove a worktree or untracked file the brief did not name. A safety-check refusal is final: report it verbatim, no retry, rewording, other shell or agent.
Details: <kit>/docs/gate.md ("Commands", "Exit codes", "Landing", "Branch cleanup"); <kit>/docs/worktree-remove.md.