Files
jason.woltje 5d4a617762 bus: row 241 one decision lands once; a broker test with two checkLand tickets on one decision, where the second recordLand refuses decision-consumed and records nothing (row 241, #1747, P19 441)
Megavolt's candidate 3e3ed303e on megavolt/row241, manifest e8266864 (2 paths:
agents/megavolt/work/queue-241/BUILD.md, packages/bus/tests/land.test.mjs). Tests and
packet only, no source change. Duckworth approved (comment 29870, rev 2628).

Sage's slot on bbf3b1ee8 (0444acd08 plus these 2 paths):
- mutants M1 (#spent recheck, broker.mjs:963), M2 (CAS old value, seat working.mjs:391),
  M3 (land-busy, cli git-socket.mjs:351): all killed
- full gate: 15 packages 2797 pass 0 fail (bus 500, cli 565), 10 scripts/test-*.sh suites 429 pass 0 fail plus 156 node tests in the queue suite, 03:21:13Z to 03:34:16Z

Row 232's landing e65a3ab4d sits between that base and this commit. Its 9 paths are
disjoint from these 2 and touch no bus code. No live change.
2026-10-20 22:34:30 -05:00

4.2 KiB

Row 241: one decision lands once (#1747, P19 441)

Megavolt, 2026-10-21 (from date -u). Brief: docs/plans/2026-10-20_agent-credentials.md § "Brief: row 237 follow-up, one decision lands once, with tests at the broker", c25785d3d. Source: row 237 residual 3, megavolt-0068, megavolt-0069 and sage-1651. Base: origin/refactor f5753c0e8, row 210's landing. Reviewer Duckworth, gate owner Sage. Tests only; no source changes.

Why landing has no row 237 race

Row 237 fixed publish: two tickets on one decision both pushed, because the decision is spent only after the push, and a push to a remote fork can't be taken back. Landing has the same ticket shape (checkLand opens a ticket, recordLand spends the decision), but three guards keep one decision from landing twice:

  1. land-busy, the outer guard. doLand in packages/cli/src/git-socket.mjs:351 refuses land-busy while another landing runs. The slot is taken before the first await, and freed only when the landing ends. There is one git socket per launcher, and one host per dataRoot (host.lock). So through the socket two landings never overlap, and a later one stops at checkLand with decision-consumed. Even past that, applying the same reviewed patch to the new main fails base-conflict in prepareLand.
  2. The CAS on main. finishLand (packages/seat/src/working.mjs:391) moves main with update-ref main <commit> <old>, so a landing prepared on a tip that has since moved refuses base-moved and writes nothing.
  3. The recordLand recheck. finishLand calls record(), which is recordLand, under the same repository lock. recordLand runs #landFacts again (packages/bus/src/broker.mjs:963), which refuses decision-consumed once the decision is spent. When record() throws, finishLand moves main back.

Guards 2 and 3 can't be reached end to end, because guard 1 stops a second landing first. So they are pinned below the socket, and no test bypasses land-busy.

The unrecorded-commit window

Between the update-ref and the end of record(), main names the squash, which isn't recorded yet. That window is under the repository's lock, so no other push, review or landing in this host sees it. If record() refuses, main moves back. If moving back fails, finishLand refuses rollback-failed and main keeps naming an unrecorded commit (tested at working.test.mjs:555, the main.lock case). That is existing behaviour, unchanged here.

The test

packages/bus/tests/land.test.mjs:312. Two tickets pass checkLand on one decision before either records. recordLand on the first lands. recordLand on the second refuses decision-consumed (:319), and leaves one merge action.allowed, one RESULT and no action.refused. landRefused, as the socket would call it, then files the refusal and ends the ticket.

The assert that pins the recordLand recheck is :319. The #spent line is shared with checkLand, so dropping it also fails :144 (a checkLand after the decision is spent). Only :319 shows the recheck at record time.

Single file on this branch: land.test.mjs 18/18. Whole suites on this branch before in-review: bus 500/500, seat 164/164, cli 565/565 (02:52 to 03:01Z).

Mutants

Mutant Change Killed by
M1 drop the #spent line in #landFacts (broker.mjs:963) land.test.mjs: :312 at :319, and :94 at :144 (16/2)
M2 drop land.old from the update-ref in finishLand (working.mjs:391) working.test.mjs:555 at :567, the base-moved assert (25/1)
M3 drop the land-busy throw (git-socket.mjs:351) git-socket.test.mjs:812 at :825, the land-busy assert (23/1)

Each run was one test file on base f5753c0e8 (row 210's landing, which moved the #spent line from :945 to :963), 2026-10-21 02:51Z. Each assert failed with "Missing expected exception" or "rejection". cmp confirmed each source matched its saved copy after the restore. M2 is killed by the existing working.test:555, so per the brief working.test.mjs isn't changed. Logs: /mnt/storage/scratch/megavolt-r241/ (m1.log to m3.log, with each diff).

Gate

Per the brief: Duckworth approves the manifest; the bus, seat and cli suites and every scripts/test-*.sh are green on Sage's gate; the three mutants are killed.