--- title: Decisions --- ```js const API = "http://127.0.0.1:8000"; const POLL = 15_000; ``` ```js // Fetch decisions + topics (for domain context) in parallel const decState = (async function*() { while (true) { let data = [], ok = false; try { const [rd, rt] = await Promise.all([ fetch(`${API}/decisions/?limit=500`), fetch(`${API}/topics/`), ]); ok = rd.ok && rt.ok; if (ok) { const [decisions, topics] = await Promise.all([rd.json(), rt.json()]); const topicMap = Object.fromEntries(topics.map(t => [t.id, t])); data = decisions .map(d => ({ ...d, domain: topicMap[d.topic_id]?.domain ?? null, topic_title: topicMap[d.topic_id]?.title ?? null, })) .sort((a, b) => { // escalated first, then open, then resolved/superseded; within group by deadline asc const rank = {escalated: 0, open: 1, resolved: 2, superseded: 3}; const dr = (rank[a.status] ?? 9) - (rank[b.status] ?? 9); if (dr !== 0) return dr; if (a.deadline && b.deadline) return a.deadline.localeCompare(b.deadline); return a.deadline ? -1 : b.deadline ? 1 : 0; }); } } catch {} yield {data, ok, ts: new Date()}; await new Promise(res => setTimeout(res, POLL)); } })(); ``` ```js const data = decState.data ?? []; const _ok = decState.ok ?? false; const _ts = decState.ts; ``` # Decisions ```js display(html`
${_ok ? `Live · updated ${_ts?.toLocaleTimeString()}` : `Offline — run: make api`}
`); ``` ```js import {MultiSelect} from "./components/multiselect.js"; const filters = view(Inputs.form( { type: MultiSelect(["pending", "made"], {label: "Type", placeholder: "All types"}), status: MultiSelect(["open", "escalated", "resolved", "superseded"], {label: "Status", placeholder: "All statuses"}), search: Inputs.text({placeholder: "Search title…", style: "width:160px"}), }, { template: ({type, status, search}) => html`
${type}${status}
`, } )); ``` ```js const filtered = data.filter(d => (filters.type.length === 0 || filters.type.includes(d.decision_type)) && (filters.status.length === 0 || filters.status.includes(d.status)) && (!filters.search || d.title.toLowerCase().includes(filters.search.toLowerCase())) ); const escalated = filtered.filter(d => d.escalation_note); const STATUS_BORDER = {open: "steelblue", escalated: "#f59e0b", resolved: "#22c55e", superseded: "#aaa"}; const TYPE_CLASS = {pending: "badge-pending", made: "badge-made"}; function fmtDate(iso) { return iso ? new Date(iso).toLocaleDateString(undefined, {day: "numeric", month: "short", year: "numeric"}) : null; } function isOverdue(iso) { return iso && new Date(iso) < new Date(); } if (filtered.length === 0) { display(html`

No decisions match the current filter.

`); } else { display(html`
${filtered.map(d => { const border = STATUS_BORDER[d.status] ?? "#ccc"; const snippet = (d.description || d.rationale || "").slice(0, 200); const due = fmtDate(d.deadline); const decided = fmtDate(d.decided_at); const overdue = isOverdue(d.deadline); return html`
${d.decision_type} ${d.status === "escalated" ? "⚠ " : ""}${d.status} ${d.domain ? html`${d.domain}` : ""} ${due ? html` ${overdue ? "⚠ overdue" : "due"} ${due} ` : ""} ${fmtDate(d.created_at)}
${d.title}
${snippet ? html`
${snippet}${snippet.length < (d.description || d.rationale || "").length ? "…" : ""}
` : ""} ${d.decided_by ? html`
✓ ${d.decided_by}${decided ? " · " + decided : ""}
` : ""} ${d.escalation_note ? html`
${d.escalation_note}
` : ""}
`; })}
`); } ``` ```js if (escalated.length > 0) { display(html`
⚠ ${escalated.length} escalated decision${escalated.length > 1 ? "s" : ""} require human approval before any action is taken (constitution §4).
`); } ``` ## Resolution Velocity ```js import * as Plot from "npm:@observablehq/plot"; const resolved = data.filter(d => d.decided_at); const byMonth = Object.entries( resolved.reduce((acc, d) => { const m = d.decided_at.slice(0, 7); acc[m] = (acc[m] ?? 0) + 1; return acc; }, {}) ).map(([month, count]) => ({month, count})); display(byMonth.length === 0 ? html`

No resolved decisions yet.

` : Plot.plot({ title: "Decisions resolved per month", x: {label: "Month", tickRotate: -30}, y: {label: "Count", grid: true}, marks: [ Plot.barY(byMonth, {x: "month", y: "count", fill: "steelblue", tip: true}), Plot.ruleY([0]), ], marginBottom: 60, width: 700, }) ); ```