Add state-hub v0.1 — local-first state service for the Custodian
Implements the first live layer of the Custodian cognitive infrastructure:
PostgreSQL schema, FastAPI REST API, FastMCP stdio server, and Observable
Framework telemetry dashboard.
- state-hub/: full stack (docker-compose, FastAPI, Alembic, MCP server, dashboard)
- 5 DB tables: topics, workstreams, tasks, decisions, progress_events
- 11 MCP tools + 5 resources registered in .mcp.json
- Observable dashboard: Overview, Workstreams, Decisions, Progress pages
- CLAUDE.md: session protocol (get_state_summary / add_progress_event ritual)
- ~/.claude/CLAUDE.md: global cross-project reference to the hub
- scripts/pull_image.py: WSL2 TLS-resilient Docker image downloader
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 17:47:49 +01:00
|
|
|
---
|
|
|
|
|
title: Progress
|
|
|
|
|
---
|
|
|
|
|
|
2026-02-24 23:19:26 +01:00
|
|
|
```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;
|
|
|
|
|
```
|
|
|
|
|
|
Add state-hub v0.1 — local-first state service for the Custodian
Implements the first live layer of the Custodian cognitive infrastructure:
PostgreSQL schema, FastAPI REST API, FastMCP stdio server, and Observable
Framework telemetry dashboard.
- state-hub/: full stack (docker-compose, FastAPI, Alembic, MCP server, dashboard)
- 5 DB tables: topics, workstreams, tasks, decisions, progress_events
- 11 MCP tools + 5 resources registered in .mcp.json
- Observable dashboard: Overview, Workstreams, Decisions, Progress pages
- CLAUDE.md: session protocol (get_state_summary / add_progress_event ritual)
- ~/.claude/CLAUDE.md: global cross-project reference to the hub
- scripts/pull_image.py: WSL2 TLS-resilient Docker image downloader
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 17:47:49 +01:00
|
|
|
# Progress Log
|
|
|
|
|
|
|
|
|
|
*Append-only per constitution §5 — no deletions.*
|
|
|
|
|
|
|
|
|
|
```js
|
dashboard: move live indicator to TOC sidebar on all pages; add live-data docs
- All four pages (index, workstreams, decisions, progress) now inject the
live indicator into #observablehq-toc via injectTocTop("live-indicator", el)
Left-aligned (no text-align: right), position:relative + padding-right for
the ? button affordance
- decisions.md: splits the former combined "decisions-sidebar" widget into two
separate injectTocTop calls — KPI box first (ends lower), live indicator
second (ends at top); both now have their own stable ids
- withDocHelp(_liveEl, "/docs/live-data") wires the ? button on every page
- src/docs/live-data.md: new documentation page explaining poll interval (15s),
indicator colour semantics, offline recovery, and which endpoints each page hits
- Removes the .live-bar CSS class from all pages; replaces with .live-indicator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 16:18:09 +01:00
|
|
|
import {injectTocTop} from "./components/toc-sidebar.js";
|
|
|
|
|
import {withDocHelp} from "./components/doc-overlay.js";
|
|
|
|
|
|
|
|
|
|
const _liveEl = html`<div class="live-indicator">
|
2026-02-24 23:19:26 +01:00
|
|
|
<span style="color:${_ok ? 'var(--theme-foreground-focus)' : 'red'}">●</span>
|
|
|
|
|
${_ok
|
|
|
|
|
? `Live · updated ${_ts?.toLocaleTimeString()} · ${data.length} events total`
|
dashboard: move live indicator to TOC sidebar on all pages; add live-data docs
- All four pages (index, workstreams, decisions, progress) now inject the
live indicator into #observablehq-toc via injectTocTop("live-indicator", el)
Left-aligned (no text-align: right), position:relative + padding-right for
the ? button affordance
- decisions.md: splits the former combined "decisions-sidebar" widget into two
separate injectTocTop calls — KPI box first (ends lower), live indicator
second (ends at top); both now have their own stable ids
- withDocHelp(_liveEl, "/docs/live-data") wires the ? button on every page
- src/docs/live-data.md: new documentation page explaining poll interval (15s),
indicator colour semantics, offline recovery, and which endpoints each page hits
- Removes the .live-bar CSS class from all pages; replaces with .live-indicator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 16:18:09 +01:00
|
|
|
: html`<span style="color:red">Offline — run: <code>make api</code></span>`}
|
|
|
|
|
</div>`;
|
|
|
|
|
withDocHelp(_liveEl, "/docs/live-data");
|
|
|
|
|
injectTocTop("live-indicator", _liveEl);
|
Add state-hub v0.1 — local-first state service for the Custodian
Implements the first live layer of the Custodian cognitive infrastructure:
PostgreSQL schema, FastAPI REST API, FastMCP stdio server, and Observable
Framework telemetry dashboard.
- state-hub/: full stack (docker-compose, FastAPI, Alembic, MCP server, dashboard)
- 5 DB tables: topics, workstreams, tasks, decisions, progress_events
- 11 MCP tools + 5 resources registered in .mcp.json
- Observable dashboard: Overview, Workstreams, Decisions, Progress pages
- CLAUDE.md: session protocol (get_state_summary / add_progress_event ritual)
- ~/.claude/CLAUDE.md: global cross-project reference to the hub
- scripts/pull_image.py: WSL2 TLS-resilient Docker image downloader
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 17:47:49 +01:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```js
|
2026-02-24 23:19:26 +01:00
|
|
|
const authorOpts = ["(all)", ...new Set(data.map(e => e.author ?? "unknown"))].sort();
|
|
|
|
|
const typeOpts = ["(all)", ...new Set(data.map(e => e.event_type))].sort();
|
|
|
|
|
|
|
|
|
|
const authorFilter = view(Inputs.select(authorOpts, {label: "Author"}));
|
|
|
|
|
const typeFilter = view(Inputs.select(typeOpts, {label: "Event type"}));
|
|
|
|
|
const sinceFilter = view(Inputs.date({label: "Since"}));
|
Add state-hub v0.1 — local-first state service for the Custodian
Implements the first live layer of the Custodian cognitive infrastructure:
PostgreSQL schema, FastAPI REST API, FastMCP stdio server, and Observable
Framework telemetry dashboard.
- state-hub/: full stack (docker-compose, FastAPI, Alembic, MCP server, dashboard)
- 5 DB tables: topics, workstreams, tasks, decisions, progress_events
- 11 MCP tools + 5 resources registered in .mcp.json
- Observable dashboard: Overview, Workstreams, Decisions, Progress pages
- CLAUDE.md: session protocol (get_state_summary / add_progress_event ritual)
- ~/.claude/CLAUDE.md: global cross-project reference to the hub
- scripts/pull_image.py: WSL2 TLS-resilient Docker image downloader
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 17:47:49 +01:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const filtered = data.filter(e =>
|
|
|
|
|
(authorFilter === "(all)" || (e.author ?? "unknown") === authorFilter) &&
|
2026-02-24 23:19:26 +01:00
|
|
|
(typeFilter === "(all)" || e.event_type === typeFilter) &&
|
Add state-hub v0.1 — local-first state service for the Custodian
Implements the first live layer of the Custodian cognitive infrastructure:
PostgreSQL schema, FastAPI REST API, FastMCP stdio server, and Observable
Framework telemetry dashboard.
- state-hub/: full stack (docker-compose, FastAPI, Alembic, MCP server, dashboard)
- 5 DB tables: topics, workstreams, tasks, decisions, progress_events
- 11 MCP tools + 5 resources registered in .mcp.json
- Observable dashboard: Overview, Workstreams, Decisions, Progress pages
- CLAUDE.md: session protocol (get_state_summary / add_progress_event ritual)
- ~/.claude/CLAUDE.md: global cross-project reference to the hub
- scripts/pull_image.py: WSL2 TLS-resilient Docker image downloader
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 17:47:49 +01:00
|
|
|
(!sinceFilter || new Date(e.created_at) >= sinceFilter)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
display(html`<p><strong>${filtered.length}</strong> events shown (append-only, no deletions).</p>`);
|
|
|
|
|
|
|
|
|
|
display(Inputs.table(filtered.map(e => ({
|
2026-02-24 23:19:26 +01:00
|
|
|
Time: new Date(e.created_at).toLocaleString(),
|
|
|
|
|
Type: e.event_type,
|
|
|
|
|
Author: e.author ?? "—",
|
Add state-hub v0.1 — local-first state service for the Custodian
Implements the first live layer of the Custodian cognitive infrastructure:
PostgreSQL schema, FastAPI REST API, FastMCP stdio server, and Observable
Framework telemetry dashboard.
- state-hub/: full stack (docker-compose, FastAPI, Alembic, MCP server, dashboard)
- 5 DB tables: topics, workstreams, tasks, decisions, progress_events
- 11 MCP tools + 5 resources registered in .mcp.json
- Observable dashboard: Overview, Workstreams, Decisions, Progress pages
- CLAUDE.md: session protocol (get_state_summary / add_progress_event ritual)
- ~/.claude/CLAUDE.md: global cross-project reference to the hub
- scripts/pull_image.py: WSL2 TLS-resilient Docker image downloader
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 17:47:49 +01:00
|
|
|
Summary: e.summary,
|
2026-02-24 23:19:26 +01:00
|
|
|
})), {rows: 50}));
|
Add state-hub v0.1 — local-first state service for the Custodian
Implements the first live layer of the Custodian cognitive infrastructure:
PostgreSQL schema, FastAPI REST API, FastMCP stdio server, and Observable
Framework telemetry dashboard.
- state-hub/: full stack (docker-compose, FastAPI, Alembic, MCP server, dashboard)
- 5 DB tables: topics, workstreams, tasks, decisions, progress_events
- 11 MCP tools + 5 resources registered in .mcp.json
- Observable dashboard: Overview, Workstreams, Decisions, Progress pages
- CLAUDE.md: session protocol (get_state_summary / add_progress_event ritual)
- ~/.claude/CLAUDE.md: global cross-project reference to the hub
- scripts/pull_image.py: WSL2 TLS-resilient Docker image downloader
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 17:47:49 +01:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Event Volume (Last 30 Days)
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
import * as Plot from "npm:@observablehq/plot";
|
|
|
|
|
|
2026-02-24 23:19:26 +01:00
|
|
|
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`<p style="color:gray">No events in the last 30 days.</p>`
|
|
|
|
|
: 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,
|
|
|
|
|
})
|
|
|
|
|
);
|
Add state-hub v0.1 — local-first state service for the Custodian
Implements the first live layer of the Custodian cognitive infrastructure:
PostgreSQL schema, FastAPI REST API, FastMCP stdio server, and Observable
Framework telemetry dashboard.
- state-hub/: full stack (docker-compose, FastAPI, Alembic, MCP server, dashboard)
- 5 DB tables: topics, workstreams, tasks, decisions, progress_events
- 11 MCP tools + 5 resources registered in .mcp.json
- Observable dashboard: Overview, Workstreams, Decisions, Progress pages
- CLAUDE.md: session protocol (get_state_summary / add_progress_event ritual)
- ~/.claude/CLAUDE.md: global cross-project reference to the hub
- scripts/pull_image.py: WSL2 TLS-resilient Docker image downloader
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 17:47:49 +01:00
|
|
|
```
|
2026-02-24 23:19:26 +01:00
|
|
|
|
|
|
|
|
<style>
|
dashboard: move live indicator to TOC sidebar on all pages; add live-data docs
- All four pages (index, workstreams, decisions, progress) now inject the
live indicator into #observablehq-toc via injectTocTop("live-indicator", el)
Left-aligned (no text-align: right), position:relative + padding-right for
the ? button affordance
- decisions.md: splits the former combined "decisions-sidebar" widget into two
separate injectTocTop calls — KPI box first (ends lower), live indicator
second (ends at top); both now have their own stable ids
- withDocHelp(_liveEl, "/docs/live-data") wires the ? button on every page
- src/docs/live-data.md: new documentation page explaining poll interval (15s),
indicator colour semantics, offline recovery, and which endpoints each page hits
- Removes the .live-bar CSS class from all pages; replaces with .live-indicator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 16:18:09 +01:00
|
|
|
.live-indicator { font-size: 0.8rem; color: gray; margin-bottom: 0.75rem; position: relative; padding-right: 1.6rem; }
|
2026-02-24 23:19:26 +01:00
|
|
|
</style>
|