--- title: Workstreams --- ```js const API = "http://127.0.0.1:8000"; const POLL = 15_000; ``` ```js // Fetch workstreams + topics + summary (for dep graph) in parallel const wsState = (async function*() { while (true) { let data = [], openWs = [], ok = false; try { const [rw, rt, rs] = await Promise.all([ fetch(`${API}/workstreams/`), fetch(`${API}/topics/`), fetch(`${API}/state/summary`), ]); ok = rw.ok && rt.ok && rs.ok; if (ok) { const [wsList, topicList, summary] = await Promise.all([rw.json(), rt.json(), rs.json()]); const topicMap = Object.fromEntries(topicList.map(t => [t.id, t])); data = wsList.map(w => ({ ...w, domain: topicMap[w.topic_id]?.domain ?? "unknown", topic_title: topicMap[w.topic_id]?.title ?? "—", })); // open_workstreams from summary carry depends_on / blocks lists openWs = summary.open_workstreams ?? []; } } catch {} yield {data, openWs, ok, ts: new Date()}; await new Promise(res => setTimeout(res, POLL)); } })(); ``` ```js const data = wsState.data ?? []; const openWs = wsState.openWs ?? []; const _ok = wsState.ok ?? false; const _ts = wsState.ts; ``` # Workstreams ```js import {injectTocTop} from "./components/toc-sidebar.js"; import {withDocHelp} from "./components/doc-overlay.js"; const _liveEl = html`
${_ok ? `Live · updated ${_ts?.toLocaleTimeString()}` : html`Offline — run: make api`}
`; withDocHelp(_liveEl, "/docs/live-data"); injectTocTop("live-indicator", _liveEl); const _h1 = document.querySelector("#observablehq-main h1"); if (_h1) { _h1.style.position = "relative"; withDocHelp(_h1, "/docs/workstreams"); } ``` ```js import {MultiSelect} from "./components/multiselect.js"; // Static options — no dependency on `data`, so selections survive polls const DOMAINS = ["custodian", "railiance", "markitect", "coulomb_social", "personhood", "foerster_capabilities"]; const STATUSES = ["active", "blocked", "completed", "archived"]; // Create filter form without displaying — shown below the chart const _filtersForm = Inputs.form( { domain: MultiSelect(DOMAINS, {label: "Domain", placeholder: "All domains"}), status: MultiSelect(STATUSES, {label: "Status", placeholder: "All statuses"}), owner: Inputs.text({placeholder: "Owner…", style: "width:120px"}), }, { template: ({domain, status, owner}) => html`
${domain}${status}
${owner}
`, } ); ``` ```js const filters = Generators.input(_filtersForm); ``` ```js // Empty array = no filter applied (show all) const filtered = data.filter(w => (filters.domain.length === 0 || filters.domain.includes(w.domain)) && (filters.status.length === 0 || filters.status.includes(w.status)) && (!filters.owner || (w.owner ?? "").toLowerCase().includes(filters.owner.toLowerCase())) ); ``` ## Status Distribution ```js import * as Plot from "npm:@observablehq/plot"; const byStatus = Object.entries( filtered.reduce((acc, w) => { acc[w.status] = (acc[w.status] ?? 0) + 1; return acc; }, {}) ).map(([status, count]) => ({status, count})); display(Plot.plot({ marks: [ Plot.barX(byStatus, {y: "status", x: "count", fill: "status", tip: true}), Plot.ruleX([0]), ], marginLeft: 80, width: 500, })); ``` ## All Workstreams ```js display(_filtersForm); display(Inputs.table(filtered.map(w => ({ Title: w.title, Domain: w.domain, Status: w.status, Owner: w.owner ?? "—", Due: w.due_date ?? "—", Updated: new Date(w.updated_at).toLocaleDateString(), })), {rows: 20})); ``` ## Dependencies ```js // Build dep cards from the enriched open_workstreams in the summary const wsWithDeps = openWs.filter(w => { const domain = data.find(d => d.id === w.id)?.domain ?? "unknown"; return (filters.domain.length === 0 || filters.domain.includes(domain)) && (filters.status.length === 0 || filters.status.includes(w.status)) && (w.depends_on.length > 0 || w.blocks.length > 0); }); if (wsWithDeps.length === 0) { display(html`

No dependency edges recorded for the current filter. Use create_dependency() via the MCP server to link workstreams.

`); } else { display(html`
${wsWithDeps.map(w => { const depRows = w.depends_on.map(d => html`
↳ depends on ${d.workstream_title}${d.description ? html` — ${d.description}` : ""}
` ); const blockRows = w.blocks.map(d => html`
⊳ blocks ${d.workstream_title}${d.description ? html` — ${d.description}` : ""}
` ); return html`
${w.title}
${w.status}
${depRows}${blockRows}
`; })}
`); } ```