blob: c840b2582b9e5179740c392cbd7b107c4854af2b [file]
<\!-- SPDX-License-Identifier: Apache-2.0
Reference rendering of the pr-management-stats dashboard (see ../../../skills/pr-management-stats/export.md).
Worked example over apache/airflow, 2026-06-09. Numbers are illustrative; the panel set + layout are the spec. -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Apache Airflow โ€” PR Backlog Dashboard</title>
<style>
:root{ --bg:#0d1117; --card:#161b22; --bd:#30363d; --fg:#e6edf3; --mut:#8b949e;
--grn:#2ea043; --ylw:#d29922; --red:#da3633; --blu:#388bfd; --pur:#a371f7; }
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);font:15px/1.5 -apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;padding:24px;max-width:1100px;margin:0 auto}
h1{font-size:24px;margin:0 0 4px} h2{font-size:18px;margin:30px 0 12px;border-bottom:1px solid var(--bd);padding-bottom:6px}
h3{font-size:14px;margin:16px 0 6px}
.ctx{color:var(--mut);font-size:13px;margin-bottom:6px}
.caveat{color:var(--ylw);font-size:12px;background:rgba(210,153,34,.1);border:1px solid var(--ylw);border-radius:6px;padding:8px 12px;margin:10px 0}
.good{color:var(--grn);font-size:12px;background:rgba(46,160,67,.1);border:1px solid var(--grn);border-radius:6px;padding:8px 12px;margin:10px 0}
.heroes{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin:16px 0}
.hero{background:var(--card);border:1px solid var(--bd);border-radius:10px;padding:16px}
.hero .lbl{color:var(--mut);font-size:12px;text-transform:uppercase;letter-spacing:.5px}
.hero .big{font-size:30px;font-weight:700;margin-top:6px}
.hero .sub{font-size:12px;color:var(--mut);margin-top:4px}
.warn{color:var(--ylw)} .bad{color:var(--red)} .ok{color:var(--grn)}
table{border-collapse:collapse;width:100%;margin:8px 0;font-size:13.5px}
th,td{border:1px solid var(--bd);padding:6px 10px;text-align:left}
th{background:#1c2128;color:var(--mut);font-weight:600}
tr:nth-child(even) td{background:#11151b}
.pri-HIGH{color:var(--red);font-weight:700} .pri-MED{color:var(--ylw);font-weight:700} .pri-LOW{color:var(--blu)} .pri-INFO{color:var(--mut)}
code{background:#1c2128;border:1px solid var(--bd);border-radius:4px;padding:1px 5px;font-size:12px}
.bar{display:inline-block;height:12px;background:var(--grn);border-radius:2px;vertical-align:middle}
.funnel{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin:10px 0}
.fcell{background:var(--card);border:1px solid var(--bd);border-radius:8px;padding:12px;text-align:center}
.fcell .n{font-size:24px;font-weight:700} .fcell .l{font-size:11px;color:var(--mut);margin-top:4px}
.yes{color:#3fb950;font-weight:600} .no{color:#f85149;font-weight:600} .part{color:#d29922;font-weight:600}
.foot{color:var(--mut);font-size:12px;margin-top:30px;border-top:1px solid var(--bd);padding-top:12px}
</style>
</head>
<body>
<h1>๐Ÿ“Š Apache Airflow โ€” PR Backlog Dashboard</h1>
<div class="ctx">Scope: <b>apache/airflow</b> ยท <b>500</b> open PRs ยท closed/merged since 2026-04-28 (6 wk) ยท viewer <code>@potiuk</code> ยท <b>refreshed 2026-06-09 (post-cleanup)</b></div>
<div class="good">โœ“ Effect of this session's cleanup: ready queue <b>236 โ†’ 217</b> (โˆ’19: 5 merged + 13 changes-requested stripped + 1 flipped), stale-ready (&gt;4wk) <b>105 โ†’ 88</b> (โˆ’17).</div>
<div class="caveat">โš  The closed/merged velocity series hit GitHub's 1000-result Search cap โ€” weeks 0โ€“3 are authoritative, 4โ€“5 truncated.</div>
<div class="heroes">
<div class="hero"><div class="lbl">Total open</div><div class="big">500</div><div class="sub">405 non-draft ยท 95 draft</div></div>
<div class="hero"><div class="lbl">Non-maintainer</div><div class="big">373</div><div class="sub">contributor-authored (127 are maintainers' own)</div></div>
<div class="hero"><div class="lbl">Ready for review (non-maint.)</div><div class="big warn">211</div><div class="sub bad">of 217 ready; 88 are &gt;4 wk old</div></div>
<div class="hero"><div class="lbl">Drafts (author's court)</div><div class="big">95</div><div class="sub">57 non-maintainer ยท 19% of open</div></div>
</div>
<div class="ctx" style="margin:6px 0 2px"><b>Ready-for-review queue (211) split by review state</b> โ€” same colors as the "why waiting" chart below:</div>
<div class="funnel" style="grid-template-columns:repeat(4,1fr)">
<div class="fcell"><div class="n" style="color:#da3633">108</div><div class="l">Never reviewed<br>(no maintainer look yet)</div></div>
<div class="fcell"><div class="n" style="color:#388bfd">67</div><div class="l">Discussed,<br>no decision</div></div>
<div class="fcell"><div class="n" style="color:#d29922">13</div><div class="l">Changes requested<br>(author's court)</div></div>
<div class="fcell"><div class="n" style="color:#2ea043">23</div><div class="l">Approved<br>(awaiting merge)</div></div>
</div>
<div class="ctx" style="background:#11151b;border:1px solid #30363d;border-radius:6px;padding:10px 12px">
<b>What the numbers above mean</b> &nbsp;(all counts are of currently-<i>open</i> PRs):
<br>โ€ข <b>Total open โ€” 500:</b> every open PR on apache/airflow (drafts + non-drafts, contributors + maintainers).
<br>โ€ข <b>Non-maintainer โ€” 373:</b> of those, the ones authored by <b>contributors</b> (not committers). The other 127 are maintainers' own PRs, which triage/review treat separately. This 373 is the work the triage + review pipeline actually serves.
<br>โ€ข <b>Ready for review (non-maint.) โ€” 211:</b> contributor PRs carrying the <code>ready for maintainer review</code> label โ€” i.e. they cleared triage and are <b>waiting on a maintainer to review</b>. (6 maintainer-authored PRs also carry the label but are excluded โ€” maintainers self-manage.)
<br>โ€ข <b>Drafts (author's court) โ€” 95:</b> open PRs in GitHub <i>draft</i> state โ€” work-in-progress the author still owns; not awaiting maintainer action.
<br><b>The 4 coloured cards</b> break the 211 ready PRs down by <b>what each is actually waiting for</b> (same colours + the line chart further below): <span style="color:#da3633"><b>108 never reviewed</b></span> (no maintainer has looked yet โ€” the real backlog), <span style="color:#388bfd"><b>67 discussed but no decision</b></span> (a maintainer engaged but neither approved nor requested changes), <span style="color:#d29922"><b>13 changes requested</b></span> (really the author's court โ€” mislabeled ready), <span style="color:#2ea043"><b>23 approved</b></span> (review done, just awaiting a merge).
</div>
<h2>๐Ÿšฆ Triage funnel <span class="ctx" style="font-weight:400">(mutually exclusive ยท 494 triage-scope PRs; the 6 maintainer-authored <code>ready</code> PRs are excluded โ€” triage skips collaborator PRs)</span></h2>
<div class="funnel">
<div class="fcell"><div class="n warn">211</div><div class="l">Ready (queue, non-maint.)</div></div>
<div class="fcell"><div class="n">15</div><div class="l">Responded</div></div>
<div class="fcell"><div class="n">34</div><div class="l">Waiting (author silent)</div></div>
<div class="fcell"><div class="n">139</div><div class="l">Waiting for human maintainer comment</div></div>
<div class="fcell"><div class="n">95</div><div class="l">Drafts (author's court)</div></div>
</div>
<div class="ctx">Every open non-maintainer PR sits in exactly one of these five mutually-exclusive states (left โ†’ right โ‰ˆ closest-to-merge โ†’ furthest):
<br>โ€ข <b>Ready (queue) โ€” 211:</b> carries the <code>ready for maintainer review</code> label โ†’ awaiting a maintainer's review. This is the bottleneck (see the split above).
<br>โ€ข <b>Responded โ€” 15:</b> a maintainer left review feedback and the author has <i>since replied / pushed</i> โ€” back in the maintainer's court for a re-look.
<br>โ€ข <b>Waiting (author silent) โ€” 34:</b> a maintainer left feedback and the author has <i>not</i> responded for &gt;7 days โ€” the author's court; stale-ping candidates.
<br>โ€ข <b>Waiting for human maintainer comment โ€” 139:</b> open non-draft PRs with no <code>ready</code> label and no maintainer triage marker โ€” <i>no human maintainer has engaged yet</i>. <b>Not</b> all neglected contributor work: <b>79 of 139 (57%) are maintainers' own PRs</b> (triage skips collaborator PRs โ€” they self-manage); only <b>60 are contributor PRs genuinely awaiting a first triage pass</b>, most brand-new (76 &lt;1 wk; 25 &gt;4 wk). Real "needs first triage" โ‰ˆ 60, not 139.
<br>โ€ข <b>Drafts (author's court) โ€” 95:</b> PRs in GitHub <i>draft</i> state โ€” work-in-progress the <b>author</b> owns, <b>not awaiting maintainer action</b> (either opened as draft, or triage converted them for failing CI / conflicts / staleness). They re-enter the funnel only when the author re-marks "Ready for review."</div>
<h2>๐Ÿ”Ž Why the ready-for-review queue (211) is actually waiting <span class="ctx" style="font-weight:400">โ€” non-maintainer PRs only (the 6 maintainer-authored ready PRs are excluded โ€” triage skips collaborator PRs); by age</span></h2>
<svg viewBox="0 0 640 215" width="100%" style="max-width:640px;background:#0d1117;border:1px solid #30363d;border-radius:8px">
<line x1="55" y1="185.0" x2="585" y2="185.0" stroke="#21262d"/>
<text x="48" y="189" fill="#8b949e" font-size="10" text-anchor="end">0</text>
<line x1="55" y1="145.0" x2="585" y2="145.0" stroke="#21262d"/>
<text x="48" y="149" fill="#8b949e" font-size="10" text-anchor="end">10</text>
<line x1="55" y1="105.0" x2="585" y2="105.0" stroke="#21262d"/>
<text x="48" y="109" fill="#8b949e" font-size="10" text-anchor="end">20</text>
<line x1="55" y1="65.0" x2="585" y2="65.0" stroke="#21262d"/>
<text x="48" y="69" fill="#8b949e" font-size="10" text-anchor="end">30</text>
<line x1="55" y1="25.0" x2="585" y2="25.0" stroke="#21262d"/>
<text x="48" y="29" fill="#8b949e" font-size="10" text-anchor="end">40</text>
<text x="55" y="203" fill="#8b949e" font-size="10" text-anchor="middle">>12w</text>
<text x="188" y="203" fill="#8b949e" font-size="10" text-anchor="middle">8-12w</text>
<text x="320" y="203" fill="#8b949e" font-size="10" text-anchor="middle">4-8w</text>
<text x="452" y="203" fill="#8b949e" font-size="10" text-anchor="middle">2-4w</text>
<text x="585" y="203" fill="#8b949e" font-size="10" text-anchor="middle">0-2w</text>
<text x="55" y="16" fill="#e6edf3" font-size="11" font-weight="600">211 ready PRs by why they wait (oldest left -> newest right)</text>
<polyline points=" 55,173.0 188,169.0 320,61.0 452,49.0 585,41.0" fill="none" stroke="#da3633" stroke-width="2.5"/>
<circle cx="55" cy="173.0" r="3" fill="#da3633"/>
<text x="55" y="166" fill="#da3633" font-size="9" text-anchor="middle">3</text>
<circle cx="188" cy="169.0" r="3" fill="#da3633"/>
<text x="188" y="162" fill="#da3633" font-size="9" text-anchor="middle">4</text>
<circle cx="320" cy="61.0" r="3" fill="#da3633"/>
<text x="320" y="54" fill="#da3633" font-size="9" text-anchor="middle">31</text>
<circle cx="452" cy="49.0" r="3" fill="#da3633"/>
<text x="452" y="42" fill="#da3633" font-size="9" text-anchor="middle">34</text>
<circle cx="585" cy="41.0" r="3" fill="#da3633"/>
<text x="585" y="34" fill="#da3633" font-size="9" text-anchor="middle">36</text>
<polyline points=" 55,121.0 188,145.0 320,121.0 452,121.0 585,149.0" fill="none" stroke="#388bfd" stroke-width="2.5"/>
<circle cx="55" cy="121.0" r="3" fill="#388bfd"/>
<text x="55" y="114" fill="#388bfd" font-size="9" text-anchor="middle">16</text>
<circle cx="188" cy="145.0" r="3" fill="#388bfd"/>
<text x="188" y="138" fill="#388bfd" font-size="9" text-anchor="middle">10</text>
<circle cx="320" cy="121.0" r="3" fill="#388bfd"/>
<text x="320" y="114" fill="#388bfd" font-size="9" text-anchor="middle">16</text>
<circle cx="452" cy="121.0" r="3" fill="#388bfd"/>
<text x="452" y="114" fill="#388bfd" font-size="9" text-anchor="middle">16</text>
<circle cx="585" cy="149.0" r="3" fill="#388bfd"/>
<text x="585" y="142" fill="#388bfd" font-size="9" text-anchor="middle">9</text>
<polyline points=" 55,181.0 188,181.0 320,165.0 452,161.0 585,185.0" fill="none" stroke="#d29922" stroke-width="2.5"/>
<circle cx="55" cy="181.0" r="3" fill="#d29922"/>
<text x="55" y="174" fill="#d29922" font-size="9" text-anchor="middle">1</text>
<circle cx="188" cy="181.0" r="3" fill="#d29922"/>
<text x="188" y="174" fill="#d29922" font-size="9" text-anchor="middle">1</text>
<circle cx="320" cy="165.0" r="3" fill="#d29922"/>
<text x="320" y="158" fill="#d29922" font-size="9" text-anchor="middle">5</text>
<circle cx="452" cy="161.0" r="3" fill="#d29922"/>
<text x="452" y="154" fill="#d29922" font-size="9" text-anchor="middle">6</text>
<circle cx="585" cy="185.0" r="3" fill="#d29922"/>
<text x="585" y="178" fill="#d29922" font-size="9" text-anchor="middle">0</text>
<polyline points=" 55,173.0 188,173.0 320,161.0 452,157.0 585,169.0" fill="none" stroke="#2ea043" stroke-width="2.5"/>
<circle cx="55" cy="173.0" r="3" fill="#2ea043"/>
<text x="55" y="166" fill="#2ea043" font-size="9" text-anchor="middle">3</text>
<circle cx="188" cy="173.0" r="3" fill="#2ea043"/>
<text x="188" y="166" fill="#2ea043" font-size="9" text-anchor="middle">3</text>
<circle cx="320" cy="161.0" r="3" fill="#2ea043"/>
<text x="320" y="154" fill="#2ea043" font-size="9" text-anchor="middle">6</text>
<circle cx="452" cy="157.0" r="3" fill="#2ea043"/>
<text x="452" y="150" fill="#2ea043" font-size="9" text-anchor="middle">7</text>
<circle cx="585" cy="169.0" r="3" fill="#2ea043"/>
<text x="585" y="162" fill="#2ea043" font-size="9" text-anchor="middle">4</text>
</svg>
<div class="ctx"><span style="color:#da3633">โ– </span> never reviewed (no formal review, no non-triage maintainer comment) &nbsp; <span style="color:#388bfd">โ– </span> discussed, no decision &nbsp; <span style="color:#d29922">โ– </span> changes requested &nbsp; <span style="color:#2ea043">โ– </span> approved (awaiting merge)</div>
<div class="caveat">๐Ÿ”Ž <b>The headline finding:</b> <b>108 of 211 non-maintainer ready PRs (51%) have never had a real maintainer look</b> โ€” and the red "never reviewed" line <b>climbs steeply toward the right</b> (newer PRs) โ€” 36 + 34 + 31 of them are &lt;8 wk old, while only 3โ€“4 are &gt;8 wk โ€” i.e. the <code>ready</code> label is being applied faster than anyone reviews, so fresh PRs pile up unreviewed. Breakdown: never-reviewed <b>108</b> ยท discussed-no-decision <b>67</b> ยท approved-awaiting-merge <b>23</b> ยท changes-requested <b>13</b> (still mislabeled <code>ready</code> โ€” these sit in <i>other</i> areas beyond the two cleaned this session, so a queue-wide bot would catch them all). The "review-bound" diagnosis is really two problems: a <b>first-review gap</b> (the 108) and a <b>decision/merge gap</b> (the 67 + 23). <i>(The 6 maintainer-authored ready PRs are excluded โ€” they self-manage and triage skips them.)</i></div>
<h2>๐Ÿค– Workflow automation potential</h2>
<p>Categorising every decision-point this session by whether the rule is <b>deterministic</b> (a GitHub Action could reproduce it exactly) or needs <b>judgment</b> (LLM/human):</p>
<table>
<tr><th>Workflow step</th><th>Vol.</th><th>Determinism</th><th>CI-automatable?</th></tr>
<tr><td>Mark-ready gate (real-CI-green + mergeable + 0 unresolved + not changes-req)</td><td>53</td><td>Deterministic</td><td class="yes">YES โ€” caught 13/53 false-positives by rule</td></tr>
<tr><td>CI-failing โ†’ draft + violations comment (checkโ†’category map)</td><td>49</td><td>Deterministic</td><td class="yes">YES</td></tr>
<tr><td>Conflict โ†’ rebase comment (<code>mergeable_state=dirty</code>)</td><td>4</td><td>Deterministic</td><td class="yes">YES</td></tr>
<tr><td>Changes-requested โ†’ strip <code>ready</code> + nudge (<code>reviewDecision</code>)</td><td>13</td><td>Deterministic</td><td class="yes">YES</td></tr>
<tr><td>Stale-draft <i>detection</i> (idle thresholds)</td><td>5</td><td>Deterministic</td><td class="yes">YES (flag only)</td></tr>
<tr><td>Workflow-approval safety scan (no workflow-file edits, no exfil patterns)</td><td>9</td><td>Semi</td><td class="part">PARTIAL โ€” scan auto, approve = judgment</td></tr>
<tr><td>Unresolved-thread engagement split (replied/pushed after marker)</td><td>10</td><td>Semi</td><td class="part">PARTIAL</td></tr>
<tr><td>Stale-draft <i>close decision</i></td><td>5</td><td>Judgment</td><td class="no">NO โ€” 3/5 kept (dependency / AIP / active first-timer)</td></tr>
<tr><td>Code-review correctness</td><td>6</td><td>Judgment</td><td class="no">NO โ€” caught a real "doesn't fix the bug" defect</td></tr>
<tr><td>Mentor reply</td><td>1</td><td>Judgment</td><td class="no">NO</td></tr>
</table>
<div class="funnel" style="grid-template-columns:repeat(3,1fr)">
<div class="fcell"><div class="n yes">~80%</div><div class="l">Fully deterministic<br>(124 of ~155 decision-points)</div></div>
<div class="fcell"><div class="n part">~12%</div><div class="l">Semi-deterministic<br>(detection auto, action judgment)</div></div>
<div class="fcell"><div class="n no">~8%</div><div class="l">Judgment-required<br>(review, mentor, close)</div></div>
</div>
<h3 class="ok">Highest-value CI automations (in order)</h3>
<table>
<tr><th>#</th><th>GitHub Action</th><th>Why it wins</th></tr>
<tr><td>1</td><td><b>ready-label bot</b> โ€” auto add/remove <code>ready for maintainer review</code> on every push from: real CI green (not just bot checks) + mergeable + 0 unresolved threads + <code>reviewDecision โ‰  CHANGES_REQUESTED</code></td><td>The mark-ready gate is 100% deterministic yet humans get it wrong <b>25%</b> of the time (the rollup-SUCCESS-but-real-CI-never-ran trap). A bot runs it continuously and never gets fooled โ€” would have prevented all 13 false-positives + kept the 13 changes-requested out of the queue automatically.</td></tr>
<tr><td>2</td><td><b>red-CI drafter</b> โ€” convert to draft + post the quality-violations comment when required checks fail</td><td>49 PRs this session; pure check-nameโ†’category mapping.</td></tr>
<tr><td>3</td><td><b>conflict / changes-requested sweeper</b> โ€” comment + relabel on <code>dirty</code> or <code>CHANGES_REQUESTED</code></td><td>17 PRs; trivially rule-based, keeps the queue honest about author-vs-maintainer court.</td></tr>
<tr><td>4</td><td><b>stale flagger</b> โ€” label (not close) drafts idle &gt; threshold</td><td>Detection is safe to automate; the <i>close</i> stays human (3/5 were wrongly closeable).</td></tr>
</table>
<p class="ctx"><b>Keep human/LLM:</b> code-review correctness, mentor replies, and close-vs-keep decisions โ€” these are where the session's judgment actually changed outcomes (caught a non-fixing "fix", kept 3 PRs that rules would have closed, unblocked a stuck first-timer).</p>
<h3>Drafts vs closes per week โ€” <span class="warn">contributor PRs only</span> <span class="ctx">(bot &amp; backport PRs excluded; wk 0โ€“3 reliable)</span></h3>
<svg viewBox="0 0 640 215" width="100%" style="max-width:640px;background:#0d1117;border:1px solid #30363d;border-radius:8px">
<line x1="55" y1="185.0" x2="585" y2="185.0" stroke="#21262d"/>
<text x="48" y="189" fill="#8b949e" font-size="10" text-anchor="end">0</text>
<line x1="55" y1="145.0" x2="585" y2="145.0" stroke="#21262d"/>
<text x="48" y="149" fill="#8b949e" font-size="10" text-anchor="end">13</text>
<line x1="55" y1="105.0" x2="585" y2="105.0" stroke="#21262d"/>
<text x="48" y="109" fill="#8b949e" font-size="10" text-anchor="end">27</text>
<line x1="55" y1="65.0" x2="585" y2="65.0" stroke="#21262d"/>
<text x="48" y="69" fill="#8b949e" font-size="10" text-anchor="end">41</text>
<line x1="55" y1="25.0" x2="585" y2="25.0" stroke="#21262d"/>
<text x="48" y="29" fill="#8b949e" font-size="10" text-anchor="end">55</text>
<text x="55" y="203" fill="#8b949e" font-size="10" text-anchor="middle">wk-3</text>
<text x="232" y="203" fill="#8b949e" font-size="10" text-anchor="middle">wk-2</text>
<text x="408" y="203" fill="#8b949e" font-size="10" text-anchor="middle">wk-1</text>
<text x="585" y="203" fill="#8b949e" font-size="10" text-anchor="middle">now</text>
<text x="55" y="16" fill="#e6edf3" font-size="11" font-weight="600">Drafts vs closes per week (contributor PRs only, wk 0-3 reliable)</text>
<polyline points=" 55,121.0 232,153.0 408,161.7 585,54.1" fill="none" stroke="#d29922" stroke-width="2.5"/>
<circle cx="55" cy="121.0" r="3" fill="#d29922"/><text x="55" y="114" fill="#d29922" font-size="9" text-anchor="middle">22</text>
<circle cx="232" cy="153.0" r="3" fill="#d29922"/><text x="232" y="146" fill="#d29922" font-size="9" text-anchor="middle">11</text>
<circle cx="408" cy="161.7" r="3" fill="#d29922"/><text x="408" y="154.7" fill="#d29922" font-size="9" text-anchor="middle">8</text>
<circle cx="585" cy="54.1" r="3" fill="#d29922"/><text x="585" y="47.1" fill="#d29922" font-size="9" text-anchor="middle">45</text>
<polyline points=" 55,135.5 232,33.7 408,80.3 585,123.9" fill="none" stroke="#da3633" stroke-width="2.5"/>
<circle cx="55" cy="135.5" r="3" fill="#da3633"/><text x="55" y="128.5" fill="#da3633" font-size="9" text-anchor="middle">17</text>
<circle cx="232" cy="33.7" r="3" fill="#da3633"/><text x="232" y="26.7" fill="#da3633" font-size="9" text-anchor="middle">52</text>
<circle cx="408" cy="80.3" r="3" fill="#da3633"/><text x="408" y="73.3" fill="#da3633" font-size="9" text-anchor="middle">36</text>
<circle cx="585" cy="123.9" r="3" fill="#da3633"/><text x="585" y="116.9" fill="#da3633" font-size="9" text-anchor="middle">21</text>
</svg>
<div class="ctx"><span style="color:#d29922">โ– </span> draft-conversions (<code>ConvertToDraft</code> events) &nbsp; <span style="color:#da3633">โ– </span> closed-unmerged. <b>Bot-authored and backport PRs (base โ‰  <code>main</code>) are removed</b> โ€” that stripped <b>76 of 203 closes</b> (51 bot + 57 backport = release-branch housekeeping, not contributor triage) and 4 of 127 drafted PRs. wk-0 draft bump is partly this session; closes wk 4โ€“5 truncated by the Search cap.</div>
<h3>Who does it โ€” triage process vs author, and @potiuk's share <span class="ctx">(contributor PRs, 6-wk window)</span></h3>
<table>
<tr><th>Action</th><th>Total</th><th>By triage (maintainer acted)</th><th>By author (self)</th><th>@potiuk</th></tr>
<tr><td><b>Draft-conversions</b></td><td>173</td><td class="warn">125 &nbsp;(72%)</td><td>48 &nbsp;(28%)</td><td><b>90</b> โ€” 52% of all ยท <b>72% of triage</b></td></tr>
<tr><td><b>Closes</b></td><td>127</td><td>67 &nbsp;(53%)</td><td>59 &nbsp;(46%)</td><td><b>7</b> โ€” 6% of all ยท 10% of triage</td></tr>
</table>
<div class="funnel" style="grid-template-columns:repeat(2,1fr)">
<div class="fcell"><div class="n warn">72%</div><div class="l">of triage <b>drafts</b> by @potiuk<br>(one-maintainer-heavy โ†’ prime automation target)</div></div>
<div class="fcell"><div class="n ok">10%</div><div class="l">of triage <b>closes</b> by @potiuk<br>(closing is @kaxil-heavy, see below)</div></div>
</div>
<h3>Closing stats per person <span class="ctx">(contributor PRs closed by a maintainer โ‰  author; n = 67)</span></h3>
<table>
<tr><th>Maintainer</th><th>Contributor PRs closed</th><th>Share</th><th></th></tr>
<tr><td><b>@kaxil</b></td><td>40</td><td>60%</td><td><span class="bar" style="width:240px"></span></td></tr>
<tr><td>@potiuk</td><td>7</td><td>10%</td><td><span class="bar" style="width:42px"></span></td></tr>
<tr><td>@pierrejeambrun</td><td>6</td><td>9%</td><td><span class="bar" style="width:36px"></span></td></tr>
<tr><td>@bbovenzi</td><td>5</td><td>7%</td><td><span class="bar" style="width:30px"></span></td></tr>
<tr><td>@choo121600</td><td>3</td><td>4%</td><td><span class="bar" style="width:18px"></span></td></tr>
<tr><td>@shahar1</td><td>2</td><td>3%</td><td><span class="bar" style="width:12px"></span></td></tr>
<tr><td>@Lee-W / @jason810496 / @eladkal / @ashb</td><td>1 each</td><td>~1%</td><td><span class="bar" style="width:6px"></span></td></tr>
</table>
<div class="ctx"><b>Both triage actions are single-maintainer-concentrated, but different people:</b> @potiuk does <b>72%</b> of triage <i>drafting</i>, @kaxil does <b>60%</b> of contributor-PR <i>closing</i>. Drafting is fully deterministic โ†’ automate it (red-CI drafter). Closing needs judgment (3/5 close candidates this session were wrongly closeable) โ†’ keep human, but the @kaxil concentration is a bus-factor worth spreading. Definitions: <b>triage</b> = actor โ‰  author; <b>author</b> = self-action.</div>
<h2>๐Ÿ”ฅ Pressure by area (refreshed)</h2>
<table>
<tr><th>#</th><th>Area</th><th>Score</th><th>Open PRs</th></tr>
<tr><td>1</td><td><code>area:task-sdk</code></td><td>173</td><td>106</td></tr>
<tr><td>2</td><td><code>area:providers</code></td><td>159</td><td>134</td></tr>
<tr><td>3</td><td><code>area:API</code></td><td>112</td><td>72</td></tr>
<tr><td>4</td><td><code>(no area)</code></td><td>67</td><td>63</td></tr>
<tr><td>5</td><td><code>area:dev-tools</code></td><td>63</td><td>47</td></tr>
<tr><td>6</td><td><code>area:UI</code></td><td>59</td><td>42</td></tr>
<tr><td>7</td><td><code>area:DAG-processing</code></td><td>56</td><td>40</td></tr>
<tr><td>8</td><td><code>area:Scheduler</code></td><td>51</td><td>45</td></tr>
</table>
<h2>๐Ÿ“ˆ Closure velocity (wk 0โ€“3, from prior snapshot)</h2>
<table>
<tr><th>Week ago</th><th>Merged</th><th>Closed</th><th>Bar (merged)</th></tr>
<tr><td>0</td><td>235</td><td>55</td><td><span class="bar" style="width:235px"></span></td></tr>
<tr><td>1</td><td>202</td><td>64</td><td><span class="bar" style="width:202px"></span></td></tr>
<tr><td>2</td><td>161</td><td>60</td><td><span class="bar" style="width:161px"></span></td></tr>
<tr><td>3</td><td>171</td><td>24</td><td><span class="bar" style="width:171px"></span></td></tr>
</table>
<div class="ctx">Avg 192 merged/wk ยท ~314 opened/wk. Inflow runs ahead of closes โ€” but the exact net is cap-limited (see Trends caveat).</div>
<h2>๐Ÿ“‰ Trends over time (last 6 weeks)</h2>
<div class="caveat">โš  <b>Data limit:</b> GitHub's Search API caps results at 1000, so closes older than ~4 weeks are truncated. That makes the <i>absolute backlog</i> and <i>net-flow</i> for weeks 4โ€“5 unreliable (they read artificially low). Weeks <b>0โ€“3</b> below are authoritative. <b>True historical trends need an uncapped daily snapshot job</b> โ€” itself a good automation (a tiny scheduled Action writing <code>open_count / ready_count / per-area</code> to a CSV would give exact week-over-week deltas the Search API cannot).</div>
<h3>PRs opened per week, by author class <span class="ctx">(reliable wk 0โ€“3)</span></h3>
<svg viewBox="0 0 640 215" width="100%" style="max-width:640px;background:#0d1117;border:1px solid #30363d;border-radius:8px">
<line x1="55" y1="185.0" x2="585" y2="185.0" stroke="#21262d"/>
<text x="48" y="189" fill="#8b949e" font-size="10" text-anchor="end">0</text>
<line x1="55" y1="145.0" x2="585" y2="145.0" stroke="#21262d"/>
<text x="48" y="149" fill="#8b949e" font-size="10" text-anchor="end">45</text>
<line x1="55" y1="105.0" x2="585" y2="105.0" stroke="#21262d"/>
<text x="48" y="109" fill="#8b949e" font-size="10" text-anchor="end">90</text>
<line x1="55" y1="65.0" x2="585" y2="65.0" stroke="#21262d"/>
<text x="48" y="69" fill="#8b949e" font-size="10" text-anchor="end">135</text>
<line x1="55" y1="25.0" x2="585" y2="25.0" stroke="#21262d"/>
<text x="48" y="29" fill="#8b949e" font-size="10" text-anchor="end">180</text>
<text x="55" y="203" fill="#8b949e" font-size="10" text-anchor="middle">wk-3</text>
<text x="232" y="203" fill="#8b949e" font-size="10" text-anchor="middle">wk-2</text>
<text x="408" y="203" fill="#8b949e" font-size="10" text-anchor="middle">wk-1</text>
<text x="585" y="203" fill="#8b949e" font-size="10" text-anchor="middle">now</text>
<text x="55" y="16" fill="#e6edf3" font-size="11" font-weight="600">PRs opened / week by author class</text>
<polyline points=" 55,169.9 232,147.7 408,165.4 585,155.7" fill="none" stroke="#a371f7" stroke-width="2.5"/>
<circle cx="55" cy="169.9" r="3" fill="#a371f7"/>
<text x="55" y="162.9" fill="#a371f7" font-size="9" text-anchor="middle">17</text>
<circle cx="232" cy="147.7" r="3" fill="#a371f7"/>
<text x="232" y="140.7" fill="#a371f7" font-size="9" text-anchor="middle">42</text>
<circle cx="408" cy="165.4" r="3" fill="#a371f7"/>
<text x="408" y="158.4" fill="#a371f7" font-size="9" text-anchor="middle">22</text>
<circle cx="585" cy="155.7" r="3" fill="#a371f7"/>
<text x="585" y="148.7" fill="#a371f7" font-size="9" text-anchor="middle">33</text>
<polyline points=" 55,33.9 232,68.6 408,68.6 585,58.8" fill="none" stroke="#388bfd" stroke-width="2.5"/>
<circle cx="55" cy="33.9" r="3" fill="#388bfd"/>
<text x="55" y="26.9" fill="#388bfd" font-size="9" text-anchor="middle">170</text>
<circle cx="232" cy="68.6" r="3" fill="#388bfd"/>
<text x="232" y="61.6" fill="#388bfd" font-size="9" text-anchor="middle">131</text>
<circle cx="408" cy="68.6" r="3" fill="#388bfd"/>
<text x="408" y="61.6" fill="#388bfd" font-size="9" text-anchor="middle">131</text>
<circle cx="585" cy="58.8" r="3" fill="#388bfd"/>
<text x="585" y="51.8" fill="#388bfd" font-size="9" text-anchor="middle">142</text>
<polyline points=" 55,70.3 232,66.8 408,49.9 585,48.1" fill="none" stroke="#2ea043" stroke-width="2.5"/>
<circle cx="55" cy="70.3" r="3" fill="#2ea043"/>
<text x="55" y="63.3" fill="#2ea043" font-size="9" text-anchor="middle">129</text>
<circle cx="232" cy="66.8" r="3" fill="#2ea043"/>
<text x="232" y="59.8" fill="#2ea043" font-size="9" text-anchor="middle">133</text>
<circle cx="408" cy="49.9" r="3" fill="#2ea043"/>
<text x="408" y="42.9" fill="#2ea043" font-size="9" text-anchor="middle">152</text>
<circle cx="585" cy="48.1" r="3" fill="#2ea043"/>
<text x="585" y="41.1" fill="#2ea043" font-size="9" text-anchor="middle">154</text>
</svg>
<div class="ctx"><span style="color:#a371f7">โ– </span> first-time &nbsp; <span style="color:#388bfd">โ– </span> contributor &nbsp; <span style="color:#2ea043">โ– </span> maintainer. Roughly <b>~48% maintainer-authored, ~43% contributor, ~9% first-timer</b> โ€” steady week over week. Contributor + first-timer inflow (~50%) is exactly the volume the triage automation serves.</div>
<h3>Merged per week <span class="ctx">(reliable wk 0โ€“2; wk 3 partial)</span></h3>
<svg viewBox="0 0 640 215" width="100%" style="max-width:640px;background:#0d1117;border:1px solid #30363d;border-radius:8px">
<line x1="55" y1="185.0" x2="585" y2="185.0" stroke="#21262d"/>
<text x="48" y="189" fill="#8b949e" font-size="10" text-anchor="end">0</text>
<line x1="55" y1="145.0" x2="585" y2="145.0" stroke="#21262d"/>
<text x="48" y="149" fill="#8b949e" font-size="10" text-anchor="end">62</text>
<line x1="55" y1="105.0" x2="585" y2="105.0" stroke="#21262d"/>
<text x="48" y="109" fill="#8b949e" font-size="10" text-anchor="end">125</text>
<line x1="55" y1="65.0" x2="585" y2="65.0" stroke="#21262d"/>
<text x="48" y="69" fill="#8b949e" font-size="10" text-anchor="end">187</text>
<line x1="55" y1="25.0" x2="585" y2="25.0" stroke="#21262d"/>
<text x="48" y="29" fill="#8b949e" font-size="10" text-anchor="end">250</text>
<text x="55" y="203" fill="#8b949e" font-size="10" text-anchor="middle">wk-3</text>
<text x="232" y="203" fill="#8b949e" font-size="10" text-anchor="middle">wk-2</text>
<text x="408" y="203" fill="#8b949e" font-size="10" text-anchor="middle">wk-1</text>
<text x="585" y="203" fill="#8b949e" font-size="10" text-anchor="middle">now</text>
<text x="55" y="16" fill="#e6edf3" font-size="11" font-weight="600">Merged / week</text>
<polyline points=" 55,75.6 232,82.0 408,55.7 585,34.6" fill="none" stroke="#2ea043" stroke-width="2.5"/>
<circle cx="55" cy="75.6" r="3" fill="#2ea043"/>
<text x="55" y="68.6" fill="#2ea043" font-size="9" text-anchor="middle">171</text>
<circle cx="232" cy="82.0" r="3" fill="#2ea043"/>
<text x="232" y="75" fill="#2ea043" font-size="9" text-anchor="middle">161</text>
<circle cx="408" cy="55.7" r="3" fill="#2ea043"/>
<text x="408" y="48.7" fill="#2ea043" font-size="9" text-anchor="middle">202</text>
<circle cx="585" cy="34.6" r="3" fill="#2ea043"/>
<text x="585" y="27.6" fill="#2ea043" font-size="9" text-anchor="middle">235</text>
</svg>
<h3>Open backlog at week-end <span class="ctx">(wk 0โ€“3 only โ€” 4โ€“5 cap-distorted, omitted)</span></h3>
<svg viewBox="0 0 640 215" width="100%" style="max-width:640px;background:#0d1117;border:1px solid #30363d;border-radius:8px">
<line x1="55" y1="185.0" x2="585" y2="185.0" stroke="#21262d"/>
<text x="48" y="189" fill="#8b949e" font-size="10" text-anchor="end">0</text>
<line x1="55" y1="145.0" x2="585" y2="145.0" stroke="#21262d"/>
<text x="48" y="149" fill="#8b949e" font-size="10" text-anchor="end">137</text>
<line x1="55" y1="105.0" x2="585" y2="105.0" stroke="#21262d"/>
<text x="48" y="109" fill="#8b949e" font-size="10" text-anchor="end">275</text>
<line x1="55" y1="65.0" x2="585" y2="65.0" stroke="#21262d"/>
<text x="48" y="69" fill="#8b949e" font-size="10" text-anchor="end">412</text>
<line x1="55" y1="25.0" x2="585" y2="25.0" stroke="#21262d"/>
<text x="48" y="29" fill="#8b949e" font-size="10" text-anchor="end">550</text>
<text x="55" y="203" fill="#8b949e" font-size="10" text-anchor="middle">wk-3</text>
<text x="232" y="203" fill="#8b949e" font-size="10" text-anchor="middle">wk-2</text>
<text x="408" y="203" fill="#8b949e" font-size="10" text-anchor="middle">wk-1</text>
<text x="585" y="203" fill="#8b949e" font-size="10" text-anchor="middle">now</text>
<text x="55" y="16" fill="#e6edf3" font-size="11" font-weight="600">Open backlog at week-end</text>
<polyline points=" 55,89.0 232,62.8 408,52.1 585,39.5" fill="none" stroke="#d29922" stroke-width="2.5"/>
<circle cx="55" cy="89.0" r="3" fill="#d29922"/>
<text x="55" y="82" fill="#d29922" font-size="9" text-anchor="middle">330</text>
<circle cx="232" cy="62.8" r="3" fill="#d29922"/>
<text x="232" y="55.8" fill="#d29922" font-size="9" text-anchor="middle">420</text>
<circle cx="408" cy="52.1" r="3" fill="#d29922"/>
<text x="408" y="45.1" fill="#d29922" font-size="9" text-anchor="middle">457</text>
<circle cx="585" cy="39.5" r="3" fill="#d29922"/>
<text x="585" y="32.5" fill="#d29922" font-size="9" text-anchor="middle">500</text>
</svg>
<div class="ctx">Even within the reliable window the backlog is climbing (330 โ†’ 500 across the cap-clean weeks). Note this slope is partly inflated by the same truncation (older closes missing); the honest read is "backlog is growing, magnitude needs a snapshot job to pin down." This is the strongest argument for the daily-snapshot Action above.</div>
<h2>๐Ÿ“‹ Detailed โ€” top areas (Total / Ready / Waiting / Untriaged / Draft)</h2>
<table>
<tr><th>Area</th><th>Total</th><th>Ready</th><th>Waiting</th><th>Untriaged</th><th>Draft</th></tr>
<tr><td><code>area:providers</code></td><td>134</td><td>50</td><td>11</td><td>35</td><td>30</td></tr>
<tr><td><code>area:task-sdk</code></td><td>106</td><td>41</td><td>9</td><td>38</td><td>18</td></tr>
<tr><td><code>area:API</code></td><td>72</td><td>27</td><td>8</td><td>23</td><td>14</td></tr>
<tr><td><code>(no area)</code></td><td>63</td><td>40</td><td>0</td><td>15</td><td>8</td></tr>
<tr><td><code>area:dev-tools</code></td><td>47</td><td>11</td><td>1</td><td>22</td><td>13</td></tr>
<tr><td><code>area:Scheduler</code></td><td>45</td><td>20</td><td>1</td><td>11</td><td>11</td></tr>
<tr><td><code>area:UI</code></td><td>42</td><td>11</td><td>1</td><td>22</td><td>8</td></tr>
<tr><td><code>area:DAG-processing</code></td><td>40</td><td>16</td><td>3</td><td>12</td><td>8</td></tr>
</table>
<div class="foot">
<b>Bottom line:</b> the queue is review-bound, not triage-bound (211 contributor PRs ready, 88 &gt;4 wk; inflow ~+72/wk). The cleanup measurably shrank the ready queue by 19. <b>~80% of the triage workflow is deterministic and CI-automatable</b> โ€” the single highest-leverage move is a <b>ready-label GitHub Action</b> that runs the mark-ready gate continuously (humans mis-call it 25% of the time). Reserve human/LLM effort for code-review correctness, mentoring, and close-vs-keep โ€” the ~8% where judgment changed outcomes this session.<br><br>
Read-only stats โ€” no mutations. Triage detection via the <code>Pull Request quality criteria</code> marker. Generated by Claude Code (Opus 4.8) for @potiuk.
</div>
</body>
</html>