plans: brief for the host starting the landing run once a merge decision is approved (#1651)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
jason.woltjeandClaude Opus 5.5 committed 2026-10-18 09:35:56 -05:00
1 parent 917ef55e18
commit bb0766826d
1 file changed
+104
+104
View File
@@ -0,0 +1,104 @@
# Landing-run launch brief, 2026-10-18
A #314 follow-up from the row 149 review (Filbert, comment 28878 on
#1643). Line numbers are at 4c1fee85a. Gitea #1651.
## The host starts the landing run once a merge decision is approved
### Problem
A non-thread instance never starts on its own. Only role.launch, pm's
`launch` tool or `bus start <business> --pm` starts one
(`packages/cli/src/launcher.mjs:1-36`). The thread tick
(`launcher.mjs:358`, scheduled at `:561`) starts thread instances only.
The launcher asks the broker `landHeld` once, at launch (`launcher.mjs:262`),
and binds `land_work` and the git socket's land op only when it is true
(`packages/bus/src/broker.mjs:951`). So the merge-gate run that raises a
merge decision can't land it, and the approval comes back as a DECISION
message (`broker.mjs:1590-1615`) that no launcher hears.
Row 149 closes the gap in the contracts: the technical arbiter tells the
chief of staff, and the chief of staff launches merge-gate. That depends
on two model turns, and after a restart (acceptance 314 step 7) on the
chief of staff's next run reading a message sent before the restart.
This row makes the host start the landing run itself.
### Owner and reviewer
- Owner: set when the row is briefed.
- Reviewer: filbert.
### Files owned
- `packages/bus/src/broker.mjs`: a trusted read, `landHolders(business)`,
the non-thread instances for which `landHeld` with `thread: null` is
true, each with the id and resolution time of the decision that makes
it true
- `packages/bus/src/runtime.mjs`, `packages/bus/src/process.mjs`: the
launch op `landHolders`, beside `threadsState` (`runtime.mjs:163`,
`process.mjs:18`, `:36`)
- `packages/cli/src/launcher.mjs`: the tick and its header comment
- `packages/bus/tests/land.test.mjs`, `packages/cli/tests/launcher.test.mjs`
- `packages/cli/README.md`, `docs/TOOLS.md`
- `roles/merge-gate.md`, `roles/chief-of-staff.md`, `roles/cto.md`: only
to say the host now starts the run. Row 149's steps stay as the
fallback: cto tells the chief of staff, which ends the raising run if
`launch` refuses `instance-running`, then launches
- the packet `agents/<owner>/work/queue-<row>/`
### What ships
1. Each tick, after the thread pass, the host asks `landHolders`. For
each holder that `launch.instances` lists and that isn't running, it
starts the instance through `check()` and `start()` with
`launchedBy` the business's human, as the thread pass does. The start
refuses as any launch does.
2. The raising run is usually still running. A runner stays in its
`message.receive` loop after its turn, and only thread runs have an
idle stop (`launcher.mjs:370-391`). A run that started before the
decision's resolution has no `land_work`. The host stops it the way
the idle stop does, SIGTERM to the runner after the start-time check
(`launcher.mjs:388-390`), and logs `land-relaunch` with the run and
the decision. It stops only between turns: a run with a turn in
progress keeps going to a later tick, as `threadIdle` checks for a
thread run, and the row adds that check for this run. The stop
doesn't touch the decision. The next tick
starts the instance. A run that started after the resolution already
has `land_work`, and the host leaves it alone.
3. One start per decision. The host keys starts on the decision id, as
the thread pass keys them on the message and open
(`launcher.mjs:396-400`). A run that ended without landing doesn't
start again every tick. A refusal is logged once per code and isn't a
start.
4. A thread instance is never a holder. Thread runs keep today's rule.
5. After a host restart, the first tick starts the holder, so step 7's
landing comes from a new run of the same instance with the approval
from before the restart.
Tests:
- `landHolders` lists an instance only while `landHeld` is true for it:
not while open, declined, spent or gated, and not another role's.
- The tick starts a listed holder once, gives it `land_work`, and starts
nothing for an unlisted one or the same decision twice.
- A holder whose run started before the resolution: the tick stops that
run, logs `land-relaunch`, and the next tick starts it with
`land_work`. A run started after the resolution is left running.
- After the landing spends the decision, the tick starts nothing.
- A host stopped and started with a held decision starts the holder on
its first tick.
Mutants: one per behavior above, with the killing test in the packet.
### Out of scope
- The land op itself (row 137) and the contracts' two-run flow (row 149).
- Thread instances that hold a merge decision.
- A live smoke. Acceptance 314 steps 5 and 7 run after the re-pin, with
Mos's go.
### Gate
Filbert approves on #1651, naming the candidate manifest digest. Sage
reruns the full gate at the landing target, and it must be green. The
bus is touched, so the live re-pin needs Mos's go.