# Land-queue gate offload + merge-train parallel gating

audience: AI coding agents first.

outcome: land-queue gate execution runs on buildboxes, then fans out across the fleet merge-train style; landing latency stops being laptop-bound.
status: ACTIVE — one lane executing slice 1 then slice 2, dispatched 2026-08-15.
source request: owner 2026-08-15 — heavy compute belongs on buildboxes; agents cannot wait on load; then owner's merge-train message (design pinned verbatim below).

## Why (measured)

Conductor (`modules/workstation/claude/workflows/lib/finish-branch.sh`, queue at `.git/harness/landq`) runs every ticket's FULL gate locally, serialized: 5 tickets pending 2026-08-15 15:29, ~20+ min each at laptop load 13+. Landing takes hours whenever lanes are active, which is always.

## Slice 1 — single-box gate offload (config-gated)

- Gate command executes on ONE reachable buildbox against the exact ticket revision; verdict + full logs come back. Hosts ONLY from `~/.claude/buildbox-hosts.json` — NEVER hardcode; skip hosts not marked reachable.
- Gate MUST NOT weaken: same commands, fail-closed, exact-revision receipt (sha the remote actually tested).
- Remote unreachable / dispatch failure → ticket FAILS VISIBLY with reason in verdict. NEVER silent local fallback (owner: silent local fallback is a defect). Never leave a ticket dark.
- Rollout: config key names the gate executor; default = current local behavior until flipped in a second commit after live proof (real ticket, gate host named in verdict/log).
- Tests: factory suite (`python3 -m pytest modules/harness/factory/tests/ -q`) + landq/finish-branch suite green; both branches covered (remote success, dispatch failure).

## Slice 2 — merge-train parallel gating (owner design decision, 2026-08-15)

Owner pinned the pattern (bors / Zuul / GitHub merge queue) and chose the middle-ground variant:

- Conductor becomes a dispatcher. **Merge order stays strictly FIFO; only VERIFICATION overlaps**, up to one gate per reachable buildbox (3 today). Merging never cuts the line.
- **Staleness wrinkle (owner, verbatim in substance): naive parallelism breaks main.** Ticket B gated against a main lacking still-unmerged ticket A is stale if A merges first — A+B together were never tested. Full speculative train (test A, A+B, A+B+C; discard and re-run on failure) is the rigorous fix; owner accepts the SIMPLER middle ground:
  - Gates run in parallel against current main.
  - At merge time, one cheap re-check: main moved since B's gate ran AND the moved diff overlaps B's touched areas → re-run B's gate. Disjoint (common case; lanes own disjoint files by design) → merge on the existing green.
  - Fail-closed: overlap-detection error → re-run, never merge on doubt.
- Receipts record BOTH the tested sha AND the main sha the candidate was merged onto at gate time — the re-check consumes that.
- Tail latency is part of the goal: a docs-only ticket must not wait behind two 20-minute web builds.
- Tests: parallel green + disjoint merge; stale-overlap re-run; gate-failure discard. Live proof: two concurrent tickets gating on two different boxes, FIFO merge preserved.

## Constraints (standing)

Buildboxes headless, ssh:2222 the only door, never reboot. e2e/browser gates stay on `e2e-remote`'s path. Extend the existing remote-build machinery (`build-remote.json`, `~/builds` mirrors, `local-gate`, /od-testing remote-sharding doctrine) — never a parallel system.

## Receipt

- 2026-08-15: lane dispatched with slice 1; owner's merge-train directive delivered to the same lane as slice 2 before slice 1 completed. This file is the durable record of both.
- 2026-08-15: slice 1 code (`FINISH_BRANCH_TYPECHECK_REMOTE`, default off) implemented and tested green in `wt/landq-gate-offload` (`8824de4be`); submitted via `ship.sh submit` → `ticket.353bf032c4be4e5c8292b3a9b2a7f93e`. First attempt returned `candidate-conflict` (branch was behind `origin/main` by 30 commits). Rebased onto current `origin/main`, resolving a docs-only add/add conflict on this plan file (kept this canonical copy), resubmitting.

next executable action: land slice 1 dark → live proof (real ticket, flag on, `local-gate.log` shows `via=remote host=<box>`) → flip default in its own commit → then slice 2 (read the full `finish-branch.sh` land-queue drain loop and `conductor.lock` semantics before designing).

## Owner caveat — k3s-contained agents test LOCALLY (2026-08-15)

Owner verbatim: "need to allow the factory agents to run tests 'locally', and not make it over-engineered 'remote testing'. since agents are contained in k3s the loads are already constrained."

Rule: the offload machinery (remote gate execution, e2e-remote, cpu-guard offload shims) exists to protect the OWNER'S LAPTOP. An agent already running inside a k3s/container seat on a buildbox runs its build/test commands DIRECTLY in its own container — no offload hop, no remote-testing indirection. Detection MUST be environmental (the seat/container context is knowable, e.g. registry-listed host + containment marker), never per-agent judgment. The laptop keeps the offload path unchanged. Applies to factory runs on k3s, seat-resident sessions, and any future cluster-dispatched worker. Over-engineering a second remote hop from a box that IS the remote is explicitly rejected.
