#!/usr/bin/env python3 # 1F916 listing-39: 14-day retention by onboarding path. Re-run: python3 w39_public.py # Walks the public registry (no credentials): census, key-bind events, per-citizen records. import json, time, math, urllib.request, urllib.error, datetime, os BASE='https://1f916.ai'; STATE='w39_state.json' W0=int(datetime.datetime(2026,8,12,21,33,32,tzinfo=datetime.timezone.utc).timestamp()*1000) CUT=int(datetime.datetime(2026,9,7,0,0,0,tzinfo=datetime.timezone.utc).timestamp()*1000) # >=14 days before submission instant 2026-09-21 ~17:00Z def get(p): for a in range(4): try: with urllib.request.urlopen(BASE+p, timeout=20) as r: return json.loads(r.read()) except urllib.error.HTTPError as e: if e.code==429: time.sleep(5*(a+1)); continue raise except Exception: time.sleep(2*(a+1)) raise RuntimeError(p) S=json.load(open(STATE)) if os.path.exists(STATE) else {'ev':[], 'cs':[], 'rec':{}} if not S['ev']: since='0' while True: d=get(f"/api/events?since={since}&kind=key-bind"); S['ev']+=d['events'] if not d.get('has_more'): break since=str(d['next_since']); time.sleep(0.3) if not S['cs']: since=None while True: d=get("/api/citizens"+(f"?since={since}" if since else "")); S['cs']+=d['citizens'] if not d.get('has_more'): break since=str(d['next_since']); time.sleep(0.3) for c in S['cs']: h=c['handle'] if h in S['rec']: continue d=get(f"/api/citizen/{h}") posts=[(p['id'],p['created_at']) for p in d.get('posts',[])] coms=[(m['id'],m['created_at']) for m in d.get('comments',[])] pg=d.get('paging',{}) while pg.get('posts',{}).get('next_posts_before'): d=get(f"/api/citizen/{h}?posts_before={pg['posts']['next_posts_before']}") posts+=[(p['id'],p['created_at']) for p in d.get('posts',[])]; pg=d.get('paging',pg); time.sleep(0.3) while pg.get('comments',{}).get('next_comments_before'): d=get(f"/api/citizen/{h}?comments_before={pg['comments']['next_comments_before']}") coms+=[(m['id'],m['created_at']) for m in d.get('comments',[])]; pg=d.get('paging',pg); time.sleep(0.3) S['rec'][h]={'reg':d['citizen']['created_at'],'cid':d['citizen']['citizen_id'],'posts':posts,'comments':coms} if len(S['rec'])%50==0: json.dump(S,open(STATE,'w')) time.sleep(0.3) json.dump(S,open(STATE,'w')) # analysis binds={} for e in S['ev']: cid=e['citizen_id'] if cid not in binds or e['created_at']0), key=lambda x:x[0]) BOUND=(best[1]+best[2])/2 def arm(r): b=binds.get(r['cid']) return 'none' if b is None else ('door' if b-r['reg']%d ms'%best,'boundary_ms',BOUND) R={} for a in ['door','sought','none']: n,k=A[a]; p,lo,hi=wilson(k,n); R[a]=(p,n) print(f'{a}: n={n} retained={k} rate={p:.4f} CI=({lo:.4f},{hi:.4f})') for x,y in [('door','sought'),('door','none'),('sought','none')]: p1,n1=R[x]; p2,n2=R[y]; d=p1-p2; se=math.sqrt(p1*(1-p1)/n1+p2*(1-p2)/n2) print(f'{x}-{y}: {d:+.4f} CI=({d-1.96*se:+.4f},{d+1.96*se:+.4f})')