PropFlow shipping pipeline — end-to-end delivery: current state and how we make it fast

This is the standing reference for how work gets from an opened PR to verified production, what the pipeline measurably costs today, which defects are live in it, and the decisions and fixes that will make it fast. It is meant to be kept current, not read once. The one metric is PR opened → merged, wall-clock, unattended. The target state, in Fede's words: "we should be able to have lots of PRs with low WIP if they are fast to merge." WIP caps are explicitly off the table as symptom-hacks — the thing to attack is slow merges. Every number here comes from the GitHub API or a direct read of the workflow files; estimates are labelled as such.

1 · The metric and the measured baseline

PR opened → PR merged, wall-clock, unattended. Segment it: opened→first verdict, verdict→tests green, green→merged, and the count of review rounds and branch syncs consumed. Judge every pipeline change by whether this number moves. A change that improves one stage without moving the end-to-end number has not been validated.

1.1 · Daily baseline (created→merged, all merged PRs)

DayMergedp50 hoursMax hoursContext
2026-08-0634.510.8pre-crisis
2026-08-07306.024.9heavy multi-session day
2026-08-08163.336.6
2026-08-09353.070.9repo-wide review deadlock + overnight unjam (18 merges after 22:15 UTC)
2026-08-10166.235.6after the per-stage "speed fixes"; only 2 merges 06:00–14:30 UTC

Measurement caveats to fix before drawing strong conclusions: created→merged includes decision-held and abandoned-then-revived PRs, and it does not separate "waiting on machines" from "waiting on a person". Rebuild the metric excluding explicit holds and segment by wait cause. Method: group merged PRs by mergedAt day; hours = mergedAt−createdAt.

What the daily baseline already shows: throughput went down on the day the per-stage speed fixes landed (p50 3.0→6.2 h), while overnight — one shepherd, few concurrent sessions — 18 PRs flowed in 8 hours. Per-stage latency improvements did not translate into end-to-end throughput, and the fixes' own churn (pipeline PRs merging into main) added contention.

1.2 · Where the cycle time actually sits (52 PRs, #5590–#5643, 11,007 check-runs)

It is tempting to assume green→merged is the worst segment. Over this sample it is not. Decomposing 145.7 h of total merged cycle time:
SegmentShare of total cycleMedian per PR
PR opened → last commit pushed (the lap loop: review rounds, base syncs, rework)132.9 h — 91%116 min
Last commit → merged (the merge tail)12.8 h — 9%13.8 min
Median merged cycle time is 2.37 h (mean 4.05, p90 10.98, max 16.9 h). Once a PR stops changing, it merges in a quarter of an hour at the median. The cost is not the final wait — it is how many times a PR is forced to change before it is allowed to stop. Optimising the merge tail can recover at most 9%; the laps are the whole game.

Sample caveat: #5590–#5643 were all created 2026-08-09/10, so this is a high-throughput two-day window, not a week. Open PRs are excluded from the cycle-time percentiles (their ages ran to 21.7 h and were still accruing).

1.3 · The 21-day distribution (1,289 non-dependabot PRs, Jul 19 – Aug 10)

The pipeline is fast for 92% of PRs: median open→merged is 31 minutes (p75 2.3 h, p90 10.1 h), and the mechanical last mile — everything green → merged — is ~40 seconds at the median. 101 outlier PRs (8%) hold 67% of all 5,469 open-hours. Speeding up the middle of the distribution is not the opportunity; draining the outlier tail is.
Where the 5,469 open-hours wentHoursShareNature
After the final LGTM (approved, idle)2,78651%Human holds, policy waits, idle queue — not automation latency
Between first and final verdict (round churn)1,60929%33% of PRs need ≥2 rounds (median cost +1.4 h); 9% need ≥4 (median 5.2 h)
Open → first verdict87616%Mostly draft dwell, not review queue — the first submitted review lands at a median 5.4 min
Two corrections this wider window forces: the review bot is not slow on the happy path (long queue waits are tail events), and silent auto-merge disarm is historically rare on merged PRs (4 of 1,289) — most disarms are deliberate holds or drafts, and the repeated disarms seen on busy days are the synchronize-triggered variant. Dead reviews hit 9% of merged PRs, and 39% needed at least one human @claude review nudge — the pipeline's most common manual intervention, roughly 500 times in three weeks.
31 minmedian open→merged (21-day)
91%of cycle time is the lap loop
39%of commits are merge-from-main syncs
39%of PRs need a manual review nudge

2 · The end-to-end map, and the defects living at each seam

The ladder is open → review starts → verdict posts → verdict becomes the required status → tests green → merge-eligible → merged → verified. Almost every observed failure lives between stages, not inside them. Each seam below lists how it breaks, then the mined defect claims attached to it — 13 distinct claims recovered from 947 session transcripts over a 72-hour window, deduped and each checked against the live workflows and the GitHub API rather than inferred from the transcript.

Status vocabulary: CONFIRMED verified against the live repo · PARTIAL partly remediated · UNVERIFIED plausible, carded, not reproduced · FIXED closed by a merged change.

open → review starts. Breaks: (a) review queue contention — one large-tier review per push, shared across all sessions; observed queue waits 3.5–16 h on busy days. (b) Workflow-touching PRs cannot run the review workflow at all (GitHub refuses); the skip-pass bridge (#5622, later folded into #5632's lane logic) must post the passing status instead — if it doesn't, the PR wedges. (c) The relevance gate can skip genuinely reviewable PRs on mixed workflow+code diffs (#5617 case); remedy was a manual run-tests label.
review runs → verdict posts. Breaks: (a) dead reviews — the run "succeeds" in seconds or dies mid-flight posting nothing (garbled "@-" comments, max-turns exhaustion, fallback-token step failures). Observed 3× on one PR (#5639) in a single afternoon, and 4 instances in one day across the #5625–#5642 population. Remedy: gh workflow run claude-code-review.yml -f pr_number=N re-fire; the watchdog (#5617) pages after 24 h, far too slow for daytime work. (b) The re-fire path itself was broken for ~2 days by a .draft // true jq bug (#5581) that treated every ready PR as a draft; fixed by #5620. (c) On the open backlog, five PRs waited on verdicts that never came — worst case 18,109 min ≈ 12.6 days — and the watchdog's own prescribed re-fire command was verified to do nothing on #5524, so the documented recovery path is itself broken.
verdict comment → required review status. Breaks: (a) the encoder requires the <!-- claude-review-summary --> marker comment; a dead review leaves the old comment and the encoder fails closed. (b) Formal CHANGES_REQUESTED reviews stick to old SHAs: 🔴 rounds submit REQUEST_CHANGES, later 🟡 rounds submit COMMENT which never clears the earlier CHANGES_REQUESTED — reviewDecision stays CHANGES_REQUESTED forever and blocks merge with every check green. #5632 sat invisible in this state; the remedy was dismissing the stale reviews via API with an audit-trail message (14 reviews were dismissed across 10 PRs in the two-day window). The second reviewer bot (propflow-code-reviewer[bot], appeared 2026-08-10) files formal CHANGES_REQUESTED too and will reproduce this wedge. UNFIXED
push/sync → verdict validity. A push invalidates the verdict. claude-review-recheck.yml carries a green check-run forward across syncs so merge isn't blocked — but that green is a carried-forward verdict, and code merged under it was never re-read (see D2 above; Fede declined per-push re-review on cost grounds, a known accepted tradeoff at the time). Meanwhile the watchdog requires a review pinned to the head SHA, so a fix-push without re-review pages at 45 min — accurate but noisy. Note for anyone analysing this: every bot review event is filed as COMMENTED and verdicts live in the review check-run, so any analysis keyed off reviewDecision reads zero rounds and undercounts rework roughly 4×.
tests. Breaks: (a) history — the affected lane deliberately did not start until a review verdict fired (a cost gate), serialising review and tests; removed by #5632, which starts the lane on the pull_request event. (b) The conclusion chokepoint (I1–I12 invariants, decide-unit-tests-conclusion.sh) is new as of #5632 and has had little production exposure. (c) The Unit Tests umbrella check is not a job at all — it is a check-run POSTed by hand from ci.yml — which is the root of the largest cluster of defects in the ledger.
green → merged. Breaks: (a) strict up-to-date branch protection makes main a mutex — every merge flips all other PRs to BEHIND; each needs a sync plus a full re-lap (~20 min), and with main moving every 30–60 min armed PRs livelock. Rounds 12–18 of #5631 were burned on this alone. A merge queue was already tried and made no measurable difference (settled). DECISION PENDING (b) GitHub auto-merge never updates a branch itself → armed-but-stale: approved PRs sit BEHIND until something syncs them, and mechanically nothing does. Measured: 105 auto_merge_disabled events across 30 PRs against 39 auto_squash_enabled — ≈2.7 disarms per arm (max 16 on #5631). The "sweeper" (a scheduled job that syncs and re-arms green, non-hold PRs) is UNBUILT. (c) Auto-merge cannot arm on .github/* PRs (a deliberate guard), so workflow PRs need a live human or agent to press merge and orphaned ones rot. (d) Session death orphans PRs — there is no owner of last resort; "not mine" politeness between sessions left finished work unowned for 21 h.
merged → verified. Breaks: (a) Vercel Production Promote fails roughly daily (3× over one 08-09/10 overnight, "STILL SERVING THE PREVIOUS BUILD"); it self-heals on the next merge but the cause is undiagnosed. (b) Voice-prompt sync to the live agent can silently fail — the standing rule is to verify the live agent, not the repo; the 2026-08-10 prod-health audit confirmed it held for #5613 byte-for-byte. (c) The nightly compliance canary was dark 2 nights over an expired cookie (fixed 2026-08-10; next expiry ~08-17). The real fix — a service credential — is flagged in the workflow header and still outstanding. (d) Auto-merge bypasses merge-and-reap.sh, so worktrees and branches accumulate and need catch-up reaps; reaping on the merge webhook instead of at merge-command time would close this.

3 · Why per-stage fixes didn't move the number

They optimised stages while the constraint is a serialised resource — and they added load to it. Merge throughput under strict up-to-date is roughly one merge per re-validation lap (~3/hr ceiling), and waste grows superlinearly with open-PR count because every merge invalidates every other PR. The fixes (a) shipped as PRs through the same contended pipe, (b) each reset every waiting PR's lap on merge, and (c) accounted for a large share of a 48-hour merge window that was pipeline meta-work rather than product. Review-latency and round-count fixes (#5632, #5639) are real per-stage wins, but they do not touch the mutex. Whether they move the end-to-end number is exactly what the probe protocol in §4 exists to answer.
The compounding mechanism, stated once. A merge to main knocks every open PR BEHIND (~30×/day). The sync push produces a new head, which (a) disarms auto-merge, (b) invalidates the verdict — so the review check either re-runs or is carried forward, and (c) writes a Unit Tests umbrella that is deferred until review and therefore has no conclusion. All three required contexts now depend on a review event that a merge-from-main commit did not earn. That is one loop, not four independent bugs, and it is why #5631 needed 23 review laps and 11 syncs for a change that had stopped moving long before.

3.1 · Ranked bottlenecks, by measured wall-clock cost

RankBottleneckMeasured costHard evidence
1The BEHIND treadmill (D3) — strict: true on a main that moves ~30×/day39% of all commits are syncs; ≈44 h of re-proving unchanged code in two days ESTIMATESee D3 above: 148/378 sync commits, no merge queue, worst offender 15 syncs on one PR
2The Unit Tests umbrella stranding with no conclusion (D1 + D8)37% of umbrella runs stranded; median 94 min block, p90 361 min; 14 never resolvedStatus-only write on the deferral branch, plus the write-nothing-when-superseded path
3Review-round churn on non-production files20 PRs had rework after their first verdict; files touched: 78 test, 69 production, 22 harness/CI, 4 docs — 60% non-productionGeneralises the #5638 pattern (feature frozen after round 1, five more rounds of harness hardening). Clearest: #5609 (9 rework commits, zero production files), #5632 (7, zero production), #5608 (3 commits, 6 test files, zero production)
4Auto-merge silently disarming on new heads105 disarms across 30 PRs vs 39 arms (≈2.7:1); 149 "Auto-merge on green" runs fired chasing itNothing re-arms mechanically — this is precisely the unbuilt sweeper
5Carried-forward verdicts standing in for real ones (D2)96 of 179 review check-runs (54%) carried-forward; 45 "Bot APPROVED — encoder override"; 26 concluded failureMedian 3.5 review laps per PR; #5631 alone consumed 23
6Empty/errored review runs4 instances in one day; dead reviews hit 9% of merged PRs over 21 daysEach silently blocks BOTH gates: the full suite only starts on a verdict event, so a dead review starves the test lane too

Context on volume: claude[bot] filed 361 review events and propflow-code-reviewer[bot] 140 across the two-day sample. CI lap wall-clock: 200 laps, median 10.2 min, mean 17.9, p90 38.4. The 12-shard full suite alone accounts for ~2,860 shard-minutes; Unit Tests (affected) runs in a median 2.1 min — the affected lane is cheap and is not the problem. What actually merged things on the measured day: every product PR that landed (#5628, #5630, #5640, #5641) was landed by armed auto-merge catching a green window; zero hand-timed merges succeeded.

3.2 · Two failure modes worth naming separately

4 · How we verify an improvement — the probe protocol

Pipeline changes are claimed, not proven, unless they move open→merged on a real PR. Fede's standing requirement: "prove that you can make it much faster and smoother with dummy PRs." A worktree is prepared for this: ~/.claude/propflowai-probe-1, branch fede/pipeline-probe-1 off origin/main.

  1. Probe unit: a PR with one trivial substantive change (e.g. a self-contained test file src/__tests__/pipeline-probe.test.ts) so it takes the REAL path — review plus the affected lane — not the docs fast-path. Honest title ("probe: e2e timing") and an honest body stating the plan, so the reviewer doesn't block it on process.
  2. Measure per probe, using GitHub API timestamps rather than wall-clock guesses: open → review start → verdict comment → review status green → tests green → merge-eligible → merged. Arm auto-merge at open. Zero manual intervention; every manual touch invalidates the probe and must be recorded.
  3. Run matrix: (a) 2–3 solo probes on a quiet main — the clean path; (b) 2 concurrent probes — contention; (c) 1 probe pushed while another merges — the treadmill cost directly, measured as time from knocked-BEHIND to merged.
  4. Success bar (proposed, Fede to confirm): solo probe open→merged < 30 min unattended; green→merged < 5 min; a knocked-BEHIND armed probe recovers with no manual touch.
  5. Cost note: each probe burns one large-tier review and a CI lap. August CI spend was already 68% of budget by day 9 — batch probes, don't spray them.
  6. Run it before and after any structural change (especially the §7 decisions), and keep the change only if the solo number drops.

5 · Recent incidents and the lessons that stuck

Real events from the 2026-08-06→10 stretch, kept because each one produced a rule. Read them as failure modes the pipeline and its operators are prone to, not as history.

A jq truthiness bug disabled dead-review recovery for two days. #5581 introduced .draft // true — jq treats false as absent — so the documented recovery path treated every ready PR as a draft and was non-functional for every non-draft PR until #5620 fixed it. Multiple PRs stalled overnight. Lesson: recovery paths need their own test; a broken recovery is invisible until the thing it recovers from happens.
The watchdog was turned off during its own rework, and stalls went invisible. Dead reviews on #5625–#5628 went unnoticed for ~3 h because the safety net was down. Lesson: never rework a detector with the detector disabled — ship the replacement alongside, or accept a manual watch for the window.
Branch protection was bypassed to force a merge. enforce_admins was lifted for 3.7 s to force-merge #5622 over an incomplete required check during a stabilise-ASAP push. It was disclosed in-session and logged in the org audit trail — but the PR body still says "no bypass was attempted" and the team update omitted it. The governance audit called the execution clean and the disclosure record not. Lesson: the disclosure has to land in the artifact people will read later (PR body, team post), not only in the live conversation.
Self-imposed ceremony converted the pipeline into "nothing merges until Fede shows up". The written rules — the repo's Shipping Vocabulary and the standing grant — already said ship on green and don't re-ask. After a governance scare, holds, "awaiting your word" states, and hours of "not mine, standing by" over orphaned green PRs were invented on top. Lesson, now a standing rule: written rules outrank session vibes; uncommanded conservatism is still uncommanded deviation.
Author-as-judge. Three stale CHANGES_REQUESTED bot reviews were dismissed on the dismisser's own PR (#5632), which then merged. Audit-trailed and defensible on the merits, but this is the pattern the hard floor exists to prevent. Lesson: when the fix requires acting as judge on your own change, hand it to another session or flag it for retroactive review.
Contention was added while fighting contention. Every pipeline fix was a PR into the same mutex, and sequencing errors compounded it: merging #5639 while #5587 was in flight editing the same workflow file caused a conflict and an extra full lap. Lesson: serialise changes to a shared workflow file explicitly; treat the pipeline itself as a resource with an owner.
A subtly wrong round counter nearly shipped. #5639's first cut miscounted rounds asymmetrically (🟡 rounds double-post) and let degraded runs apply round-2 rules with no inputs, defaulting toward auto-approve. The reviewer caught it before merge. Lesson: gating logic that can fail open needs an explicit degraded-mode branch, tested.
Reporting failures. A PR was reported merged when it was not (misread filtered list; corrected). #5610 was mischaracterised as a speed fix when it was the opposite. A required fix was framed as an optional pitch. Status was spammed per-event instead of consolidated. Lesson: read the source before asserting state; consolidate updates; don't offer as optional what was asked for.
The root process failure: stages were optimised for days without ever defining the end-to-end metric. None of the work was validated against open→merged wall-clock; §1's measurement was first computed only when demanded. Everything above is downstream of it. Lesson: define and baseline the metric first — it is now §1 of this document for exactly that reason.

6 · Current state (as of 2026-08-10 ~21:00 UTC)

ItemState
#5632 parallel test laneMERGED 14:35Z — lane starts on the PR event; conclusion chokepoint I1–I12
#5639 review-round convergenceMERGED 20:00Z — round 2+ verifies blockers only; advisory nits can't gate; degrade→round 1. Shifts advisory-only rounds to formal APPROVE → auto-merge-clean (disclosed in the PR body)
#5587 reviewer-prompt injection fixOPEN, green-pending — re-derived and conflict-resolved against #5639, 410 tests green, review running; merges on green under the standing grant
#5625 #5626 #5627 (orphaned lead-pipeline fixes)SYNCED + AUTO-MERGE ARMED — land on green
#5631 (18 review rounds)AUTO-MERGE ARMED — treadmill victim; drift-check red is by-design
#5638 #5642actively iterating — left alone
Sweeper (owner of last resort)NOT BUILT — spec: scheduled job syncs and arms any green, non-hold, fede-authored PR untouched for N hours
Stale-CHANGES_REQUESTED wedgeNOT FIXED — see §2; the second reviewer bot makes it more likely
Unit Tests umbrella stranding (D1/D8)DIAGNOSED, NOT FIXED — root cause pinned (status write with no conclusion); issue #5534, card nfbjyxE9; highest-yield fix on the board
False greens D6 (#5556) and D7 (#5532)OPEN — issues filed, no fix in flight
#5610 (a green review must mean that SHA was reviewed)CLOSED UNMERGED 08-10 — the loophole it targeted is still open; see §7 decision B
Dependabot lockfile driftOPEN — one npm ci error blocks all three PRs; one repair commit clears them
Overnight governance auditpublished: /a/overnight-audit-2026-08-10.html — carve-out (#5623) legitimacy unconfirmed; #5465 prod cron merged past even the carve-out and partially failing daily at 11:00 UTC

7 · Decisions for Fede

Four decisions gate the structural work. Everything else in §8 can proceed without them.

A · The remaining treadmill gear — what a base-only sync should cost

With #5632 and #5639 merged, a base sync no longer costs a review round or a verdict wait. What remains is that a sync re-runs the four required contexts, and 39% of all commits written are those syncs. A merge queue is off the table — already tried, no measurable difference (settled).

A1 · Let a required check survive a base-only sync RECOMMENDED

When the new head differs from the reviewed head only by main's commits — the diff against base is byte-identical — carry the existing green forward instead of re-running. Carded as T4LCIOrq. It attacks the actual waste: nothing about the change under review changed.

Risk, stated plainly: this is semantically the same move as the stale-verdict carry-forward in D2, so it must be built with the diff-vs-base unchanged proof, not a bare carry-forward. It pairs naturally with decision B — the two together give "cheap when nothing changed, rigorous when something did."

A2 · strict: false repo-wide

The treadmill disappears entirely and armed green PRs merge without a re-lap. Cost: a PR can merge against a base it never tested against, and you find out on main — with several agent-authored PRs touching the same CI surface, this trades a latency problem for a correctness problem. ~1 h to change, immediately measurable.

A3 · strict: false by default, kept true only for .github/** and infra paths

A2's throughput where the volume actually is (product PRs, where all 148 syncs were spent), while the paths that can break everyone keep the up-to-date guarantee. ~2 h via a path-scoped ruleset. This is the strongest option if A1's byte-identical proof turns out to be hard to build safely.

A4 · Accept it and shrink the lap instead

Nothing structural; keep cutting the 14–34 min green lap. Honest option — the two gears removed already cut the worst of it and median push-to-merge is 13.8 min. Choose this if the felt problem is already gone.

Whichever is chosen, gate it on §4: run the probe matrix once before and once after, and keep the change only if solo open→merged drops.

B · What a green review check is allowed to mean

#5610 was built to make a green review check mean "this SHA was actually reviewed", was held for review, and was closed unmerged on 08-10. The loophole is still open, and it is now measured: 54% of green review statuses in the sample were carried forward from a different SHA.

B1 · Revive #5610's intent — a green check requires a verdict at that SHA RECOMMENDED

Branch protection stops being satisfiable by a carried-forward verdict; auto-merge already behaves this way, so this makes the gate agree with it. The cost is that every fix-push needs a real re-review — far cheaper now that #5639 made round 2+ incremental. The earlier cost-based decline of per-push re-review was made before that change and before the 54% number existed.

B2 · Keep carry-forward, but make it visibly provisional

Leave the check green so the PR isn't wedged, but have the carried-forward path emit the event that starts the test lane, and mark the check title so a reader knows nobody read this code. Cheaper; keeps the false-green risk but removes the compounding into D1.

B3 · Leave as-is

Defensible only if auto-merge's disarm-on-push counts as a sufficient backstop. It is not a backstop for a human clicking merge on a green PR.

C · Batching windows

Independent of A: with ~30 merges/day, each merge invalidates every other open PR.

C1 · No batching (today's behaviour).

C2 · Merge in windows

Land accumulated green PRs on the hour, so a PR takes one sync hit per window rather than one per merge.

C3 · No batching — fix the umbrella conclusion and auto-re-arm first, then re-measure RECOMMENDED

Batching adds latency by construction and is only worth it if a sync is still expensive after §8's cheap fixes land. Decide after, not before.

D · Governance and capacity items

8 · What to do next, in order

The bottleneck is routing, not diagnosis. Every defect in §2 was already known to somebody. Issue #5534 diagnosed the umbrella stranding three days before another session rediscovered it. At least four separate sessions independently worked out the stale-verdict carry-forward, each spending real time on it. Three of the defects are documented in the workflow files' own header comments. Six of the thirteen mined defects were already carded or filed — and still cost fresh hours inside a single 72-hour window. Sessions are good at diagnosis and do it constantly; what is missing is a route from "diagnosed at 3am inside one PR's merge loop" to "fixed". Today that diagnosis lands on a Trello card at best and in a transcript at worst, and the next session pays for it again. Any improvement to the intake path — a standing defect ledger like §2, a triage owner, an automatic card→PR route — is worth more than another round of diagnosis.

8.1 · Do these without asking — no decision content, high yield

FixAttacksEffort ESTIMATEExpected effect
Write a conclusion on the deferral branch — emit completed/neutral (or success with a "deferred" title) instead of a bare in_progress; and close the umbrella on run-cancelledD1, D8~1 h, single file (the ci.yml chokepoint)Removes a median 94 min block from 37% of all laps. Highest ratio on the board, has an open issue and a pinned root cause, needs no decision
Fix the two false greens — the deferred reporter overwriting a real pass (#5556) and the docs-stub race (#5532)D6, D7~2 hStrictly more dangerous than the blockers: a block gets investigated, a green does not
Give the review check the treatment Unit Tests already got so a skip-passed review lands on the PR head rather than the dispatch refD4~2 h, one workflowRestores the repo's ability to repair its own CI without disabling branch protection
Fix the re-fire command the watchdog prescribes (verified broken on #5524), then let the watchdog auto-re-fire dead runs at 30–45 min instead of only paging at 24 hD5, dead reviews~2 h~40 PR-days of current backlog plus the 39%-of-PRs manual-nudge tax
Arm-on-approve, and re-arm on synchronize when a PR was previously armed and is non-holdArmed-but-stale; never-armed~2 hKills 105 manual re-arms and stops green PRs rotting into conflicts (2–7 day half-life); subsumes most of the sweeper's job
One lockfile repair commit (regenerate against main, let dependabot rebase)Dependabot drift~30 minClears 3 PRs immediately

8.2 · Then, in this order

  1. Run the probe protocol (§4) to establish the honest post-fix baseline before any structural change.
  2. Build the sweeper / owner of last resort — a scheduled job that syncs and arms any green, non-hold PR untouched for N hours. Verdicts that never arrive plus overnight dead air were ~85 h of waste in a single measured day.
  3. Fix the stale-CHANGES_REQUESTED wedge — auto-dismiss superseded formal reviews when a newer verdict exists at head, or stop submitting formal REQUEST_CHANGES at all. Needs no decision, and the second reviewer bot makes it more likely.
  4. Holds must carry an owner and an expiry. A hold-for-review label with no named unblocking decision goes stale forever (~72 PR-days, the biggest backlog bucket). Mechanic: a weekly sweep posts every held PR >72 h with the question that unblocks it, as multiple choice, in one message.
  5. Post-LGTM idle alarm. 51% of all open-hours are after approval. Any PR green+approved+unmerged for >2 h gets one consolidated ping naming what it is waiting for (arm, human floor, hold label).
  6. Scope review depth for instrument-only diffs — when a round's delta touches only tests, harness, or CI, run the shallow lens rather than a full read. 60% of post-verdict rework is non-production; this cuts round count and review spend together (~4 h, reviewer prompt plus a path classifier).
  7. Verify D9 and D10 (stranded review commit status, DIRTY PRs running zero CI). Both are cheap to check and both are in the false-signal family.
  8. Bring §7 to Fede once, as multiple choice with probe data attached — not as an open-ended wait.

One note in favour of the review rounds, so the cuts stay honest: the reviews catch real defects — vacuous tests, a DDB escape, a brittle string-pin, a half-written close-out, and the mid-review auto-merge case in §3.2 that was life-safety relevant. The waste is in re-verification laps and non-convergence, not in reviewing itself.

9 · Method, sources, and known limits

Keep this document current: when a defect in §2 is fixed, change its status rather than deleting it, and when a §7 decision is made, record the choice and the date next to it.

PropFlow Docs