diff --git a/dashboard/src/workstreams.md b/dashboard/src/workstreams.md
index 2a663a5..7f17e45 100644
--- a/dashboard/src/workstreams.md
+++ b/dashboard/src/workstreams.md
@@ -56,19 +56,32 @@ display(html`
```
```js
-const domainOpts = ["(all)", ...new Set(data.map(w => w.domain))].sort();
-const statusOpts = ["(all)", "active", "blocked", "completed", "archived"];
+// 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"];
-const domainFilter = view(Inputs.select(domainOpts, {label: "Domain"}));
-const statusFilter = view(Inputs.select(statusOpts, {label: "Status"}));
-const ownerFilter = view(Inputs.text({label: "Owner contains"}));
+const filters = view(Inputs.form(
+ {
+ domain: Inputs.checkbox(DOMAINS, {label: "Domain"}),
+ status: Inputs.checkbox(STATUSES, {label: "Status"}),
+ owner: Inputs.text({label: "Owner contains", placeholder: "filter by owner…"}),
+ },
+ {
+ template: ({domain, status, owner}) => html`
+
${domain}
+
${status}
+
${owner}
+
`,
+ }
+));
```
```js
+// Empty array = no filter applied (show all)
const filtered = data.filter(w =>
- (domainFilter === "(all)" || w.domain === domainFilter) &&
- (statusFilter === "(all)" || w.status === statusFilter) &&
- (!ownerFilter || (w.owner ?? "").toLowerCase().includes(ownerFilter.toLowerCase()))
+ (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()))
);
display(Inputs.table(filtered.map(w => ({
@@ -104,11 +117,12 @@ display(Plot.plot({
```js
// Build dep cards from the enriched open_workstreams in the summary
-const wsWithDeps = openWs.filter(w =>
- (domainFilter === "(all)" || (data.find(d => d.id === w.id)?.domain ?? "unknown") === domainFilter) &&
- (statusFilter === "(all)" || w.status === statusFilter) &&
- (w.depends_on.length > 0 || w.blocks.length > 0)
-);
+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.
`);
@@ -132,6 +146,9 @@ if (wsWithDeps.length === 0) {