---
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}
`;
})}
`);
}
```