Skip to content

Arm /land through transient states, ask small acks inline (work-system 1.33.0) - #104

Merged
gering merged 1 commit into
mainfrom
task/improve-land-armed-and-inline-ack
Nov 7, 2026
Merged

gering merged 1 commit into
mainfrom
task/improve-land-armed-and-inline-ack

Conversation

@gering

@gering gering commented Nov 4, 2026 •

Copy link
Copy Markdown
Owner

Summary

/land now stops only for something serious.

  • One classifier — scripts/land-gate.py probe decides every pre-merge state: ready, wait (class=transient), stop (class=hard) or merged. The /land prose only relays the class. Its stops table is cross-checked by test_land.py against every hard reason= the script can print.

  • pr-lagging (the PR Recognise native-installer claude in /sessions (work-system 1.31.1) #100 incident): local == the pushed branch (read with git ls-remote), but the PR head is stale. This is now transient instead of head=diverged → abort. Other transient states: CI queued or running, no check yet in a repo with workflows (180 s grace), mergeability UNKNOWN, a gh/GitHub error.

  • Armed /land — arm binds the exact HEAD the user landed on and the PR head seen at that moment, with a 45-minute deadline and an arm_id. wait re-probes in bounded 390-second calls (a tool call caps at 10 min, and a foreground sleep is blocked) and prints progress lines. Any of these disarms it and reports: a hard state, a new local head (head-moved), a PR head it never saw (pr-head-moved), or the deadline. The armed state lives in the git common dir, is bound to arm_id + the herdr pane, and grants nothing; landing.sh end clears it on every stop path. Only a typed /land arms, and there is no daemon.

  • Inline acknowledgement — when a post-review delta is the only coverage gap, /land summarizes it (exactly as /pr-flow:merge step 6b does) and asks one AskUserQuestion before any wait. The options are acknowledge and land / show the raw delta / re-review / stop. Only the user's own answer counts. /merge then sees via=user-ack and does not ask again.

  • CI like /merge — only a check that branch protection requires is a hard stop. A red optional check comes back as ci=failed-optional: ready, and /merge warns and asks. Pending checks are waited for while branch protection requires none, otherwise only the required ones. STALE never blocks.

Local review (/swarm:review, round 1/5): lens-only, because both cc-harness voices failed with "no gateway token". Of 13 findings I agreed with 10 and applied them; that is where the required-only CI rule comes from. The others: a running wait now re-reads its marker and stops with disarmed; the slice is 390 s so it stays under the tool cap; the timeout line now names what it waited for (last_wait=); plus small hardening. Not applied: #3 (no checks → ready after the grace period is deliberate, and /merge still gates required checks) and #11 (the post-merge head check runs where the gate's lane checks no longer hold). The outside-herdr session binding is documented as a residual. The fixes are recorded as agreed fixes (covered via=agreed-fixes).

Local review round 2 (/swarm:review --fix, again lens-only: both cc-harness voices still fail with "no gateway token"): 14 findings. I applied 12, fully or in part:

  • mergeStateStatus: BLOCKED, BEHIND and DRAFT used to read as ready. Now BLOCKED with every check green waits for the CI grace (a required check that has not registered yet), then stops as merge-blocked. REVIEW_REQUIRED, DRAFT and BEHIND are hard stops.
  • The gate compares against the PR's branch (refs/heads/<branch> on the branch's remote), not branch.<b>.merge. no-upstream is gone.
  • arm --head <the probe's head_sha=>: the armed head is the one that was probed and acknowledged.
  • The required checks are cached per process. A red check while gh cannot say which checks are required now waits (ci-required-unknown) instead of stopping.
  • The prose stops when a wait exits non-zero. Doc fixes: the 390 s slice, the CI wording, the top-level README.
  • Not applied: Dogfood knowledge-system: init + curate plugin-design learnings #13 and Harden /init scaffolding: absorb unmarked section, lazy domains #14. The step-4 head-check block duplicates relation(), but it is tested by executing it; moving it into the script is a possible follow-up.

Decisions:

  • Head binding default: any head not known when /land was typed disarms and asks. A pushed amend of the lane is not accepted, because the script cannot tell an amend from a new change.
  • unpushed stays hard: /land never pushes.
  • pr-flow is unchanged: its step 6b ack already composes with the inline ack.

Version: work-system 1.33.0. This is the next free minor after #100 (1.31.1) and #101 (1.32.0), so a rebase after those land will touch only the CHANGELOG/manifest lines.

Local review round 3 (/swarm:review --fix after the rebase onto #101/#103, cross-family: session lenses plus cc-harness gpt-6.1-sol and grok-4.7; 2 of 15 grok calls timed out): 21 findings, 13 ✅ and 4 🟨 applied, 4 ❌ left:

  • DRAFT and BEHIND are ready again: /pr-flow:merge asks to mark a draft ready and rebases a branch that is behind, so the round-2 hard stops took that away. REVIEW_REQUIRED stays hard and is now checked before CI, so a pending check cannot hide it behind the armed wait.
  • upstream-mismatch (hard, consensus of all three families): /merge checks unpushed commits against @{u} and pushes its rebase only with one. The gate keeps comparing against the PR's branch, and now also requires @{u} to be that branch.
  • The required-check set is asked on every probe (gh lists only checks that already reported); the last answer stands in only while gh fails.
  • A fork PR with the lane's branch name stops (isCrossRepository). A disarm during a slow classify wins over ready. A lagging PR head is no longer fetched. The MERGED relation uses the branch's remote.
  • /land handles a probe that exits non-zero. The inline ack offers Show the raw delta first, so a stray Enter only shows the diff. The stops-table indentation is fixed.
  • Not applied: an old gh without pr checks --json (unsupported), the slice timing (the worst case stays under 600 s), merging the gate with workers.py ready_for_merge (a different purpose), and deriving the armed path in landing.sh (the proposed fix does not work).

Test plan

  • test_land_gate.py (new): every classification on a real bare origin + worktree with a fake gh, arm/wait across calls, head binding, deadline, arm_id/pane checks, landing.sh end disarm
  • test_land.py updated (gate, ack, armed contract, stops-table coverage)
  • test_land_gate.py: required vs optional checks, STALE, a marker removed mid-wait, last_wait on timeout
  • test_land_gate.py: merge states (DRAFT/BEHIND ready, BLOCKED, REVIEW_REQUIRED also behind a pending check, UNSTABLE), missing or foreign upstream → upstream-mismatch, fork PR, arm --head mismatch
  • scripts/check-structure.py green (after both fix rounds)

🤖 Generated with Claude Code

https://claude.ai/code/session_01RtoWCyDFZc9zp1HFv5spih

@gering
gering force-pushed the task/improve-land-armed-and-inline-ack branch 3 times, most recently from 8fdb57c to 409fb6e Compare November 4, 2026 21:33
@gering
gering force-pushed the task/improve-land-armed-and-inline-ack branch from 409fb6e to ff245d3 Compare November 7, 2026 15:09
…m 1.33.0)

- land-gate.py: one classifier for every pre-merge /land state (ready,
  wait = transient, stop = hard, merged); pr-lagging (local == pushed
  branch, PR head stale) waits instead of aborting
- arm/wait: bind the exact HEAD and the PR head seen at arm, wait in
  bounded 8-minute calls up to a 45-minute deadline; head-moved,
  pr-head-moved, a hard state or the deadline disarm
- armed state in the git common dir, bound to arm_id + herdr pane;
  landing.sh end clears it with the landing marker
- /land asks the post-review delta ack as one AskUserQuestion before
  any wait; prose relays the class, a stops table covers every reason
- tests, README, CHANGELOG, knowledge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RtoWCyDFZc9zp1HFv5spih
@gering
gering force-pushed the task/improve-land-armed-and-inline-ack branch from ff245d3 to 89d01b5 Compare November 7, 2026 20:22
@gering
gering merged commit 93c3285 into main Nov 7, 2026
2 checks passed
@gering
gering deleted the task/improve-land-armed-and-inline-ack branch November 7, 2026 22:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant