0030 — Every PR has one owner per job, and the PR arms it
- Status: Accepted (the rule). Built in the idle-PR rows in
puller(gera-propflow/local-bin). The freeze-clear re-wake first planned here already existed; see Why. This ADR does not wait on those merges; ADR-0004 puts the record at decision time. It narrows one clause of ADR-0025 decision 1. See What this supersedes. Amended 2026-09-25 (Gera: orphaned PRs get adopted or closed, durably): one job added to the decision-4 table, the orphaned PR. puller marks it (local-bin #264/#267).cleaner --prsacts on it (local-bin #266, dry-run first, with the close half as its own PR). The Driver check-in hands the under-7-day ones to a live Driver (local-bin #265). Amended 2026-10-10 (Gera 2026-10-10, fleet cleanup step 2): the closing rung,cleaner --prs/co.propflow.pr-cleaner, is retired: 305 apply runs closed 0 PRs. puller still marks ORPHANED PRs; nothing closes them automatically. Superseded in part 2026-10-18 by ADR-0068: decision 4's row Waking the Driver that owns it — thenudgecheck-in is retired; fleet-medic pokes a session only while the session pulse reads itneeds-kickoff. - Date: 2026-09-24
- Deciders: Gera, 2026-09-24: "I don't like the theme of on and off when it's a bug… you just want to remove the bug… ship to be one canonical path of the fix." And, on the build: "drive all fixes then test and validate."
The decision
A fix is one canonical path. It is never a switch, an opt-in command, or a list of callers who must remember to call it. If a mechanism covers some PRs and not others, the uncovered ones are the bug, and the fix is to delete the second path, not to arm the first one more widely.
Coverage is triggered by the ARTIFACT, not the PRODUCER. "Every open PR" is observable; "every session that remembered to run
add" is not. A new kind of producer (a Driver's Agent-tool lane, a scriptedgh pr create, a human) must be covered on the day it appears, without anyone noticing it appeared.Every decision that can be blocked is re-evaluated when the blocker clears. A decision taken once, at an event, is not durable: if the event lands while something else is blocking, the decision is lost. Either the owner re-reads on a clock, or the thing that clears the blocker re-fires the decision for everything it was blocking.
One owner per job, per PR:
Job Owner Armed by Merge (propflowai) auto-merge-all.yml— it already carries every incident-hardened rule: the freeze, the cure nomination,hold-for-review, the dangerous-diff hold, the merge-evidence gate, stacked-base rule 4, 🟡/🔴 across both reviewer logins, dismissalsevery pull_requestevent — the PR itselfMerge (repos with no auto-merge-all.yml)PrDriveWorkflow(agent-smith)the PR, via its drive — never both on one repo Seeing an idle PR puller— idle-PR rows in the digest, computed every runenumeration by author, i.e. every fleet PR Waking the Driver that owns it nudgecheck-in (ADR-0017 rule 3: the puller is not a wake source)driver setup, for every Driver — not per-Driver by hand An ORPHANED PR (owner session not live; amendment 2026-09-25) idle ≥ 7d: cleaner --prscloses it with an evidence comment. Under 7d: the live Driver whose area it is adopts it by pushing. The puller only marks it (ADR-0017).puller's ORPHANED mark ( livenessDEAD) on every idle row. Never auto-closed: a PR with no owner stamp, ahold-for-reviewPR, a draftThe merge owner honours
hold-for-review; the normal flow does not apply it. The label is how a fence bot or a dangerous-diff hold stops a merge for a person to read one SHA, and the mechanism must respect it. ADR-0025 decision 1 still stands: nothing holds for a human on the ordinary path. On 2026-09-24, driver-2 and driver-3 held every one of their 47 PRs, costing ~20 PR-hours. Under ADR-0025 that is a doctrine defect in whatever told those Drivers to label at open. It is not a merge-policy choice.The guard asserts the outcome, not the wiring. The health question is "did an open PR sit with nothing running, no verdict pending, and nobody acting for longer than N minutes?" — not "does every caller call the hook?".
tests/test_canonical_paths.pyin agent-smith pins the second kind, and it was blind to the Driver fleet by construction: Driver lanes are notclaude_runnerconsumers.
Why
Measured over 12 hours on 2026-09-24 across the four live Drivers (159 PRs opened, 155 merged): per Driver, 0–24% of PR open-time was dead — nothing running, nothing approved, nobody acting. (A first pass also reported 9–29% "approved-but-unmerged". That figure is withdrawn: it counted 🟢 verdicts that Withdraw an approval the tests contradict later took back because the PR's own required tests failed.)
- Principle 3 is already met for the freeze, and the evidence first offered against it was wrong. The first draft of this ADR said freeze-held verdicts were never re-armed, citing propflowai #11770. They are. Both freeze clearers in
ci.ymlhave re-woken every open PR throughclaude-verdict-landedsince propflowai #8363 (2026-09-13). The 20:25:59Z clear loggedwoke PR #11770at 20:26:08Z, andauto-merge-all.ymlre-ran decide for it. #11770 stayed open because each of its four 🟢s was withdrawn when its ownUnit Testsfailed. The "held — main is red" annotation was the reason at the moment each verdict landed, not the reason it never merged. The lesson is recorded because it is exactly ADR-0009's trap: the first annotation read was taken as the cause. propflowai #11983 only tidies the clearer: it drops a staleAUTO_MERGE_FREEZE_CUREand stops waking drafts and stacked PRs. - The durable PR owner covered almost nothing.
PrDriveWorkflowis armed only by a Slack-Smith turn'sgh pr createor a task's singleprUrl. The Drivers opened 159 PRs; three drives were running, one in propflowai. Principles 1 and 2. - Drivers with nothing queued never woke. driver-page-speed received ~12 timer check-ins; driver-4 received none and sat idle 5.9 h. Principle 4, wake row.
- Blocked verdicts sat unread for hours. A 🔴 on propflowai #11811 went about four hours without being picked up. Principle 6: only an outcome-level guard sees this class without anyone having named it first.
Rejected
- Arm
PrDriveWorkflowon every PR and let it merge. This was the first recommendation, and it was reversed on reading the code.auto-merge-all.ymlis ~3,000 lines of rules, each written after an incident.merge_gate_activityimplements a fraction of them, and the two already disagree on 🟡 (the workflow arms on it since 2026-09-17; the drive's gate is 🟢-only). Arming both would be two merge drivers on one PR, the thingpr_drive.py's own docstring records the founder rejecting. Porting the rules would be a rewrite with the incidents re-learned one at a time. Its wake path also resolves a Slack thread, which Driver sessions do not have. - A guard per pitfall. The pitfall catalog is already hundreds of entries. Each guard covers the case it names; the outcome guard (principle 6) covers the ones nobody has named yet.
- A scheduled re-sweep of every open PR inside
auto-merge-all.yml. It is level-triggered and general, but the workflow's own comments record an event diet that cut ~600 runs a day. Re-firing the decision only for what a cleared blocker was blocking gets the durability at a cost of N dispatches per freeze-clear (4 clears on 2026-09-24).
Consequences
- The freeze clearer already gives the PRs it held a second look (since #8363). Any future claim that a blocked decision is never re-evaluated must be checked against the clearer's own log (
woke PR #N) before anything is built. - The Drivers' check-in wake becomes part of Driver setup rather than something a Driver arms. A Driver that exists and is never woken now counts as a defect, not a quiet night.
PrDriveWorkflowkeeps merging only whereauto-merge-all.ymldoes not exist. If a repo gains the workflow, its drives stop merging in the same PR. Two merge owners on one repo is a bug under principle 4.- The dead-time measurement becomes a standing instrument (the puller's idle-PR rows) rather than a one-off analysis. It is the check that this ADR is being followed.
What this supersedes — precisely, and nothing more
Per ADR-0023 decision 6 and ADR-0025's own convention, ADRs are not rewritten. The superseded clause is marked on its own Status line, appending no line.
| record | clause superseded | what stands |
|---|---|---|
| ADR-0025 decision 1 | "Its machine gate … moves into the PR driver", as applied to propflowai. propflowai's merge owner is auto-merge-all.yml (decision 4 above); PrDriveWorkflow merges only in repos that have no auto-merge-all.yml. |
Everything else in decision 1: merge on green with no human ask, human-authored PRs included, SmithApprovalWorkflow's human wait deleted, the reviewer's 🟢 as the load-bearing gate, and nothing holds for a human on the ordinary path. |
agentflow-relay CLAUDE.md, "AND SINCE 2026-09-12 THE MERGE ITSELF IS THE DRIVER'S" |
"every PR, every repo" as a claim about which system merges. | Amended in the same PR to name the per-repo owner and point here. |