| <\!-- 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 (>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 >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> (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 >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 <1 wk; 25 >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) <span style="color:#388bfd">โ </span> discussed, no decision <span style="color:#d29922">โ </span> changes requested <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 <8 wk old, while only 3โ4 are >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 > 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 & 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) <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 (72%)</td><td>48 (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 (53%)</td><td>59 (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 <span style="color:#388bfd">โ </span> contributor <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 >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> |