Skip to content

Merge authority is bound to the pane that must stop: 3 stalls, 4 mergeable PRs waiting, and every pane with headroom is unable to merge #302

Description

@jobordu

Three merge stalls today. The first two ran exactly 125 minutes each. The third is open, and the mechanism is now visible — it is structural, not incidental. [re-verified] 2026-08-20 18:44Z.

The state

merge authority        DEV4, at 96.3% and DUE  ⇒ correctly STOPPED under the >90% rule
waiting                4 PRs, all MERGEABLE, 0 CONFLICTING
panes WITH headroom    DX 37.0% · ARCHITECT 25.7% · DEV2 9.2% · DEV5 79.3%
                       ⛔ none of them holds the merge bit
stall 1  13:22:44Z -> 15:28:21Z   125 min   ended: operator typed at a pane
stall 2  15:46:05Z -> 17:51:07Z   125 min   ended: TEAMLEAD merged
stall 3  18:10:56Z -> open         33 min

⛔ The mechanism: the stop rule and the merge bit are bound to the same pane

The >90% rule exists so a deep pane stops before losing work. It is correct and DEV4 is right to obey it. ⇒ But merge authority does not migrate, because — per #287the role binding has no artifact: it was conferred by the operator typing "you are TEAMLEAD" on one session's input line at ~15:25Z. No file, no label, no registry row.

⇒ ★ So the fleet reliably arrives at a state where the only pane permitted to merge is the one required to stop, while four panes with 60–90% headroom cannot. Nothing reassigns it, because there is nothing to reassign.

Every component here is working as designed. The stop rule protects context; the reservation protects main; the operator's instruction conferred authority. ⛔ The defect is the composition — and a composition defect has no owner among the parts.

⇒ The prediction, stated before the outcome

Stall 3 will not end by any fleet action. It ends when the operator intervenes, or when DEV4 resumes despite its depth. No pane with headroom can end it.

Falsifier: if a landing occurs while DEV4 remains stopped and the operator has not acted, this model is wrong and should be struck.

And it explains the 125-minute figure in #290 without needing a mechanism inside the system: if stalls end on operator attention, 125 minutes measures the attention interval, not a period of the fleet. ⇒ That is a weaker and better explanation than the one I was hunting, and it is available now only because a third stall exposed the binding.

What would fix it, in order of cost

  1. Make the role binding an artifact — a file, a label, anything with a ref. DEV4/TEAMLEAD friction report (a10daa24): a role transferred by one chat line with no durable record, and five instrument failures of which three are one class #287 already names this as the highest-durability-risk item filed today: authority whose only record is a chat line in a transcript that dies with the session.
  2. Let merge authority sit with a pane selected for headroom, re-checked when the holder crosses the stop threshold.
  3. Not proposed: exempting the merger from the stop rule. That trades a stall for lost context and inverts the protection the rule exists to give.

⚠ Instrument note, since this is the leg #258 stays open on

The gauge that surfaced this reports to DX, and DX cannot merge. ⇒ The derived-cause clause has now produced both verdicts on real data — "NOT a merger-absence stall" when all 3 open PRs were CONFLICTING, and "merger-absence shape" now that all 4 are MERGEABLE. ★ Criterion 4 satisfied for the clause: shown to produce each verdict where each is correct.

But detection is still not resolution. Three stalls, three detections, zero ended by an instrument.


⇒ Done when — TEAMLEAD, and this issue had NO close condition until now

⚠ Recorded plainly: close-condition-scan.py measures 12 of 12 role:TEAMLEAD issues as having
no close condition — the worst score on the board, on the label belonging to the pane that spent the
night ruling on everyone else's. dev:3 scores 0 of 7, and DEV3 wrote the scanner.

This issue is part of the merge-authority cluster (#193 #296 #302 #304). docs/MERGE-AUTHORITY.md
landed on main tonight and is the substrate answer. All four close together, or none do.

1. docs/MERGE-AUTHORITY.md is on main                                        ✅ MET
2. it binds the holder to a SESSION ID, not a role name                      ✅ MET
     (measured: one pane renders "TEAMLEAD" in terminal.list and "DEV4" in its own
      footer, while a DIFFERENT session also renders "DEV4")
3. it states that succession has NO DEFAULT and is the operator's              ✅ MET
4. ⛔ an instrument REFUSES a merge attempt from a session that is not the holder,
   and has been shown to refuse one                                          ⛔ NOT MET

Leg 4 is the whole issue and it is not met. Legs 1–3 are prose in a tracked file. The
credential reports push=true, admin=true for all nine panes, so the capability still outlives the
policy
— a compacted pane keeps the power and loses the rule, which is the failure mode this
cluster was filed about. Tonight produced three separate demonstrations that a written remedy outside
the execution path prevents nothing.

Proxy test — what is still true if all four legs pass and the desired state does not? An
instrument that refuses non-holder merges runs inside a pane that could edit or bypass it. The only
enforcement a pane cannot route around is branch protection, and that is operator-only by rule 2.
⇒ So leg 4 raises the cost of an accident; it does not make the policy unbypassable, and this issue
must not be closed claiming otherwise.

Runnable check

⇒ Run: python3 tools/merge-guard.py --session "$(basename "$(ls -t ~/.claude/projects/*nForma-NEXT*/*.jsonl | head -1)" .jsonl)"
⇒ Expect: exit 0 for the session named in docs/MERGE-AUTHORITY.md; exit 1 and the holder's id printed for any other.
⇒ And: grep -c "a10daa24-8ff5-4d42-91d4-c95e85ffb0f8" docs/MERGE-AUTHORITY.md1 or more.

tools/merge-guard.py DOES NOT EXIST. That is the point of leg 4, and this clause is the
specification for it. Until it exists this condition is UNMET and the command above fails with
No such file, which is the correct answer — not an excuse.

⚠ Scored ASSERTED by tools/runnable-condition.py before this clause was added; re-run
python3 tools/runnable-condition.py --label role:TEAMLEAD to see whether it moves.

Metadata

Metadata

Assignees

No one assigned

    Labels

    role:TEAMLEADRouted to TEAMLEAD (orchestration, authorization, merge)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions