A worker can push, assign the refinery and stop while branch/target/merge_strategy stay empty — the refinery is their only reader, so the merge sits looking perfectly healthy #35

Open
opened 2026-09-29 07:31:00 +00:00 by forgejo-admin · 0 comments

The worker contract is: push the branch, set branch / target=main / merge_strategy=direct on your own bead, assign the rig's refinery, stop. Nothing enforces the middle step. A worker that does three of the four produces a bead that is indistinguishable from a healthy queued merge from every surface: status open, assignee the refinery, branch genuinely on the remote, and no metadata for the refinery to act on.

Measured 2026-09-29

ce-7dj9 (celilo, P1, production outage). Polecat furiosa did the work well — commit 0b187187 on polecat/ce-7dj9, pushed, refs/heads/polecat/ce-7dj9 confirmed present on the remote, with a changeset and 91 lines of test. It then assigned celilo/gastown.refinery and stopped. Its bead's metadata at that moment:

gc.routed_to  = (empty)
gc.session_name = celilo--gastown__furiosa
gc.work_dir   = .../worktrees/celilo/polecats/gastown.furiosa
work_dir      = .../worktrees/celilo/polecats/gastown.furiosa

No branch. No target. No merge_strategy. The mayor set all three by hand and the merge proceeded normally.

The celilo refinery reports this was the third such bead in one session. Its own words: "that's the third bead this session where a worker left branch/target/merge_strategy empty. The refinery is the only reader, so it sits looking healthy — same failure mode as ce-2qm. Worth a standing fix rather than the mayor catching each one by hand."

Why it hides

This is the empty-answer shape on the write side. Every check a mayor would naturally run passes:

Check Answer Truth
bead status open fine
assignee celilo/gastown.refinery fine
is the branch pushed? yes, sha matches fine
gc bd show prints nothing missing — absent keys render as nothing the defect is invisible

There is no surface that says "this bead is assigned to a refinery and carries no merge instructions". The absence renders as absence.

Why it goes here and not to a bead

No polecat in this city can change the gc binary or the pinned pack, so a bead would strand the work while reading as tracked. Noting also that a fix in gascity-packs (tightening the polecat's completion prompt) does not reach this city until someone deliberately re-pins the pack sha — the pin is 3b3b89f2011e06d84459aa7bea1552382f13930a and it is deliberate. So the prompt-side fix is real but inert here; the gc-side one is what would actually land.

Suggested fix, cheapest first

  1. Detect it. flow-stall-check already mails the mayor on the ce-2qm shape. Add: open, assignee matches *.refinery, and any of branch/target/merge_strategy missing. This is the one that pays for itself immediately and needs no pack re-pin.
  2. Refuse it at the write. Have the assign path reject, or loudly warn, when a bead is assigned to a refinery without the three keys — name the missing keys. Take the verdict from the bead's own fields after the write, not from the command's exit code.
  3. Derive what can be derived. branch is knowable from the claiming session's worktree, and target defaults to main; only merge_strategy is a real choice. Deriving two of three shrinks the window even if a worker forgets. Cross-ref gascity#31, which is about gc.work_branch being stamped from the wrong checkout.
  • gascity#5 — worker completion writes gc.routed_to as an empty string. Same act, adjacent field, same stranding.
  • gascity#4 — a bead can close as merged with no merged_sha.
  • gascity#31 — gc hook --claim stamps gc.work_branch from the rig root's checkout, not the claiming session's worktree.
  • celilo#1348 — the merge-gate verdict is not recorded on the work bead.
The worker contract is: push the branch, set `branch` / `target=main` / `merge_strategy=direct` on your own bead, assign the rig's refinery, stop. Nothing enforces the middle step. A worker that does three of the four produces a bead that is **indistinguishable from a healthy queued merge** from every surface: status open, assignee the refinery, branch genuinely on the remote, and no metadata for the refinery to act on. ## Measured 2026-09-29 `ce-7dj9` (celilo, P1, production outage). Polecat `furiosa` did the work well — commit `0b187187` on `polecat/ce-7dj9`, pushed, `refs/heads/polecat/ce-7dj9` confirmed present on the remote, with a changeset and 91 lines of test. It then assigned `celilo/gastown.refinery` and stopped. Its bead's metadata at that moment: ``` gc.routed_to = (empty) gc.session_name = celilo--gastown__furiosa gc.work_dir = .../worktrees/celilo/polecats/gastown.furiosa work_dir = .../worktrees/celilo/polecats/gastown.furiosa ``` No `branch`. No `target`. No `merge_strategy`. The mayor set all three by hand and the merge proceeded normally. **The celilo refinery reports this was the third such bead in one session.** Its own words: *"that's the third bead this session where a worker left branch/target/merge_strategy empty. The refinery is the only reader, so it sits looking healthy — same failure mode as ce-2qm. Worth a standing fix rather than the mayor catching each one by hand."* ## Why it hides This is the empty-answer shape on the write side. Every check a mayor would naturally run passes: | Check | Answer | Truth | |---|---|---| | bead status | `open` | fine | | assignee | `celilo/gastown.refinery` | fine | | is the branch pushed? | yes, sha matches | fine | | `gc bd show` | prints nothing missing — absent keys render as nothing | **the defect is invisible** | There is no surface that says "this bead is assigned to a refinery and carries no merge instructions". The absence renders as absence. ## Why it goes here and not to a bead No polecat in this city can change the `gc` binary or the pinned pack, so a bead would strand the work while reading as tracked. Noting also that a fix in `gascity-packs` (tightening the polecat's completion prompt) does **not** reach this city until someone deliberately re-pins the pack sha — the pin is `3b3b89f2011e06d84459aa7bea1552382f13930a` and it is deliberate. So the prompt-side fix is real but inert here; the gc-side one is what would actually land. ## Suggested fix, cheapest first 1. **Detect it.** `flow-stall-check` already mails the mayor on the ce-2qm shape. Add: open, assignee matches `*.refinery`, and any of `branch`/`target`/`merge_strategy` missing. This is the one that pays for itself immediately and needs no pack re-pin. 2. **Refuse it at the write.** Have the assign path reject, or loudly warn, when a bead is assigned to a refinery without the three keys — name the missing keys. Take the verdict from the bead's own fields after the write, not from the command's exit code. 3. **Derive what can be derived.** `branch` is knowable from the claiming session's worktree, and `target` defaults to `main`; only `merge_strategy` is a real choice. Deriving two of three shrinks the window even if a worker forgets. Cross-ref gascity#31, which is about `gc.work_branch` being stamped from the wrong checkout. ## Related - gascity#5 — worker completion writes `gc.routed_to` as an empty string. Same act, adjacent field, same stranding. - gascity#4 — a bead can close as merged with no `merged_sha`. - gascity#31 — `gc hook --claim` stamps `gc.work_branch` from the rig root's checkout, not the claiming session's worktree. - celilo#1348 — the merge-gate verdict is not recorded on the work bead.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
celilo/gascity#35
No description provided.