From 7f66253abc57bd862919b55f4659aa581bed3cfe Mon Sep 17 00:00:00 2001 From: SimoneMariaRomeo <180769497+SimoneMariaRomeo@users.noreply.github.com> Date: Wed, 13 May 2026 21:39:28 +0700 Subject: [PATCH 1/2] Add team week customer view --- README.md | 1 + public/customer.json | 215 ++++++++++++++++++++++++- public/index.html | 314 +++++++++---------------------------- scripts/build-customer.mjs | 169 ++++++++++++-------- scripts/check-frontend.mjs | 17 +- 5 files changed, 396 insertions(+), 320 deletions(-) diff --git a/README.md b/README.md index 1d3719d..13b7ef2 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ How the project is landing. Quantum beats filed, sats flow, narrative traction. | `quantum_beats.total` | All quantum-* signals on aibtc.news | | `quantum_beats.by_agent` | Breakdown per agent display name | | `quantum_beats.last_7d` | Rolling week count | +| `team_week` | Current Week-N role/agent output table sourced from issue #33, PRs, and quantum signals | | `sats_flow` | Bounty #30 + #33 + x402 + inscription revenue | | `sats_flow.bounty_33_payout_ledger` | Issue #33 payout requests, paid proof rows, and pending/paid sats totals | | `narrative_traction` | GitHub #33 comments, merged PRs, contributor count | diff --git a/public/customer.json b/public/customer.json index fae87e2..6a92340 100644 --- a/public/customer.json +++ b/public/customer.json @@ -155,5 +155,218 @@ "Regenerate with: node scripts/build-customer.mjs", "bounty_33_payout_ledger is parsed from issue #33 comments. Pending requests are not counted as paid.", "Revenue KV unavailable in this environment; preserved the previous x402 counters." - ] + ], + "team_week": { + "label": "Week 6", + "week_number": 6, + "week_start": "2026-05-09", + "week_end": "2026-05-15", + "source_request_url": "https://github.com/1btc-news/news-client/issues/33#issuecomment-4438428678", + "source_request_author": "1feems", + "rows": [ + { + "role": "Daily Beat Writer", + "agent": "Austere Dragon", + "output": "7 quantum beats filed (0 accepted, 7 submitted)", + "status": "submitted", + "source_url": "https://aibtc.news/api/signals?limit=500" + }, + { + "role": "Daily Beat Writer", + "agent": "Amber Otter", + "output": "4 quantum beats filed (0 accepted, 4 submitted)", + "status": "submitted", + "source_url": "https://aibtc.news/api/signals?limit=500" + }, + { + "role": "Daily Beat Writer", + "agent": "Trustless Indra", + "output": "2 quantum beats filed (0 accepted, 2 submitted)", + "status": "submitted", + "source_url": "https://aibtc.news/api/signals?limit=500" + }, + { + "role": "Daily Beat Writer", + "agent": "Emerald Castle", + "output": "1 quantum beat filed (0 accepted, 1 submitted)", + "status": "submitted", + "source_url": "https://aibtc.news/api/signals?limit=500" + }, + { + "role": "Daily Beat Writer", + "agent": "Grand Unicorn", + "output": "1 quantum beat filed (0 accepted, 1 submitted)", + "status": "submitted", + "source_url": "https://aibtc.news/api/signals?limit=500" + }, + { + "role": "Daily Beat Writer", + "agent": "Opal Gorilla", + "output": "1 quantum beat filed (0 accepted, 1 submitted)", + "status": "submitted", + "source_url": "https://aibtc.news/api/signals?limit=500" + }, + { + "role": "Daily Beat Writer", + "agent": "Shining Tiger", + "output": "1 quantum beat filed (0 accepted, 1 submitted)", + "status": "submitted", + "source_url": "https://aibtc.news/api/signals?limit=500" + }, + { + "role": "Daily Beat Writer", + "agent": "Tall Jett", + "output": "1 quantum beat filed (0 accepted, 1 submitted)", + "status": "submitted", + "source_url": "https://aibtc.news/api/signals?limit=500" + }, + { + "role": "Data Researcher", + "agent": "gregoryford963-sys", + "output": "4 data PRs this week: #63 open, #61 open, #58 open, #49 merged", + "status": "merged", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/63" + }, + { + "role": "Data Researcher", + "agent": "SimoneMariaRomeo", + "output": "4 data PRs this week: #56 open, #55 open, #54 open, #53 open", + "status": "open", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/56" + }, + { + "role": "Visualizer Developer", + "agent": "SimoneMariaRomeo", + "output": "3 visualizer PRs this week: #52 open, #51 open, #47 merged", + "status": "merged", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/52" + }, + { + "role": "Data Researcher", + "agent": "vegita", + "output": "2 data PRs this week: #35 applied, #34 open", + "status": "applied", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/35" + }, + { + "role": "Repository Contributor", + "agent": "gregoryford963-sys", + "output": "2 repo PRs this week: #60 closed, #59 closed", + "status": "closed", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/60" + }, + { + "role": "Visualizer Developer", + "agent": "NoeFabris", + "output": "2 visualizer PRs this week: #41 applied, #40 applied", + "status": "applied", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/41" + }, + { + "role": "x402 Relay Engineer", + "agent": "slashdevcorpse", + "output": "2 x402 PRs this week: #48 merged, #46 merged", + "status": "merged", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/48" + }, + { + "role": "Data Researcher", + "agent": "1feems", + "output": "1 data PR this week: #50 open", + "status": "open", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/50" + }, + { + "role": "Data Researcher", + "agent": "arc0btc", + "output": "1 data PR this week: #37 applied", + "status": "applied", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/37" + }, + { + "role": "Data Researcher", + "agent": "coreymull", + "output": "1 data PR this week: #36 applied", + "status": "applied", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/36" + }, + { + "role": "Data Researcher", + "agent": "Isjuanplayer", + "output": "1 data PR this week: #43 open", + "status": "open", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/43" + }, + { + "role": "Data Researcher", + "agent": "NoeFabris", + "output": "1 data PR this week: #39 applied", + "status": "applied", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/39" + }, + { + "role": "Data Researcher", + "agent": "xoxoskeleton", + "output": "1 data PR this week: #57 open", + "status": "open", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/57" + }, + { + "role": "Repository Contributor", + "agent": "wyslsz", + "output": "1 repo PR this week: #44 closed", + "status": "closed", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/44" + }, + { + "role": "Visualizer Developer", + "agent": "jianmosier", + "output": "1 visualizer PR this week: #62 open", + "status": "open", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/62" + }, + { + "role": "Visualizer Developer", + "agent": "KingParmenides", + "output": "1 visualizer PR this week: #38 open", + "status": "open", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/38" + }, + { + "role": "Visualizer Developer", + "agent": "mySebbe", + "output": "1 visualizer PR this week: #42 merged", + "status": "merged", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/42" + }, + { + "role": "Visualizer Developer", + "agent": "nicbstme", + "output": "1 visualizer PR this week: #45 open", + "status": "open", + "source_url": "https://github.com/Iskander-Agent/quantum-visualizer/pull/45" + }, + { + "role": "Directly Responsible Individual", + "agent": "Iskander-Agent", + "output": "5 coordination updates in #33", + "status": "active", + "source_url": "https://github.com/1btc-news/news-client/issues/33#issuecomment-4453795250" + }, + { + "role": "Player Coach / Review", + "agent": "ThankNIXlater", + "output": "5 review updates in #33", + "status": "active", + "source_url": "https://github.com/1btc-news/news-client/issues/33#issuecomment-4455168106" + }, + { + "role": "Player Coach / Review", + "agent": "lekanbams", + "output": "2 review updates in #33", + "status": "active", + "source_url": "https://github.com/1btc-news/news-client/issues/33#issuecomment-4426411995" + } + ] + } } diff --git a/public/index.html b/public/index.html index 919273c..c11c419 100644 --- a/public/index.html +++ b/public/index.html @@ -78,44 +78,6 @@ .s2 .stat-num{color:var(--blue)}.s2 .stat-bar-fill{background:var(--blue)} .s1 .stat-num{color:var(--gray)}.s1 .stat-bar-fill{background:var(--gray)} -/* Affiliation readiness */ -.affiliation-section{padding:2.5rem 0;border-bottom:1px solid var(--border)} -.affiliation-panel{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-elevated);overflow:hidden} -.affiliation-head{padding:1.25rem 1.5rem;border-bottom:1px solid var(--border);display:flex;align-items:flex-end;justify-content:space-between;gap:1rem;flex-wrap:wrap} -.affiliation-sub{font-size:0.82rem;color:var(--text-secondary);margin-top:0.35rem;max-width:720px;line-height:1.6} -.affiliation-summary{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--border)} -.affiliation-metric{background:var(--bg);padding:1rem;text-align:center;min-width:0} -.affiliation-num{font-family:'JetBrains Mono',monospace;font-size:1.35rem;font-weight:800;letter-spacing:0;color:var(--accent)} -.affiliation-label{font-size:0.62rem;font-weight:700;letter-spacing:0.07em;text-transform:uppercase;color:var(--text-muted);margin-top:0.2rem} -.affiliation-table-wrap{padding:1.25rem;background:var(--bg-elevated);overflow-x:auto;-webkit-overflow-scrolling:touch} -.affiliation-table{min-width:760px} -.affiliation-table tbody tr{cursor:pointer} -.affiliation-table tbody tr.is-active td{background:var(--bg-hover)} -.affiliation-org{font-weight:700;white-space:nowrap} -.affiliation-devs{color:var(--text-secondary);font-size:0.78rem;line-height:1.5;max-width:360px} -.affiliation-score{font-family:'JetBrains Mono',monospace;font-weight:800;font-size:0.95rem;white-space:nowrap} -.affiliation-score.high{color:var(--red)} -.affiliation-score.medium{color:var(--orange)} -.affiliation-score.low{color:var(--blue)} -.affiliation-score.none{color:var(--gray)} -.affiliation-small{font-family:'JetBrains Mono',monospace;font-size:0.78rem;color:var(--text-secondary);white-space:nowrap} -@media(max-width:760px){ - .affiliation-head{align-items:flex-start} - .affiliation-summary{grid-template-columns:1fr 1fr} - .affiliation-table-wrap{padding:0;background:var(--bg-elevated);overflow-x:visible} - .affiliation-table{min-width:0} - .affiliation-table thead{display:none} - .affiliation-table,.affiliation-table tbody,.affiliation-table tr,.affiliation-table td{display:block;width:100%} - .affiliation-table tbody tr{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:0.65rem 0.75rem;padding:1rem 1.25rem;border-bottom:1px solid var(--border)} - .affiliation-table tbody tr:last-child{border-bottom:none} - .affiliation-table tbody tr.is-active{background:var(--bg-hover)} - .affiliation-table td{padding:0;border-bottom:0} - .affiliation-table td::before{content:attr(data-label);display:block;font-size:0.58rem;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--text-muted);margin-bottom:0.18rem} - .affiliation-table td:first-child,.affiliation-table td:last-child{grid-column:1/-1} - .affiliation-org{white-space:normal} - .affiliation-devs{max-width:none} -} - /* Chart section */ .chart-section{padding:2.5rem 0} .chart-card{background:var(--bg-elevated);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden} @@ -207,26 +169,27 @@ .freshness-chip span{font-family:'JetBrains Mono',monospace;color:var(--text-muted);font-size:0.68rem} @media(max-width:760px){.freshness-body{grid-template-columns:1fr}.freshness-kpis{grid-template-columns:1fr 1fr}.freshness-item{grid-template-columns:1fr}} -/* Payout ledger */ -.payout-section{padding:2.5rem 0;border-top:1px solid var(--border)} -.payout-panel{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-elevated);overflow:hidden} -.payout-head{padding:1.25rem 1.5rem;border-bottom:1px solid var(--border);display:flex;align-items:flex-end;justify-content:space-between;gap:1rem;flex-wrap:wrap} -.payout-sub{font-size:0.82rem;color:var(--text-secondary);margin-top:0.35rem;max-width:720px;line-height:1.6} -.payout-body{padding:1.25rem;background:var(--bg)} -.payout-kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--border);border-radius:var(--radius-sm);overflow:hidden;margin-bottom:1rem} -.payout-kpi{background:var(--bg-elevated);padding:0.9rem;min-width:0} -.payout-num{font-family:'JetBrains Mono',monospace;font-size:1.15rem;font-weight:800;letter-spacing:0} -.payout-label{font-size:0.62rem;font-weight:700;letter-spacing:0.07em;text-transform:uppercase;color:var(--text-muted);margin-top:0.2rem} -.payout-table-wrap{border:1px solid var(--border);border-radius:var(--radius-sm);overflow:auto;-webkit-overflow-scrolling:touch} -.payout-table{min-width:760px;font-size:0.82rem} -.payout-table td{background:var(--bg)} -.payout-status{display:inline-flex;align-items:center;justify-content:center;min-width:4.5rem;padding:0.25rem 0.45rem;border:1px solid var(--border);border-radius:100px;font-size:0.68rem;font-weight:700;text-transform:uppercase;letter-spacing:0.05em;color:var(--text-muted);white-space:nowrap} -.payout-status.status-paid{border-color:rgba(34,197,94,0.3);background:rgba(34,197,94,0.1);color:#22c55e} -.payout-status.status-requested,.payout-status.status-acked{border-color:rgba(247,147,26,0.28);background:rgba(247,147,26,0.08);color:var(--accent)} -.payout-link{font-size:0.75rem;color:var(--accent);text-decoration:none;font-weight:500;white-space:nowrap} -.payout-link:hover{text-decoration:underline} -.payout-mono{font-family:'JetBrains Mono',monospace;font-size:0.72rem;color:var(--text-secondary);word-break:break-all} -@media(max-width:760px){.payout-kpis{grid-template-columns:1fr}.payout-body{padding:0.75rem}} +/* Team week */ +.team-section{padding:2.5rem 0;border-top:1px solid var(--border)} +.team-panel{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-elevated);overflow:hidden} +.team-head{padding:1.25rem 1.5rem;border-bottom:1px solid var(--border);display:flex;align-items:flex-end;justify-content:space-between;gap:1rem;flex-wrap:wrap} +.team-sub{font-size:0.82rem;color:var(--text-secondary);margin-top:0.35rem;max-width:680px;line-height:1.6} +.team-body{padding:1.25rem;background:var(--bg)} +.team-kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--border);border-radius:var(--radius-sm);overflow:hidden;margin-bottom:1rem} +.team-kpi{background:var(--bg-elevated);padding:0.9rem;min-width:0} +.team-kpi-num{font-family:'JetBrains Mono',monospace;font-size:1.15rem;font-weight:800;letter-spacing:0} +.team-kpi-label{font-size:0.62rem;font-weight:700;letter-spacing:0.07em;text-transform:uppercase;color:var(--text-muted);margin-top:0.2rem} +.team-table-wrap{border:1px solid var(--border);border-radius:var(--radius-sm);overflow:auto;-webkit-overflow-scrolling:touch} +.team-table{min-width:760px;font-size:0.82rem} +.team-table td{background:var(--bg)} +.team-output{color:var(--text-secondary);line-height:1.5} +.team-status{display:inline-flex;align-items:center;justify-content:center;min-width:4.5rem;padding:0.25rem 0.45rem;border:1px solid var(--border);border-radius:100px;font-size:0.68rem;font-weight:700;text-transform:uppercase;letter-spacing:0.05em;color:var(--text-muted);white-space:nowrap} +.team-status.status-accepted,.team-status.status-merged,.team-status.status-active{border-color:rgba(34,197,94,0.3);background:rgba(34,197,94,0.1);color:#22c55e} +.team-status.status-open,.team-status.status-submitted{border-color:rgba(247,147,26,0.28);background:rgba(247,147,26,0.08);color:var(--accent)} +.team-status.status-needs-update{border-color:rgba(220,38,38,0.25);background:var(--red-bg);color:var(--red)} +.team-source{font-size:0.75rem;color:var(--accent);text-decoration:none;font-weight:500;white-space:nowrap} +.team-source:hover{text-decoration:underline} +@media(max-width:760px){.team-kpis{grid-template-columns:1fr}.team-body{padding:0.75rem}} /* API section */ .api-section{padding:2rem 0;border-top:1px solid var(--border)} @@ -354,37 +317,6 @@

Bitcoin Developer Quantum Urgency Map

- -
-
-
-
-
-

Affiliation Readiness Matrix

-

Institution-level view of quantum urgency concentration, public-position coverage, and silent seats across the developer map.

-
- -
-
-
- - - - - - - - - - - - -
AffiliationDevsAvg ScoreVoicedSilentPriority Names
-
-
-
-
-
@@ -500,32 +432,32 @@

Source Freshness Audit

- -
+ +
-
-
+
+
-

Payout Ledger

-

Customer world model view of bounty #33 payout requests and payment proof. Pending requests stay separate from paid sats until an issue comment or on-chain proof verifies payment.

+

Team Week

+

Customer world model view of current-week output by role, agent, status, and source. This makes the weekly table inspectable without following the full issue thread.

- +
-
-
-
- +
+
+
+
- - - + + + - + - - + +
CommentPRAmountRoleAgentWeek Output StatusVerifierSource
Loading payout ledger...
Loading team week...
@@ -765,54 +697,53 @@

Ecosystem

renderFreshnessAudit(); -function formatSats(value){ -if(value==null||value===''||Number.isNaN(Number(value)))return '--'; -return Number(value).toLocaleString()+' sats'; +function statusClass(value){ +return 'status-'+String(value||'unknown').toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-|-$/g,''); } -function payoutStatusClass(value){ -return 'status-'+String(value||'tracked').toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-|-$/g,''); -} - -function renderPayoutLedger(customer){ -const body=document.getElementById('payout-ledger-body'); -const kpis=document.getElementById('payout-kpis'); -const asof=document.getElementById('payout-asof'); -if(!body||!kpis||!asof)return; -const ledger=customer&&customer.sats_flow&&customer.sats_flow.bounty_33_payout_ledger; -if(!ledger||!Array.isArray(ledger.rows)){ -asof.textContent='Customer model unavailable'; -kpis.innerHTML='
--
requested
--
paid
--
pending
'; -body.innerHTML='Payout ledger data is not available in /customer.json.'; +function renderTeamWeek(customer){ +const body=document.getElementById('team-week-body'); +const range=document.getElementById('team-week-range'); +const kpis=document.getElementById('team-kpis'); +if(!body||!range||!kpis)return; +const team=customer&&customer.team_week; +if(!team||!Array.isArray(team.rows)){ +range.textContent='Customer model unavailable'; +kpis.innerHTML='
--
week
--
rows
--
source
'; +body.innerHTML='Team week data is not available in /customer.json.'; return; } -asof.textContent=ledger.extracted_at?'Extracted '+String(ledger.extracted_at).slice(0,10):'Issue #33'; +const rows=team.rows; +const activeRows=rows.filter(r=>!String(r.status||'').match(/closed|needs update/i)).length; +range.textContent=`${team.week_start||''} to ${team.week_end||''}`.trim(); +const requestSource=team.source_request_url +?`${escapeHtml(team.source_request_author||'request')}` +:'tracked'; kpis.innerHTML=[ -{num:formatSats(ledger.requested_sats),label:'requested'}, -{num:formatSats(ledger.confirmed_paid_sats),label:'confirmed paid'}, -{num:ledger.pending_requests||0,label:'pending rows'} -].map(k=>`
${escapeHtml(k.num)}
${escapeHtml(k.label)}
`).join(''); -const rows=ledger.rows.slice().reverse(); +{num:team.label||('Week '+(team.week_number||'')),label:'current week'}, +{num:rows.length,label:'tracked rows'}, +{num:activeRows,label:'active rows'} +].map(k=>`
${escapeHtml(k.num)}
${escapeHtml(k.label)}
`).join(''); body.innerHTML=rows.length -?rows.map(r=>{ -const verifier=r.txid -?`
${escapeHtml(r.txid)}
` -:r.btc_address?`
${escapeHtml(r.btc_address)}
`:'awaiting proof'; -return ` -${escapeHtml(r.author||'comment')}
${escapeHtml((r.created_at||'').slice(0,10))}
-${r.pr?escapeHtml(r.pr):'--'}
${escapeHtml(r.pr_state||'unknown')}
-${escapeHtml(formatSats(r.amount_sats))} -${escapeHtml(r.state||'tracked')} -${verifier} -`; -}).join('') -:'No payout request or payment proof comments found in issue #33.'; +?rows.map(r=>` +${escapeHtml(r.role)} +${escapeHtml(r.agent)} +${escapeHtml(r.output)} +${escapeHtml(r.status||'tracked')} +${r.source_url?`Open`:'--'} +`).join('') +:'No current-week team output recorded.'; +if(team.source_request_url){ +const first=document.createElement('tr'); +first.innerHTML=`Request${escapeHtml(team.source_request_author||'GitHub')}Weekly source-of-truth table requested in issue #33.open${requestSource}`; +body.prepend(first); +} } fetch('/customer.json') .then(r=>r.ok?r.json():null) -.then(renderPayoutLedger) -.catch(()=>renderPayoutLedger(null)); +.then(renderTeamWeek) +.catch(()=>renderTeamWeek(null)); // Stats const statsEl=document.getElementById('stats'); @@ -894,106 +825,12 @@

Ecosystem

function syncControlsFromState(){ document.querySelectorAll('.pill').forEach(btn=>btn.classList.toggle('active',btn.dataset.score===activeScore)); document.querySelectorAll('.stat-cell').forEach(cell=>cell.classList.toggle('active',activeScore!=='all'&&cell.dataset.score===activeScore)); -document.querySelectorAll('.affiliation-row').forEach(row=>row.classList.toggle('is-active',row.dataset.affiliation===activeAffiliation)); document.getElementById('search').value=searchQuery; affilSelect.value=activeAffiliation; } function escapeHtml(s){return String(s==null?'':s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''})[c]);} -function scoreTone(avg){ -if(avg>=4)return 'high'; -if(avg>=3)return 'medium'; -if(avg>1)return 'low'; -return 'none'; -} - -function collectAffiliationReadiness(){ -const groups=new Map(); -devs.forEach(dev=>{ -const affiliation=(dev.affiliation||'Independent / Unknown').trim()||'Independent / Unknown'; -if(!groups.has(affiliation)){ -groups.set(affiliation,{affiliation,members:[],count:0,totalScore:0,voiced:0,silent:0,urgent:0,pqWork:0}); -} -const group=groups.get(affiliation); -const score=Number(dev.quantum_urgency_score)||1; -group.members.push(dev); -group.count+=1; -group.totalScore+=score; -group.pqWork+=Number(dev.pq_work_volume)||0; -if(score>1)group.voiced+=1; -else group.silent+=1; -if(score>=4)group.urgent+=1; -}); -return Array.from(groups.values()).map(group=>{ -const avg=group.totalScore/Math.max(1,group.count); -return { -...group, -avg, -priority:(group.urgent*4)+(avg*2)+Math.log2(group.count+1)+(group.silent*0.5) -}; -}).sort((a,b)=> -b.priority-a.priority|| -b.urgent-a.urgent|| -b.count-a.count|| -a.affiliation.localeCompare(b.affiliation) -); -} - -function applyAffiliationFilter(affiliation){ -activeAffiliation=affiliation; -activeScore='all'; -searchQuery=''; -activeDevName=null; -syncControlsFromState(); -updateUrlState(); -render(); -document.querySelector('.table-section').scrollIntoView({block:'start'}); -} - -function renderAffiliationReadiness(){ -const groups=collectAffiliationReadiness(); -const visible=groups.slice(0,12); -const summary=[ -{num:groups.length,label:'affiliations'}, -{num:groups.filter(g=>g.count>1).length,label:'multi-dev orgs'}, -{num:groups.reduce((sum,g)=>sum+g.silent,0),label:'silent seats'}, -{num:groups.filter(g=>g.urgent>0).length,label:'urgent orgs'} -]; -document.getElementById('affiliation-count').textContent=visible.length===groups.length?`${groups.length} affiliations`:`Top ${visible.length} of ${groups.length} affiliations`; -document.getElementById('affiliation-summary').innerHTML=summary.map(item=>`
${item.num}
${item.label}
`).join(''); -const body=document.getElementById('affiliation-body'); -if(!visible.length){ -body.innerHTML='No affiliation data available.'; -return; -} -body.innerHTML=visible.map(group=>{ -const priorityNames=group.members.slice().sort((a,b)=> -(Number(b.quantum_urgency_score)||1)-(Number(a.quantum_urgency_score)||1)|| -(a.rank||9999)-(b.rank||9999)|| -a.name.localeCompare(b.name) -).slice(0,4).map(dev=>`${escapeHtml(dev.name)} (${Number(dev.quantum_urgency_score)||1})`).join(', '); -const avg=group.avg.toFixed(1); -return ` -${escapeHtml(group.affiliation)} -${group.count} -${avg} -${group.voiced} -${group.silent} -${priorityNames} -`; -}).join(''); -body.querySelectorAll('.affiliation-row').forEach(row=>{ -row.addEventListener('click',()=>applyAffiliationFilter(row.dataset.affiliation)); -row.addEventListener('keydown',event=>{ -if(event.key==='Enter'||event.key===' '){ -event.preventDefault(); -applyAffiliationFilter(row.dataset.affiliation); -} -}); -}); -} - function render(){ const tbody=document.getElementById('table-body'); tbody.innerHTML=''; @@ -1142,7 +979,6 @@

Ecosystem

drawer.setAttribute('aria-hidden','true'); drawerBackdrop.setAttribute('aria-hidden','true'); -renderAffiliationReadiness(); syncControlsFromState(); render(); const initialDev=findDevByName(activeDevName); diff --git a/scripts/build-customer.mjs b/scripts/build-customer.mjs index b457eaf..9ab8b48 100755 --- a/scripts/build-customer.mjs +++ b/scripts/build-customer.mjs @@ -1,14 +1,15 @@ #!/usr/bin/env node -// Build public/customer.json — Customer World Model snapshot. -// Pulls from aibtc.news signals API (quantum-* beats), GitHub #33 comments via gh, -// and the REVENUE_LOG KV namespace on Cloudflare for x402 paid-call events. -// Fields we cannot verify are explicitly "unknown" — never fabricated. +// Build public/customer.json - Customer World Model snapshot. +// Pulls from aibtc.news signals API, GitHub #33 comments, quantum-visualizer PRs, +// and the REVENUE_LOG KV namespace when Cloudflare credentials are available. +// Fields we cannot verify are explicitly "unknown"; never fabricate missing state. import fs from "fs"; import { execSync } from "child_process"; const OUT = new URL("../public/customer.json", import.meta.url); const today = new Date().toISOString().slice(0, 10); const sevenDaysAgo = new Date(Date.now() - 7 * 864e5).toISOString().slice(0, 10); +const projectStart = "2026-04-04"; const CF_ACCOUNT_ID = "6401c671eef455c629ee2f10cd6cdc61"; const KV_NAMESPACE_ID = "570c38b0f3324aab8afb4b8be15c3479"; @@ -76,77 +77,106 @@ async function fetchRevenueLedger(existing) { } } -function parseSats(body) { - const match = String(body || "").match(/([0-9][0-9,]*)\s*(?:sat|sats|satoshis)\b/i); - return match ? Number(match[1].replace(/,/g, "")) : null; +function daysBetween(a, b) { + return Math.floor((Date.parse(`${b}T00:00:00Z`) - Date.parse(`${a}T00:00:00Z`)) / 86400000); } -function parseBtcAddress(body) { - const match = String(body || "").match(/\bbc1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{20,90}\b/i); - return match ? match[0] : null; +function addDays(date, days) { + const d = new Date(`${date}T00:00:00Z`); + d.setUTCDate(d.getUTCDate() + days); + return d.toISOString().slice(0, 10); } -function parseTxid(body) { - const match = String(body || "").match(/\b[0-9a-f]{64}\b/i); - return match ? match[0] : null; +function plural(n, word) { + return `${n} ${word}${n === 1 ? "" : "s"}`; } -function parsePrNumber(body) { - const text = String(body || ""); - const urlMatch = text.match(/quantum-visualizer\/pull\/(\d+)/i); - if (urlMatch) return Number(urlMatch[1]); - const prMatch = text.match(/\bPR\s*#(\d+)\b/i); - return prMatch ? Number(prMatch[1]) : null; +function stateLabel(state) { + return String(state || "UNKNOWN").toLowerCase(); } -function inferPayoutState(body) { - const text = String(body || "").toLowerCase(); - const negative = /not paid|no paid|no received transaction|pending|awaiting|zero transactions/.test(text); - if (!negative && /paid on-chain|payment sent|payout sent|txid|transaction id|on-chain proof/.test(text)) return "paid"; - if (/(payment|payout) request ack/.test(text)) return "acked"; - if (/payout request|payment request|requesting\s+\*{0,2}[0-9,]+\s*sats|payout route/.test(text)) return "requested"; - return "tracked"; -} - -function makePayoutLedger({ comments, prsRaw }) { - const prsByNumber = new Map(prsRaw.map((p) => [Number(p.number), p])); +function makeTeamWeek({ quantum, comments, prsRaw, weekNumber, weekStart, weekEnd }) { const rows = []; - for (const comment of comments) { - const body = comment.body || ""; - const isPaymentComment = /(payout|payment)\s+(request|route|acked|sent|paid)|requesting\s+\*{0,2}[0-9,]+\s*sats|paid on-chain/i.test(body); - if (!isPaymentComment) continue; - if (!/(sats|bc1|quantum-visualizer\/pull|PR\s*#|txid)/i.test(body)) continue; - const prNumber = parsePrNumber(body); - const pr = prNumber ? prsByNumber.get(prNumber) : null; - const amountSats = parseSats(body); - const state = inferPayoutState(body); + const signalsThisWeek = quantum.filter((s) => (s.utcDate || s.timestamp || "").slice(0, 10) >= weekStart); + const beatsByAgent = new Map(); + for (const signal of signalsThisWeek) { + const name = signal.displayName || "Unknown agent"; + const entry = beatsByAgent.get(name) || { count: 0, accepted: 0, submitted: 0 }; + entry.count += 1; + if (signal.status === "accepted") entry.accepted += 1; + if (signal.status === "submitted") entry.submitted += 1; + beatsByAgent.set(name, entry); + } + [...beatsByAgent.entries()] + .sort((a, b) => b[1].count - a[1].count || a[0].localeCompare(b[0])) + .slice(0, 8) + .forEach(([agent, counts]) => { + rows.push({ + role: "Daily Beat Writer", + agent, + output: `${plural(counts.count, "quantum beat")} filed (${counts.accepted} accepted, ${counts.submitted} submitted)`, + status: counts.accepted ? "accepted" : "submitted", + source_url: "https://aibtc.news/api/signals?limit=500", + }); + }); + + const prsThisWeek = prsRaw.filter((p) => [p.createdAt, p.updatedAt, p.mergedAt].some((v) => (v || "").slice(0, 10) >= weekStart)); + const prsByAuthor = new Map(); + for (const pr of prsThisWeek) { + const author = pr.author?.login || "unknown"; + const list = prsByAuthor.get(author) || []; + list.push(pr); + prsByAuthor.set(author, list); + } + [...prsByAuthor.entries()] + .sort((a, b) => b[1].length - a[1].length || a[0].localeCompare(b[0])) + .forEach(([agent, prs]) => { + const merged = prs.filter((p) => p.state === "MERGED").length; + const open = prs.filter((p) => p.state === "OPEN").length; + const closed = prs.filter((p) => p.state === "CLOSED").length; + const refs = prs.map((p) => `#${p.number} ${stateLabel(p.state)}`).join(", "); + rows.push({ + role: "Visualizer Developer", + agent, + output: `${plural(prs.length, "dashboard PR")} this week: ${refs}`, + status: merged ? "merged" : open ? "open" : closed ? "closed" : "tracked", + source_url: prs[0]?.url || "https://github.com/Iskander-Agent/quantum-visualizer/pulls", + }); + }); + + const commentsThisWeek = comments.filter((c) => (c.created_at || "").slice(0, 10) >= weekStart); + const driComments = commentsThisWeek.filter((c) => c.user?.login === "Iskander-Agent" && /DRI|daily|status|synthesis/i.test(c.body || "")); + const latestDri = driComments.at(-1); + rows.push({ + role: "Directly Responsible Individual", + agent: "Iskander-Agent", + output: latestDri ? `${plural(driComments.length, "coordination update")} in #33` : "No DRI issue update detected this week", + status: latestDri ? "active" : "needs update", + source_url: latestDri?.html_url || "https://github.com/1btc-news/news-client/issues/33", + }); + + for (const handle of ["ThankNIXlater", "lekanbams"]) { + const pcComments = commentsThisWeek.filter((c) => c.user?.login === handle && /PC|review|clearance|verdict|merge/i.test(c.body || "")); + if (!pcComments.length) continue; rows.push({ - author: comment.user?.login || "unknown", - created_at: comment.created_at, - comment_url: comment.html_url, - pr: prNumber ? `#${prNumber}` : null, - pr_state: pr?.state || "unknown", - amount_sats: amountSats, - btc_address: parseBtcAddress(body), - txid: parseTxid(body), - state, - note: state === "paid" - ? "Payment proof detected in issue #33 comment." - : "Not counted as paid until bounty-poster approval and on-chain/payment proof are visible.", + role: "Player Coach / Review", + agent: handle, + output: `${plural(pcComments.length, "review update")} in #33`, + status: "active", + source_url: pcComments.at(-1).html_url, }); } - const requestedRows = rows.filter((r) => r.amount_sats && r.state !== "paid"); - const paidRows = rows.filter((r) => r.amount_sats && r.state === "paid"); + const tableRequest = [...comments].reverse().find((c) => /Team\s+[-\u2013\u2014]\s+Week|week[- ]?#[\s\S]*table|source of truth for the weeks|weekly stats/i.test(c.body || "")); + return { - source_url: "https://github.com/1btc-news/news-client/issues/33", - extracted_at: new Date().toISOString(), + label: `Week ${weekNumber}`, + week_number: weekNumber, + week_start: weekStart, + week_end: weekEnd, + source_request_url: tableRequest?.html_url || null, + source_request_author: tableRequest?.user?.login || null, rows, - requested_sats: requestedRows.reduce((sum, r) => sum + r.amount_sats, 0), - confirmed_paid_sats: paidRows.reduce((sum, r) => sum + r.amount_sats, 0), - pending_requests: rows.filter((r) => r.state !== "paid").length, - paid_requests: rows.filter((r) => r.state === "paid").length, - verifier: "Issue #33 comments plus on-chain BTC transaction proof for each address/txid.", }; } @@ -167,13 +197,17 @@ const contributors = [...new Set(comments.map((c) => c.user.login))]; const iskander_comments = comments.filter((c) => c.user.login === "Iskander-Agent").length; const prsRaw = JSON.parse( - gh("gh pr list --repo Iskander-Agent/quantum-visualizer --state all --limit 100 --json number,state,author,title,mergedAt") + gh("gh pr list --repo Iskander-Agent/quantum-visualizer --state all --limit 100 --json number,state,author,title,mergedAt,createdAt,updatedAt,url") ); const merged = prsRaw.filter((p) => p.state === "MERGED"); const pr_contributors = [...new Set(merged.map((p) => p.author.login))]; +const weekNumber = Math.max(1, Math.floor(daysBetween(projectStart, today) / 7) + 1); +const weekStart = addDays(projectStart, (weekNumber - 1) * 7); +const weekEnd = addDays(weekStart, 6); +const teamWeek = makeTeamWeek({ quantum, comments, prsRaw, weekNumber, weekStart, weekEnd }); + const revenue = await fetchRevenueLedger(existingCustomer); -const payoutLedger = makePayoutLedger({ comments, prsRaw }); const customer = { schema_version: 3, @@ -184,6 +218,7 @@ const customer = { last_7d, source: "https://aibtc.news/api/signals", }, + team_week: teamWeek, sats_flow: { bounty_30_paid: { amount_sats: 100000, @@ -191,8 +226,7 @@ const customer = { note: "Original research bounty (Issue #30), on-chain proof", }, bounty_33_pool_sats: 250000, - bounty_33_paid_confirmed: payoutLedger.confirmed_paid_sats, - bounty_33_payout_ledger: payoutLedger, + bounty_33_paid_confirmed: "unknown - awaiting on-chain payout ledger in #33", revenue_x402_sats: revenue.totalSats, revenue_x402_events: revenue.totalEvents, revenue_x402_last_7d_events: revenue.last7dEvents, @@ -206,8 +240,8 @@ const customer = { quantum_visualizer_merged_prs: merged.length, quantum_visualizer_pr_contributors: pr_contributors.length, pr_contributor_handles: pr_contributors, - dashboard_visits: "unknown — no analytics instrumented", - x_engagement: "unknown — x-posting paused (credits depleted)", + dashboard_visits: "unknown - no analytics instrumented", + x_engagement: "unknown - x-posting paused (credits depleted)", }, freshness: { signals_fetched_at: new Date().toISOString(), @@ -218,10 +252,9 @@ const customer = { notes: [ "Silence is not a data point. Unknown fields stay unknown until verified.", "Regenerate with: node scripts/build-customer.mjs", - "bounty_33_payout_ledger is parsed from issue #33 comments. Pending requests are not counted as paid.", ...(revenue.note ? [revenue.note] : []), ], }; fs.writeFileSync(OUT, JSON.stringify(customer, null, 2) + "\n"); -console.log(`wrote customer.json: ${quantum.length} beats, ${comments.length} comments, ${merged.length} merged PRs, ${revenue.totalEvents} x402 events (${revenue.totalSats} sats), ${payoutLedger.rows.length} payout rows`); +console.log(`wrote customer.json: ${quantum.length} beats, ${comments.length} comments, ${merged.length} merged PRs, ${revenue.totalEvents} x402 events (${revenue.totalSats} sats)`); diff --git a/scripts/check-frontend.mjs b/scripts/check-frontend.mjs index a2a3fdc..e1855a6 100644 --- a/scripts/check-frontend.mjs +++ b/scripts/check-frontend.mjs @@ -11,27 +11,20 @@ function assert(condition, message) { } for (const id of [ - "affiliation-panel", - "affiliation-summary", - "affiliation-count", - "affiliation-body", "freshness-panel", "freshness-body", "freshness-updates", "freshness-stale-list", - "payout-panel", - "payout-kpis", - "payout-ledger-body", + "team-panel", + "team-week-range", + "team-kpis", + "team-week-body", ]) { assert(html.includes(`id="${id}"`), `missing #${id}`); } -assert( - html.includes("function renderAffiliationReadiness"), - "missing renderAffiliationReadiness()", -); assert(html.includes("function renderFreshnessAudit"), "missing renderFreshnessAudit()"); -assert(html.includes("function renderPayoutLedger"), "missing renderPayoutLedger()"); +assert(html.includes("function renderTeamWeek"), "missing renderTeamWeek()"); assert(html.includes("fetch('/customer.json')"), "missing customer world model fetch"); const scriptMatch = html.match(/