--- title: Progress --- ```js const API = "http://127.0.0.1:8000"; const POLL = 15_000; ``` ```js const progState = (async function*() { while (true) { let data = [], ok = false; try { const r = await fetch(`${API}/progress/?limit=500`); ok = r.ok; data = ok ? await r.json() : []; } catch {} yield {data, ok, ts: new Date()}; await new Promise(res => setTimeout(res, POLL)); } })(); ``` ```js const data = progState.data ?? []; const _ok = progState.ok ?? false; const _ts = progState.ts; ``` # Progress Log *Append-only per constitution §5 — no deletions.* ```js import {injectTocTop} from "./components/toc-sidebar.js"; import {withDocHelp} from "./components/doc-overlay.js"; const _liveEl = html`
make api`}
${filtered.length} events shown (append-only, no deletions).
`); display(Inputs.table(filtered.map(e => ({ Time: new Date(e.created_at).toLocaleString(), Type: e.event_type, Author: e.author ?? "—", Summary: e.summary, })), {rows: 50})); ``` ## Event Volume (Last 30 Days) ```js import * as Plot from "npm:@observablehq/plot"; const cutoff = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); const byDay = Object.entries( data .filter(e => new Date(e.created_at) >= cutoff) .reduce((acc, e) => { const day = e.created_at.slice(0, 10); acc[day] = (acc[day] ?? 0) + 1; return acc; }, {}) ).sort().map(([day, count]) => ({day, count})); display(byDay.length === 0 ? html`No events in the last 30 days.
` : Plot.plot({ title: "Progress events per day (30-day window)", x: {label: "Date", tickRotate: -30}, y: {label: "Events", grid: true}, marks: [ Plot.areaY(byDay, {x: "day", y: "count", fill: "steelblue", fillOpacity: 0.3}), Plot.lineY(byDay, {x: "day", y: "count", stroke: "steelblue"}), Plot.ruleY([0]), ], marginBottom: 60, width: 750, }) ); ```