Adds preferred workplan REST/event surfaces, legacy-meter telemetry and weekly review summaries, documentation/dashboard terminology updates, dashboard API loading fixes, and close-out sync for STATE-WP-0052 and STATE-WP-0054.
T4: workstreams.md and dependencies.md now call /state/deps instead of the
full /state/summary — removes 2 heavy 10-table queries per 60 s cycle.
T5: index.md's 4 independent polling loops (summaryState, sbomSnapState,
regsState, wsChartState) consolidated into a single pageState generator
with one Promise.all batch and a shared backoff counter.
T6: config.js gains waitForVisible(ms) — pauses polling entirely while the
tab is hidden and fires immediately on visibilitychange. pollDelay()
simplified (hidden-tab POLL_HIDDEN logic removed). All 16 polling pages
migrated from await sleep(pollDelay(...)) to await waitForVisible(pollDelay(...)).
CUST-WP-0039 complete — all 6 tasks done.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Always call display() for the warning element so Observable Framework
replaces it on each poll re-run. Previously the conditional display()
call left the warning rendered indefinitely once shown.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The card titled "SBOM" was displaying contribution type counts (FR/BR/EP/UPR)
which is unrelated to SBOM data. Added a sbomSnapState generator that fetches
/sbom/snapshots/ and shows: total tracked packages (sum of entry_count across
all snapshots), repos tracked, and copyleft risk count from the existing
licence_risk_count in the summary. Card turns orange if licenceRisk > 0.
Resolves suggestion b6775727.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
EP catalogue (all domains):
- EP-RAIL-001 ep_id patched (schema fix: add ep_id to EPUpdate)
- EP-RAIL-003 (git bare-repo mirrors) and EP-RAIL-004 (offsite secondary
backup) registered from railiance-cluster/docs/backup-restore.md
- EP-CUST-003..007 ep_ids assigned to existing custodian EPs
- EP-CUST-008 (State Hub API auth) and EP-CUST-009 (update_workstream MCP
tool) registered as new custodian extension points
TD catalogue (railiance — first 5 items):
- TD-RAIL-001: backup cron runs as root without audit trail (high/security)
- TD-RAIL-002: k3s kubeconfig world-readable mode 644 (medium/security)
- TD-RAIL-003: no Ansible role unit tests (medium/test)
- TD-RAIL-004: age key extracted via awk — fragile (medium/impl)
- TD-RAIL-005: etcd snapshot retention uncoordinated (low/impl)
Dashboard (T08 + T10):
- Extract API URL and POLL to src/components/config.js; all 15 pages
now import from the shared module (contributions/goals keep custom POLL)
- Shared .kpi-infobox, .filter-bar, .filter-search/.filter-owner CSS
moved to observablehq.config.js head <style> block; removed from 9 pages
- Build: 0 errors, 0 warnings
API (T09):
- progress.py: limit param now Query(100, le=1000) — prevents unbounded
list requests; closes TD-CUST-004 for the only endpoint that had limit
CUST-WP-0004 marked completed (all 10 tasks done).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Domains are sorted top-to-bottom by the latest updated_at across their
workstreams (most recently active domain first). Within a domain,
workstreams are also ordered by updated_at desc. Replaces alphabetical sort.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move Interventions under Workstreams in the navigator
- Add action-confirm.js: shared modal component for actions requiring a
mandatory comment (survives live-poll re-renders, unlike inline DOM mutation)
- Wire action-confirm into Interventions (Mark done) and Decisions (Resolve)
- Fix Interventions completed section: fetch all tasks and filter client-side
so resolved interventions (needs_human=false) still appear under Completed
- Add docs/interventions.md help page with ? button on the h1
- Replace all hardcoded "Bernd" with "human" across dashboard src and docs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After the v0.5 migration TopicRead.domain was renamed to domain_slug.
index.md, decisions.md and tasks.md still referenced the old field,
causing every workstream domain to fall back to "unknown". Also
updated tasks.md to load the domain filter list dynamically from
/domains/ instead of the hardcoded 6-slug array.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces padding-right-only with full padding (0.55rem top/bottom, 0.7rem
left, 1.8rem right). The top padding gives the absolute-positioned ? button
a proper anchor and stops it sitting too low against the text.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
Polling fix:
- Blocking decisions now use a Mutable (blockingDecisions) + refreshDecisions()
instead of deriving from summary.blocking_decisions
- Cards only re-render on initial page load or after a successful resolve,
not on every 15 s summary poll — so typing in the form is never interrupted
- On successful resolve, refreshDecisions() re-fetches the list; the resolved
decision no longer matches the open/escalated filter so it disappears cleanly
Copy to clipboard:
- Small "Copy" button in the card header (next to deadline/escalation badges)
- Formats full decision as markdown: title, description, context, status, date
- Shows "✓ Copied" for 1.5 s, reverts to "Copy"; shows "⚠ Failed" on error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
API:
- DecisionResolve schema (rationale, decided_by, write_log flag)
- POST /decisions/{id}/resolve — marks resolved, emits progress event,
appends entry to DECISIONS.md in the project's registered directory
(found via the topic's registration milestone event)
Dashboard:
- Replace Inputs.table for blocking decisions with full-text cards
- Each card shows title, full description (pre-wrap), rationale/context,
escalation warning if present
- Expandable "Resolve →" section with rationale textarea, decided-by
input, submit button that calls the resolve endpoint
- On success: collapses form, dims card, confirms log was written
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- y-axis tick labels now show domain name (only on the first
workstream in each domain group, blank for subsequent ones)
- Workstream title rendered as text at x=0 inside the bar
- Titles truncated at 36/24 chars to avoid overflow
- marginLeft reduced to 160 (domain names are shorter than titles)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
API:
- WorkstreamWithTaskCounts schema extends WorkstreamRead with
tasks_total/todo/in_progress/blocked/done fields
- /state/summary now includes these counts in open_workstreams via
a single extra GROUP BY query (workstream_id, status)
Dashboard:
- Replace domain workstream-count bar with a horizontal stacked
progress bar per workstream (done/in-progress/blocked/todo)
- Workstreams with no tasks show "no tasks yet" annotation
- Workstreams with tasks show "X/N done" label after the bar
- Sorted by domain then title so domains group naturally
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MCP server: add create_workstream(topic_id, title, slug?, owner?,
description?, due_date?) — auto-generates slug from title if omitted;
emits workstream_created progress event. Now 12 tools total.
CLI: add two new subcommands —
custodian create-workstream --domain DOMAIN --title TITLE [--slug] [--owner] [--description]
custodian create-task --workstream ID_OR_SLUG --title TITLE [--priority] [--assignee]
create-task accepts workstream UUID or slug (resolves via API).
Dashboard: hint box below "Open Workstreams by Domain" chart listing
registered domains that have zero workstreams, with the exact
custodian create-workstream command to run.
TOOLS.md: updated tool count (11 → 12) and added create_workstream row.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CORS: add CORSMiddleware to FastAPI for localhost:3000 so browser fetch
works across ports without errors.
All four pages now use async generator cells that call the API directly
and re-yield every 15 s — no data loader cache, no manual cache clearing.
Each page shows a live status bar (● green/red · last updated time).
Offline state shows the `make api` hint inline.
index.md: add "Registered Projects" section — polls
/progress/?event_type=milestone&limit=500 and filters for
"Project registered with State Hub:" events; shows project name,
domain, path, and registration timestamp.
workstreams.md: fix broken domain column — now fetches /workstreams/
and /topics/ in parallel and joins on topic_id client-side.
Previously the domain column showed "unknown" for all rows because
WorkstreamRead schema doesn't include domain.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>