Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
25 kB · 393 lines
Python
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394import json, math, htmlSD="/tmp/claude-1000/-home-jmm-Code-blogposts/7ba1f72e-807b-4749-bcd3-a065da793e6d/scratchpad"S=json.load(open(f"{SD}/stats.json"))e=lambda s: html.escape(str(s))n=lambda v: f"{v:,}"
# ---- ordinal ramp, rank1 = darkest (validated 5 steps, both modes)# ordinal red ramp, rank 1 brightest -> rank 5 darkest (validated on #000000)RAMP=["#ffb3a5","#ff8066","#ff4d2e","#e0230a","#961407"]
def bars(rows, maxv, unit): """horizontal emphasis bars: you=accent, others=muted""" H,GAP,LBL = 22,8,168 W=760; plot=W-LBL-64 out=[]; y=0 for r in rows: w=max(2, plot*r["n"]/maxv) cls="tg-b-me" if r["me"] else "tg-b-other" out.append( f'<g class="tg-row"><title>{e(r["handle"])} — {n(r["n"])} {unit}</title>' f'<text class="tg-blbl{" tg-me" if r["me"] else ""}" x="{LBL-8}" y="{y+H/2+4}" text-anchor="end">{e(r["handle"])}</text>' f'<rect class="{cls}" x="{LBL}" y="{y}" width="{w:.1f}" height="{H}" rx="4"/>' f'<text class="tg-bval" x="{LBL+w+8:.1f}" y="{y+H/2+4}">{n(r["n"])}</text></g>') y+=H+GAP return f'<svg class="tg-svg" viewBox="0 0 {W} {y-GAP}" role="img" preserveAspectRatio="xMinYMin meet">{"".join(out)}</svg>'
def pie(p, ramp_class): R,CX,CY=104,120,120 tot=p["total"]; a=-math.pi/2; segs=[] for i,s in enumerate(p["slices"]): frac=s["n"]/tot; a2=a+frac*2*math.pi x1,y1=CX+R*math.cos(a), CY+R*math.sin(a) x2,y2=CX+R*math.cos(a2), CY+R*math.sin(a2) large=1 if frac>0.5 else 0 d=f"M{CX},{CY} L{x1:.2f},{y1:.2f} A{R},{R} 0 {large} 1 {x2:.2f},{y2:.2f} Z" segs.append(f'<path class="tg-sl tg-s{i+1}" d="{d}"><title>{e(s["handle"])} — {n(s["n"])} ({frac*100:.1f}%)</title></path>') if frac>=0.08: am=(a+a2)/2; lx,ly=CX+R*0.62*math.cos(am), CY+R*0.62*math.sin(am) segs.append(f'<text class="tg-pct tg-p{i+1}" x="{lx:.1f}" y="{ly:.1f}">{frac*100:.0f}%</text>') a=a2 svg=f'<svg class="tg-pie {ramp_class}" viewBox="0 0 240 240" role="img" preserveAspectRatio="xMidYMid meet">{"".join(segs)}</svg>' leg="".join( f'<li><span class="tg-sw tg-s{i+1}"></span><span class="tg-lh{" tg-me" if s["me"] else ""}">{e(s["handle"])}</span>' f'<span class="tg-lv">{n(s["n"])}<span class="tg-lp">{s["n"]/tot*100:.1f}%</span></span></li>' for i,s in enumerate(p["slices"])) return svg, f'<ol class="tg-legend {ramp_class}">{leg}</ol>'
def piecard(title, sub, p): svg,leg=pie(p,"tg-ramp") return (f'<figure class="tg-pcard"><figcaption><h4>{e(title)}</h4><p>{e(sub)}</p></figcaption>' f'<div class="tg-pbody">{svg}{leg}</div></figure>')
# ---- timeline stacked columnstl=[t for t in S["timeline"] if t["merged"]>0]mx=max(t["merged"] for t in tl)W,Hh,PAD,TOP=760,190,26,16bw=W/len(tl)cols=[]for i,t in enumerate(tl): x=i*bw+bw*0.18; w=bw*0.64 h=(Hh-PAD-TOP)*t["merged"]/mx mh=(Hh-PAD-TOP)*t["minemerged"]/mx oh=h-mh y0=Hh-PAD cols.append(f'<g><title>{t["month"]}: {n(t["merged"])} merged ({n(t["minemerged"])} by permadeath.com)</title>' f'<rect class="tg-b-other" x="{x:.1f}" y="{y0-h:.1f}" width="{w:.1f}" height="{max(0,oh):.1f}" rx="3"/>' + (f'<rect class="tg-b-me" x="{x:.1f}" y="{y0-mh:.1f}" width="{w:.1f}" height="{mh:.1f}" rx="3"/>' if mh>0 else "") + f'<text class="tg-xt" x="{x+w/2:.1f}" y="{Hh-8}" text-anchor="middle">{t["month"][2:].replace("-","/")}</text></g>')tlsvg=f'<svg class="tg-svg" viewBox="0 0 {W} {Hh}" role="img" preserveAspectRatio="xMinYMin meet"><line class="tg-axis" x1="0" y1="{Hh-PAD}" x2="{W}" y2="{Hh-PAD}"/>{"".join(cols)}</svg>'
# ---- my august dailyd=S["myAugustDaily"]; dmx=max(x["n"] for x in d)W2,H2=760,150; bw2=W2/31dcols=[]for i in range(1,32): day=f"2026-08-{i:02d}"; v=next((x["n"] for x in d if x["day"]==day),0) h=(H2-24)*v/dmx; x=(i-1)*bw2+bw2*0.15 dcols.append(f'<g><title>{day}: {v} merged</title><rect class="tg-b-me" x="{x:.1f}" y="{H2-24-h:.1f}" width="{bw2*0.7:.1f}" height="{max(h,0.8):.1f}" rx="3"/></g>') if i in (1,8,15,22,29): dcols.append(f'<text class="tg-xt" x="{x+bw2*0.35:.1f}" y="{H2-8}" text-anchor="middle">{i}</text>')pk=max(d,key=lambda x:x["n"]); pi=int(pk["day"][-2:])dcols.append(f'<text class="tg-vlbl" x="{(pi-1)*bw2+bw2*0.5:.1f}" y="{H2-24-(H2-24)-2:.1f}" text-anchor="middle">{pk["n"]} on {pi} Aug</text>')daysvg=f'<svg class="tg-svg" viewBox="0 0 {W2} {H2}" role="img" preserveAspectRatio="xMinYMin meet"><line class="tg-axis" x1="0" y1="{H2-24}" x2="{W2}" y2="{H2-24}"/>{"".join(dcols)}</svg>'
def table(rows, unit, tot): tr="".join(f'<tr{" class=tg-me-row" if r["me"] else ""}><td>{i+1}</td><td>{e(r["handle"])}</td>' f'<td class="tg-num">{n(r["n"])}</td><td class="tg-num">{r["n"]/tot*100:.1f}%</td></tr>' for i,r in enumerate(rows)) return f'<table class="tg-tbl"><thead><tr><th>#</th><th>Handle</th><th class="tg-num">{e(unit)}</th><th class="tg-num">Share</th></tr></thead><tbody>{tr}</tbody></table>'
L=S["leaderboards"]; me=S["me"]; oth=S["others"]; sc=S["scope"]; ds=S["distribution"]mt=me["medianTimeToMergeMin"]; ot=oth["medianTimeToMergeMin"]
CSS = """.tg-stats{ --bg:#000000;--card:#0d0605;--rule:#2a1614;--grid:#1c0f0e;--axis:#3a201d; --brand:#ff0000;--accent:#ff2d16;--deemph:#7d6763; --ink:#ffc9c0;--muted:#b8908a;--dim:#8a6a66; --s1:#ffb3a5;--s2:#ff8066;--s3:#ff4d2e;--s4:#e0230a;--s5:#961407; color-scheme:dark;background:var(--bg);color:var(--ink); font-family:"Sometype Mono","Sometype Mono Fallback",ui-monospace,SFMono-Regular,Menlo,monospace; font-size:13.5px;line-height:1.6; padding:26px 22px;border:1px solid var(--rule);border-radius:3px;max-width:880px;margin:0 auto}.tg-stats *{box-sizing:border-box}.tg-stats h2{font-size:19px;margin:0 0 5px;color:var(--brand);font-weight:700;letter-spacing:-.01em}.tg-stats h3{font-size:14px;margin:0 0 3px;color:var(--brand);font-weight:700; text-transform:uppercase;letter-spacing:.07em}.tg-stats h4{font-size:12px;margin:0 0 2px;color:var(--ink);font-weight:700}.tg-stats p{margin:0;color:var(--ink)}.tg-sub{font-size:11.5px;color:var(--muted);line-height:1.55}.tg-sec{background:var(--card);border:1px solid var(--rule);border-radius:2px;padding:16px 16px 15px;margin:12px 0}.tg-hero{display:flex;flex-wrap:wrap;gap:12px 32px;align-items:flex-end;margin:2px 0}.tg-hero b{display:block;font-size:42px;line-height:1;font-weight:700;letter-spacing:-.04em;color:var(--brand)}.tg-hero span{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.06em}.tg-kpi{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:1px; background:var(--rule);border:1px solid var(--rule);border-radius:2px;overflow:hidden;margin-top:14px}.tg-kpi div{background:var(--card);padding:10px 12px}.tg-kpi b{display:block;font-size:18px;font-weight:700;color:var(--ink);letter-spacing:-.02em}.tg-kpi span{font-size:10.5px;color:var(--dim);text-transform:uppercase;letter-spacing:.05em;line-height:1.4;display:block;margin-top:2px}.tg-svg{width:100%;height:auto;display:block;margin-top:12px;overflow:visible}.tg-b-me{fill:var(--accent)}.tg-b-other{fill:var(--deemph)}.tg-row:hover .tg-b-me,.tg-row:hover .tg-b-other{opacity:.75}.tg-blbl{font-size:11px;fill:var(--muted)}.tg-blbl.tg-me{fill:var(--brand);font-weight:700}.tg-bval{font-size:11px;fill:var(--ink);dominant-baseline:middle}.tg-xt{font-size:10px;fill:var(--dim)}.tg-axis{stroke:var(--axis);stroke-width:1}.tg-grid{stroke:var(--grid);stroke-width:1}.tg-vlbl{font-size:10.5px;fill:var(--ink);font-weight:700}.tg-pies{display:grid;grid-template-columns:repeat(auto-fit,minmax(330px,1fr));gap:12px}.tg-pcard{margin:0;background:var(--card);border:1px solid var(--rule);border-radius:2px;padding:14px}.tg-pcard figcaption p{font-size:10.5px;color:var(--dim)}.tg-pbody{display:flex;gap:14px;align-items:center;margin-top:10px;flex-wrap:wrap}.tg-pie{width:150px;height:150px;flex:0 0 auto}.tg-sl{stroke:var(--card);stroke-width:2}.tg-sl:hover{opacity:.85}.tg-s1{fill:var(--s1)}.tg-s2{fill:var(--s2)}.tg-s3{fill:var(--s3)}.tg-s4{fill:var(--s4)}.tg-s5{fill:var(--s5)}.tg-pct{font-size:14px;font-weight:700;text-anchor:middle;dominant-baseline:middle;pointer-events:none;fill:#1a0806}.tg-pct.tg-p4,.tg-pct.tg-p5{fill:#fff2ef}.tg-legend{list-style:none;margin:0;padding:0;flex:1 1 152px;min-width:152px}.tg-legend li{display:flex;align-items:center;gap:7px;padding:3px 0;font-size:11px;border-bottom:1px solid var(--grid)}.tg-legend li:last-child{border-bottom:0}.tg-sw{width:9px;height:9px;border-radius:1px;flex:0 0 auto}.tg-sw.tg-s1{background:var(--s1)}.tg-sw.tg-s2{background:var(--s2)}.tg-sw.tg-s3{background:var(--s3)}.tg-sw.tg-s4{background:var(--s4)}.tg-sw.tg-s5{background:var(--s5)}.tg-lh{flex:1 1 auto;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tg-lh.tg-me{color:var(--brand);font-weight:700}.tg-lv{color:var(--ink);font-weight:700}.tg-lp{color:var(--dim);font-weight:400;margin-left:6px;font-size:10px}.tg-tbl{width:100%;border-collapse:collapse;font-size:11.5px;margin-top:10px}.tg-tbl th{text-align:left;font-weight:700;color:var(--dim);font-size:10px;text-transform:uppercase; letter-spacing:.06em;padding:5px 8px;border-bottom:1px solid var(--axis)}.tg-tbl td{padding:5px 8px;border-bottom:1px solid var(--grid);color:var(--muted)}.tg-num{text-align:right}.tg-me-row td{color:var(--brand);font-weight:700}.tg-stats details{margin-top:11px}.tg-stats summary{cursor:pointer;font-size:11px;color:var(--dim);padding:3px 0; text-transform:uppercase;letter-spacing:.05em}.tg-stats summary:hover{color:var(--brand)}.tg-stats summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.tg-key{display:flex;gap:16px;font-size:10.5px;color:var(--dim);margin-top:10px;flex-wrap:wrap; text-transform:uppercase;letter-spacing:.05em}.tg-key span{display:flex;align-items:center;gap:6px}.tg-key i{width:10px;height:10px;border-radius:1px;display:inline-block}.tg-note{font-size:10.5px;color:var(--dim);line-height:1.7}.tg-note strong{color:var(--muted)}.tg-note code{font-size:10px;background:#180b0a;color:var(--muted);padding:1px 4px;border-radius:2px}.tg-stats.tg-solo{padding:0;border:none;background:none;max-width:none}.tg-stats.tg-solo>.tg-sec{margin:0}.tg-pies.tg-one{grid-template-columns:minmax(0,440px)}"""
def keyrow(): return ('<div class="tg-key"><span><i style="background:var(--accent)"></i>permadeath.com</span>' '<span><i style="background:var(--deemph)"></i>everyone else</span></div>')
# ---------------------------------------------------------------- provenanceP=S.get("prov",{})def pv(k,fb): v=P.get(k) return fb if v in (None,"",0) and k not in ("myNegative",) else vASOF = pv("asOf","2 September 2026")K1 = pv("knot1Repos",13312)EXTRA = pv("extraRepos",844)AUGTOT = pv("augMergedTotal",2546)AUGAUT = pv("augAuthors",79)ALLAUT = pv("allTimeAuthors",213)MULT = pv("recordMultiple",4.2)PREVM = pv("prevRecordMonth","2026-07")NEG = pv("othNegative",395)NEGTOT = pv("othMergedWithTs",2159)NEGPCT = pv("othNegativePct",18.0)DISPCT = pv("monthDisagreePct",0.4)BACKLOG= pv("backlogTopCount",1334)PEOPLE = pv("peopleCrawled",3291)KNOTS = pv("knots",182)NOTARGET=pv("noTargetRecords",964)UNATTRIB=pv("unattributedRepos",2172)OLDEST = pv("oldestOpen","2025-09")def monthname(ym): try: y,m=ym.split("-"); return f"{['','January','February','March','April','May','June','July','August','September','October','November','December'][int(m)]} {y}" except Exception: return ym
# ---------------------------------------------------------------- blocksBLOCKS=[]def block(slug,html): BLOCKS.append((slug,html))
block("headline", f"""<div class="tg-sec"> <h3>Tangled, by the numbers</h3> <p class="tg-sub">Every pull request on the network — {n(sc['reposScanned'])} repositories, {n(sc['authors'])} authors — as of {ASOF}.</p> <div class="tg-hero" style="margin-top:14px"> <div><b>{n(me['merged'])}</b><span>merged PRs by permadeath.com</span></div> <div><b>{ds['top1Share']}%</b><span>of every merged PR on Tangled</span></div> <div><b>{n(me['commits'])}</b><span>commits landed</span></div> </div> <div class="tg-kpi"> <div><b>{n(sc['totalPulls'])}</b><span>PRs network-wide</span></div> <div><b>{n(sc['mergedPulls'])}</b><span>merged network-wide</span></div> <div><b>{n(ds['authorsWithMerged'])}</b><span>people with a merged PR</span></div> <div><b>{ds['median']}</b><span>median merged PRs per person</span></div> <div><b>{me['augMerged']/max(1,len(S['myAugustDaily'])):.0f}/day</b><span>on each of my {len(S['myAugustDaily'])} active August days</span></div> <div><b>{me['merged']/me['pulls']*100:.0f}%</b><span>of my PRs got merged</span></div> </div></div>""")
block("merged-alltime", f"""<div class="tg-sec"> <h3>Merged pull requests — all time</h3> <p class="tg-sub">Top 15 of {n(ds['authorsWithMerged'])} people who have ever landed a PR on Tangled.</p> {bars(L['mergedAllTime'], L['mergedAllTime'][0]['n'], 'merged PRs')} {keyrow()} <details><summary>Show as table</summary>{table(L['mergedAllTime'],'Merged PRs',sc['mergedPulls'])}</details></div>""")
block("merged-august", f"""<div class="tg-sec"> <h3>Merged pull requests — August 2026</h3> <p class="tg-sub">By merge date. Tangled's busiest month: {n(AUGTOT)} PRs merged network-wide, {me['augMerged']/max(1,AUGTOT)*100:.0f}% of them mine.</p> {bars(L['mergedAugust'], L['mergedAugust'][0]['n'], 'merged PRs')} {keyrow()} <details><summary>Show as table</summary>{table(L['mergedAugust'],'Merged PRs',AUGTOT)}</details></div>""")
block("commits", f"""<div class="tg-sec"> <h3>Commits landed — all time</h3> <p class="tg-sub">Counted from the git patch attached to each merged PR ({n(sc['patchesParsed'])} of {n(sc['mergedPulls'])} patches readable).</p> {bars(L['commitsAllTime'], L['commitsAllTime'][0]['n'], 'commits')} {keyrow()} <details><summary>Show as table — all time</summary>{table(L['commitsAllTime'],'Commits',sc['commitsCounted'])}</details> <details><summary>Show as table — August 2026</summary>{table(L['commitsAugust'],'Commits',S['pies']['commitsAugust']['total'])}</details></div>""")
block("commits-august", f"""<div class="tg-sec"> <h3>Commits landed — August 2026</h3> <p class="tg-sub">Commits inside PRs merged during August, by author.</p> {bars(L['commitsAugust'], L['commitsAugust'][0]['n'], 'commits')} {keyrow()} <details><summary>Show as table</summary>{table(L['commitsAugust'],'Commits',S['pies']['commitsAugust']['total'])}</details></div>""")
PIE_META=[ ("pie-merged-alltime","Merged PRs — all time", f"{n(S['pies']['mergedAllTime']['total'])} merged PRs, {ALLAUT} authors", S['pies']['mergedAllTime']), ("pie-merged-august", "Merged PRs — August 2026",f"{n(S['pies']['mergedAugust']['total'])} merged PRs, {AUGAUT} authors", S['pies']['mergedAugust']), ("pie-commits-alltime","Commits — all time", f"{n(S['pies']['commitsAllTime']['total'])} commits in merged PRs", S['pies']['commitsAllTime']), ("pie-commits-august","Commits — August 2026", f"{n(S['pies']['commitsAugust']['total'])} commits in merged PRs", S['pies']['commitsAugust']),]PIE_CAPTION="The four biggest contributors, brightest first, against everyone else combined."block("pies", '<div class="tg-sec">\n <h3>Share of the network</h3>\n <p class="tg-sub">'+PIE_CAPTION+'</p>\n <div class="tg-pies">' + "".join(piecard(t.replace("—","—"),sub,pd) for _,t,sub,pd in PIE_META) + '</div>\n</div>')for slug,t,sub,pd in PIE_META: block(slug, f'<div class="tg-sec">\n <h3>{t}</h3>\n <p class="tg-sub">{PIE_CAPTION}</p>\n <div class="tg-pies tg-one">' + piecard(t.replace("—","—"),sub,pd) + '</div>\n</div>')
block("timeline", f"""<div class="tg-sec"> <h3>Merged PRs per month, network-wide</h3> <p class="tg-sub">Tangled's whole history. August 2026 is {MULT}× the previous record ({monthname(PREVM)}) — and most of that spike is one person.</p> {tlsvg} {keyrow()}</div>""")
block("august-daily", f"""<div class="tg-sec"> <h3>My August, day by day</h3> <p class="tg-sub">{n(me['augMerged'])} PRs merged across {len(S['myAugustDaily'])} active days — a peak of {max(x['n'] for x in S['myAugustDaily'])} in one day.</p> {daysvg}</div>""")
block("comparison", f"""<div class="tg-sec"> <h3>What an agentic PR looks like</h3> <p class="tg-sub">permadeath.com versus every other author on the network.</p> <table class="tg-tbl"><thead><tr><th>Measure</th><th class="tg-num">permadeath.com</th><th class="tg-num">everyone else</th></tr></thead><tbody> <tr><td>Pull requests opened</td><td class="tg-num">{n(me['pulls'])}</td><td class="tg-num">{n(oth['pulls'])}</td></tr> <tr><td>Merged</td><td class="tg-num">{n(me['merged'])}</td><td class="tg-num">{n(oth['merged'])}</td></tr> <tr><td>Still open</td><td class="tg-num">{n(me['open'])}</td><td class="tg-num">{n(oth['open'])}</td></tr> <tr><td>Merge rate, of PRs actually decided</td><td class="tg-num">{me['merged']/me['decided']*100:.0f}%</td><td class="tg-num">{oth['merged']/oth['decided']*100:.0f}%</td></tr> <tr><td>Commits landed</td><td class="tg-num">{n(me['commits'])}</td><td class="tg-num">{n(oth['commits'])}</td></tr> <tr><td>Commits per merged PR, mean</td><td class="tg-num">{me['commitsPerPull']}</td><td class="tg-num">{oth['commitsPerPull']}</td></tr> <tr><td>Commits per merged PR, median</td><td class="tg-num">{me['medianCommitsPerPull']:.0f}</td><td class="tg-num">{oth['medianCommitsPerPull']:.0f}</td></tr> <tr><td>Median PR description</td><td class="tg-num">{n(int(me['medianBodyLen']))} chars</td><td class="tg-num">{n(int(oth['medianBodyLen']))} chars</td></tr> <tr><td>Repositories touched</td><td class="tg-num">{me['repos']}</td><td class="tg-num">—</td></tr> </tbody></table> <p class="tg-note" style="margin-top:10px"> Merge rate counts only PRs that were actually decided — merged or closed. Counting open PRs as failures would compare one month of my work against the network's backlog: {n(oth['open'])} PRs are still open network-wide, {n(BACKLOG)} of them on a single repository, opened as far back as {monthname(OLDEST)}. On the raw opened-versus-merged basis the same two figures read {me['merged']/me['pulls']*100:.0f}% and {oth['merged']/oth['pulls']*100:.0f}%, which flatters me for no reason other than my account being new. Commit totals are floors: every one of my merged PRs yielded a readable patch, against {oth['patchCoverage']:.0f}% of everyone else's, so their {n(oth['commits'])} is understated by roughly {(100-oth['patchCoverage'])/max(1e-9,oth['patchCoverage'])*100:.0f}%. </p></div>""")
block("method", f"""<div class="tg-sec"> <h3>Method</h3> <p class="tg-note"> Counted from the source records, not from Tangled's index — <code>api.tangled.org</code> was down throughout (every <code>/xrpc/</code> path returning <em>Failed to start container</em>), so these figures were rebuilt from the underlying atproto data. Tangled is not one server: repositories live on knots that anyone can host. Starting from the main knot's repo list, each account's own <code>sh.tangled.repo</code> records were read from its PDS — those records name both the owner and the knot — every knot so discovered was listed in turn, and the cycle repeated until no new accounts appeared. It closed at <strong>{n(KNOTS)} knots, {n(K1)} repositories and {n(PEOPLE)} accounts</strong>. Every account's <code>sh.tangled.repo.pull</code> and <code>sh.tangled.repo.pull.status</code> records were then read directly from its PDS; a pull's state is the status of the newest status record pointing at it, the same derivation the appview performs. Commits come from the patch attached to each merged PR — a gzipped <code>git format-patch</code> blob on newer records, an inline string on older ones. Commits pushed straight to a branch without a PR are not counted. Figures as of {ASOF}. </p> <p class="tg-note" style="margin-top:9px"> <strong>Why not query the pulls directly.</strong> A first attempt asked Constellation which pull records pointed at each repo. That undercounts: the record has changed shape three times — an inline <code>patch</code> string, then <code>rounds</code> with a patch blob, now <code>versions</code> with base and head SHAs — and {n(NOTARGET)} records carry no target field at all, so a backlink index cannot see them. Reading each person's repository finds every record whatever its shape. Verified against Tangled's own web interface on 37 sampled pull requests across every generation and all three states, on knot1 and on self-hosted knots alike: 42 agreed, 0 disagreed. </p> <p class="tg-note" style="margin-top:9px"> <strong>How complete is it.</strong> People were found three ways: repository ownership, backlinks in <a href="https://constellation.microcosm.blue">constellation</a>, and Tangled's own social records — follows, knot and spindle memberships, collaborator entries — read from each person's repository, which is the only channel with no index in front of it. {n(PEOPLE)} accounts were read in all. Checked against Tangled's own web interface: <strong>42 of 42</strong> sampled pull requests had the state derived here, and <strong>71 of 73</strong> were present in the data at all — 97% recall. Adding the social channel found 164 authors the first two channels missed, which moved the totals by about 4%. </p> <p class="tg-note" style="margin-top:9px"> <strong>What it still cannot see.</strong> These figures are read from the source records, so a pull request whose author has since deactivated is unreachable even though Tangled still displays it — about 9% of accounts with no records return an error rather than an empty list. {n(UNATTRIB)} repository DIDs could not be attributed to an owner, mostly repos whose records were deleted. Only 10 of the {n(KNOTS)} knots answer a repo listing at all; the rest run older software, so the repository count is a floor. And the residual gap sits with small contributors, not large ones: every one of the top ten authors was re-counted against their live PDS and matched exactly, give or take pull requests opened while the crawl was running. </p> <p class="tg-note" style="margin-top:9px"> <strong>Not shown: time to merge.</strong> A pull record carries <code>createdAt</code> and its status record carries another — but they are asserted by different people's clients on different machines, and {NEGPCT}% of other authors' merged PRs claim to have merged <em>before</em> they were opened. Month bucketing survives this ({DISPCT}% of merged PRs land in a different month under either field, and the August ranking is unchanged); a duration does not. </p></div>""")
# ---------------------------------------------------------------- writeimport osBODY='<section class="tg-stats">\n<h2>Tangled, by the numbers</h2>\n' \ f'<p class="tg-sub">Every pull request on the network — {n(sc["reposScanned"])} repositories, ' \ f'{n(sc["authors"])} authors — as of {ASOF}.</p>\n\n' \ + "\n\n".join(h for slug,h in BLOCKS if slug not in {p[0] for p in PIE_META}) + "\n</section>"open(f"{SD}/embed.html","w").write(f"<style>{CSS}</style>\n{BODY}\n")
bd=f"{SD}/blocks"; os.makedirs(bd,exist_ok=True)ORDER=["headline","merged-alltime","merged-august","commits","commits-august","pies", "pie-merged-alltime","pie-merged-august","pie-commits-alltime","pie-commits-august", "timeline","august-daily","comparison","method"]by=dict(BLOCKS); written=[]for i,slug in enumerate(ORDER,1): if slug not in by: continue frag=f'<style>{CSS}</style>\n<section class="tg-stats tg-solo">\n{by[slug]}\n</section>\n' fn=f"{i:02d}-{slug}.html"; open(f"{bd}/{fn}","w").write(frag); written.append(fn)
FONT='<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Sometype+Mono:wght@400;700&display=swap">'open(f"{SD}/preview.html","w").write( f'<title>Tangled by the Numbers</title>\n{FONT}\n' f"<style>body{{margin:0;padding:22px;background:#000}}{CSS}</style>\n{BODY}\n")
# a contact sheet so every separate block can be eyeballed at oncesheet="".join( f'<p style="font:11px ui-monospace,monospace;color:#8a6a66;margin:26px 0 6px;' f'text-transform:uppercase;letter-spacing:.08em">blocks/{fn}</p>\n' f'<section class="tg-stats tg-solo">{by[fn.split("-",1)[1][:-5]]}</section>\n' for fn in written)open(f"{SD}/blocks/index.html","w").write( f'<title>Tangled Stat Blocks</title>\n{FONT}\n' f"<style>body{{margin:0;padding:22px;background:#000}}{CSS}</style>\n{sheet}")print("wrote embed.html +", len(written), "blocks:", ", ".join(written))