From 947c2e88241f13752d097abbe648d0f3da120c19 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 1 Mar 2026 23:46:26 +0100 Subject: [PATCH] feat(dashboard): nav restructure, full context-help coverage, 11 new ref docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Navigation: - New order: Overview · Todo · Domains · Repos · Workstreams (collapsible, open:false, with atomic sub-entries: Decisions, Tasks, Debt, Extends, Dependencies) · Contributions · SBOM · Progress · Reference (collapsible) - Reference section gains path:/reference landing page; all 18 doc pages listed in nav (alphabetical) and in reference.md table New pages: - todo.md — Internal / Ecosystem / Third-party todo classification - dependencies.md — dependency edge table derived from state/summary - reference.md — Reference landing page with full doc index New reference doc pages (11): contributions, debt, dependencies, domains, extensions, overview, repos, tasks, todo + reference (meta) already added previously doc-overlay.js — lazy bubblehelp tooltip: - _titleCache Map + _fetchDocTitle(docPath): on first hover of any ? button, fetches the target doc page, parses

, sets btn.title - Native browser tooltip appears exactly on the ? circle on subsequent hover Context-help wired on all 14 dashboard pages: - h1 withDocHelp added to: index, todo, domains, repos, tasks, techdept, extensions, dependencies (contributions/workstreams/decisions/sbom/ progress/reference were already wired) - domains.md + repos.md: added missing withDocHelp import and live-data link - tasks/techdept/extensions: removed duplicate _h1 const that caused SyntaxError: Identifier '_h1' has already been declared Co-Authored-By: Claude Sonnet 4.6 --- dashboard/observablehq.config.js | 55 ++++-- dashboard/src/components/doc-overlay.js | 24 ++- dashboard/src/contributions.md | 5 + dashboard/src/dependencies.md | 158 ++++++++++++++++ dashboard/src/docs/contributions.md | 120 ++++++++++++ dashboard/src/docs/debt.md | 91 +++++++++ dashboard/src/docs/dependencies.md | 90 +++++++++ dashboard/src/docs/domains.md | 82 +++++++++ dashboard/src/docs/extensions.md | 102 ++++++++++ dashboard/src/docs/overview.md | 84 +++++++++ dashboard/src/docs/reference.md | 119 ++++++++++++ dashboard/src/docs/repos.md | 83 +++++++++ dashboard/src/docs/tasks.md | 95 ++++++++++ dashboard/src/docs/todo.md | 71 +++++++ dashboard/src/domains.md | 5 + dashboard/src/extensions.md | 6 +- dashboard/src/index.md | 3 + dashboard/src/reference.md | 47 +++++ dashboard/src/repos.md | 6 + dashboard/src/tasks.md | 6 +- dashboard/src/techdept.md | 6 +- dashboard/src/todo.md | 235 ++++++++++++++++++++++++ 22 files changed, 1468 insertions(+), 25 deletions(-) create mode 100644 dashboard/src/dependencies.md create mode 100644 dashboard/src/docs/contributions.md create mode 100644 dashboard/src/docs/debt.md create mode 100644 dashboard/src/docs/dependencies.md create mode 100644 dashboard/src/docs/domains.md create mode 100644 dashboard/src/docs/extensions.md create mode 100644 dashboard/src/docs/overview.md create mode 100644 dashboard/src/docs/reference.md create mode 100644 dashboard/src/docs/repos.md create mode 100644 dashboard/src/docs/tasks.md create mode 100644 dashboard/src/docs/todo.md create mode 100644 dashboard/src/reference.md create mode 100644 dashboard/src/todo.md diff --git a/dashboard/observablehq.config.js b/dashboard/observablehq.config.js index 570bcf1..91cf8c8 100644 --- a/dashboard/observablehq.config.js +++ b/dashboard/observablehq.config.js @@ -2,29 +2,54 @@ export default { root: "src", title: "Custodian State Hub", pages: [ + // ── Overview ────────────────────────────────────────────────────────────── { name: "Overview", path: "/" }, - { name: "Workstreams", path: "/workstreams" }, - { name: "Tasks", path: "/tasks" }, - { name: "Decisions", path: "/decisions" }, - { name: "Progress", path: "/progress" }, + { name: "Todo", path: "/todo" }, + // ── Organizational Entity Views ─────────────────────────────────────────── { name: "Domains", path: "/domains" }, - { name: "Repos", path: "/repos" }, - { name: "Extension Points", path: "/extensions" }, - { name: "Technical Debt", path: "/techdept" }, + { name: "Repos", path: "/repos" }, + { + name: "Workstreams", + path: "/workstreams", + collapsible: true, + open: false, + pages: [ + { name: "Decisions", path: "/decisions" }, + { name: "Tasks", path: "/tasks" }, + { name: "Debt", path: "/techdept" }, + { name: "Extends", path: "/extensions" }, + { name: "Dependencies", path: "/dependencies" }, + ], + }, + // ── Functional Report Views ──────────────────────────────────────────────── { name: "Contributions", path: "/contributions" }, - { name: "SBOM", path: "/sbom" }, + { name: "SBOM", path: "/sbom" }, + { name: "Progress", path: "/progress" }, + // ── Reference ───────────────────────────────────────────────────────────── { name: "Reference", + path: "/reference", collapsible: true, + open: false, pages: [ - { name: "Decision Health", path: "/docs/decisions-kpi" }, - { name: "Decisions", path: "/docs/decisions" }, + { name: "Contributions", path: "/docs/contributions" }, + { name: "Decision Health", path: "/docs/decisions-kpi" }, + { name: "Decisions", path: "/docs/decisions" }, + { name: "Dependencies", path: "/docs/dependencies" }, + { name: "Domains", path: "/docs/domains" }, + { name: "Extension Points", path: "/docs/extensions" }, { name: "Inter-Repo Communication", path: "/docs/inter-repo-communication" }, - { name: "Live Data", path: "/docs/live-data" }, - { name: "Progress Log", path: "/docs/progress-log" }, - { name: "SBOM", path: "/docs/sbom" }, - { name: "Workstream Health", path: "/docs/workstream-health-index" }, - { name: "Workstreams", path: "/docs/workstreams" }, + { name: "Live Data", path: "/docs/live-data" }, + { name: "Overview", path: "/docs/overview" }, + { name: "Progress Log", path: "/docs/progress-log" }, + { name: "Reference & Context Help", path: "/docs/reference" }, + { name: "Repos", path: "/docs/repos" }, + { name: "SBOM", path: "/docs/sbom" }, + { name: "Tasks", path: "/docs/tasks" }, + { name: "Technical Debt", path: "/docs/debt" }, + { name: "Todo", path: "/docs/todo" }, + { name: "Workstream Health", path: "/docs/workstream-health-index" }, + { name: "Workstreams", path: "/docs/workstreams" }, ], }, ], diff --git a/dashboard/src/components/doc-overlay.js b/dashboard/src/components/doc-overlay.js index bc501e0..54493b7 100644 --- a/dashboard/src/components/doc-overlay.js +++ b/dashboard/src/components/doc-overlay.js @@ -12,7 +12,21 @@ * The ? button is invisible until the user hovers over the element. */ -const _STYLE_ID = "doc-overlay-styles"; +const _STYLE_ID = "doc-overlay-styles"; +const _titleCache = new Map(); + +async function _fetchDocTitle(docPath) { + if (_titleCache.has(docPath)) return _titleCache.get(docPath); + try { + const res = await fetch(docPath); + if (!res.ok) return null; + const parser = new DOMParser(); + const doc = parser.parseFromString(await res.text(), "text/html"); + const title = doc.querySelector("h1")?.textContent?.trim() ?? null; + if (title) _titleCache.set(docPath, title); + return title; + } catch { return null; } +} function _ensureStyles() { if (typeof document === "undefined" || document.getElementById(_STYLE_ID)) return; @@ -195,6 +209,14 @@ export function withDocHelp(element, docPath) { btn.setAttribute("aria-label", "Open documentation"); btn.addEventListener("click", e => { e.stopPropagation(); _openOverlay(docPath); }); + // Lazy-load the h1 of the target doc page as a native tooltip (bubblehelp) + btn.addEventListener("mouseenter", async () => { + if (btn.dataset.titleFetched) return; + btn.dataset.titleFetched = "1"; + const title = await _fetchDocTitle(docPath); + if (title) btn.title = title; + }, {once: true}); + element.append(btn); return element; } diff --git a/dashboard/src/contributions.md b/dashboard/src/contributions.md index 9c759e4..0915195 100644 --- a/dashboard/src/contributions.md +++ b/dashboard/src/contributions.md @@ -33,12 +33,17 @@ const _ts = contribState.ts; ```js import {injectTocTop} from "./components/toc-sidebar.js"; +import {withDocHelp} from "./components/doc-overlay.js"; const _liveEl = html`
${_ok ? `Live · ${_ts?.toLocaleTimeString()}` : html`API offline`}
`; +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/contributions"); } ``` ```js diff --git a/dashboard/src/dependencies.md b/dashboard/src/dependencies.md new file mode 100644 index 0000000..de7164f --- /dev/null +++ b/dashboard/src/dependencies.md @@ -0,0 +1,158 @@ +--- +title: Dependencies +--- + +```js +const API = "http://127.0.0.1:8000"; +const POLL = 15_000; +``` + +```js +// Fetch workstreams + topics + summary (summary carries dep edges on open_workstreams) +const depState = (async function*() { + while (true) { + let wsMap = {}, edges = [], ok = false; + try { + const [rw, rto, rs] = await Promise.all([ + fetch(`${API}/workstreams/`), + fetch(`${API}/topics/`), + fetch(`${API}/state/summary`), + ]); + ok = rw.ok && rto.ok && rs.ok; + if (ok) { + const [wsList, topicList, summary] = await Promise.all([ + rw.json(), rto.json(), rs.json(), + ]); + const topicMap = Object.fromEntries(topicList.map(t => [t.id, t])); + wsMap = Object.fromEntries(wsList.map(w => [w.id, { + ...w, + domain: topicMap[w.topic_id]?.domain_slug ?? "unknown", + }])); + // Build directed edge list from open_workstreams depends_on arrays + for (const ow of (summary.open_workstreams ?? [])) { + for (const depId of (ow.depends_on ?? [])) { + edges.push({from_id: ow.id, to_id: depId}); + } + } + } + } catch {} + yield {wsMap, edges, ok, ts: new Date()}; + await new Promise(res => setTimeout(res, POLL)); + } +})(); +``` + +```js +const wsMap = depState.wsMap ?? {}; +const edges = depState.edges ?? []; +const _ok = depState.ok ?? false; +const _ts = depState.ts; +``` + +# Dependencies + +```js +import {injectTocTop} from "./components/toc-sidebar.js"; +import {withDocHelp} from "./components/doc-overlay.js"; + +// ── KPI sidebar card ────────────────────────────────────────────────────────── +const _wsWithDeps = new Set([...edges.map(e => e.from_id), ...edges.map(e => e.to_id)]); +const _kpiBox = html`
+
Dependencies
+
+ edges +
${edges.length}
+
+
+ workstreams involved +
${_wsWithDeps.size}
+
+
`; + +const _liveEl = html`
+ + ${_ok + ? `Live · updated ${_ts?.toLocaleTimeString()}` + : html`Offline — run: make api`} +
`; + +const _h1 = document.querySelector("#observablehq-main h1"); +if (_h1) { _h1.style.position = "relative"; withDocHelp(_h1, "/docs/dependencies"); } + +injectTocTop("dep-kpi-box", _kpiBox); +injectTocTop("live-indicator", _liveEl); +``` + +Directed edges between active workstreams. An edge **A → B** means A cannot +fully proceed until B reaches a satisfactory state. + +```js +if (edges.length === 0) { + display(html`

No dependency edges registered.

`); +} else { + const rows = edges.map(e => { + const from = wsMap[e.from_id]; + const to = wsMap[e.to_id]; + return { + from_domain: from?.domain ?? "—", + from_title: from?.title ?? e.from_id, + from_status: from?.status ?? "—", + to_domain: to?.domain ?? "—", + to_title: to?.title ?? e.to_id, + to_status: to?.status ?? "—", + }; + }); + + display(html` + + + + + + + + + + + ${rows.map(r => html` + + + + + + + + + `)} +
Depends-on domainDepends-on workstreamBlocked-by domainBlocked-by workstreamStatus
${r.from_domain}${r.from_title}${r.to_domain}${r.to_title}${r.to_status}
`); +} +``` + + diff --git a/dashboard/src/docs/contributions.md b/dashboard/src/docs/contributions.md new file mode 100644 index 0000000..2e35dae --- /dev/null +++ b/dashboard/src/docs/contributions.md @@ -0,0 +1,120 @@ +--- +title: Contributions — Reference +--- + +# Contributions — Reference + +Contributions track **outbound upstream work** — things the Custodian has +identified that belong in a repo it does not own or control. Each contribution +is a structured artifact filed locally in the repo's `contrib/` directory and +registered in the state hub so it is never lost. + +--- + +## Contribution types + +| Type | Full name | Use when | +|------|-----------|----------| +| `br` | Bug Report | You found a defect in an upstream tool or library | +| `fr` | Feature Request | You need functionality that upstream does not yet provide | +| `ep` | Extension Point | You identified a future enhancement opportunity in upstream code | +| `upr` | Upstream PR | You have written (or are writing) a patch for an upstream repo | + +--- + +## Lifecycle + +``` +draft → submitted → acknowledged → accepted → merged + ↘ ↘ + rejected withdrawn +``` + +| Status | Meaning | +|--------|---------| +| **draft** | Artifact written locally; not yet sent upstream | +| **submitted** | Filed as a GitHub issue, PR, or email — awaiting upstream response | +| **acknowledged** | Upstream has seen it and responded (e.g. triaged, commented) | +| **accepted** | Upstream agreed to take action | +| **merged** | PR accepted and merged; issue resolved | +| **rejected** | Upstream declined; record kept for future reference | +| **withdrawn** | We decided not to pursue it | + +Transitions are enforced by the API — you cannot skip stages arbitrarily. +`submitted_at` is stamped automatically when status moves to `submitted`; +`resolved_at` is stamped when status moves to `merged`, `rejected`, or `withdrawn`. + +--- + +## Relation to the Todo classification + +Contributions map directly to the **Third-party** class in the inter-repo +communication taxonomy: + +| Todo class | Mechanism | +|------------|-----------| +| Internal | Workplan file + task in this repo's workstream | +| Ecosystem | State hub task with `[repo:]` prefix | +| **Third-party** | **Contribution artifact in `contrib/` + state hub registration** | + +Contributions in `draft`, `submitted`, or `acknowledged` status appear as +open Third-party todos on the [Todo](/todo) page. + +--- + +## File layout + +Each artifact lives in the current repo under `contrib/`: + +``` +contrib/ + bug-reports/ br-YYYY-MM-DD------.md + feature-requests/ fr-YYYY-MM-DD------.md + extension-points/ EP--NNN------.md + upstream-prs/ upr-YYYY-MM-DD------.md +``` + +Templates live in `~/the-custodian/canon/standards/contrib-templates/`. +Convention details: `~/the-custodian/canon/standards/contribution-convention_v0.1.md`. + +--- + +## Adding a contribution + +**1. Write the artifact file** using the appropriate template. + +**2. Register it in the state hub** via MCP: + +``` +register_contribution( + type = "fr", + title = "Add sidebar TOC injection API", + target_org = "observablehq", + target_repo = "framework", + body_path = "contrib/feature-requests/fr-2026-02-26--observablehq--framework--toc.md", + related_workstream_id = "" +) +``` + +**3. Close the loop** when you file it upstream: + +``` +update_contribution_status(contribution_id="", status="submitted") +``` + +**4. Keep updating** as upstream responds — `acknowledged`, `accepted`, `merged`. + +--- + +## Kanban board + +The Contributions page groups artifacts by status column. Only columns with at +least one entry are shown. The **⚠ follow-up banner** appears when any +contribution has been in `submitted` or `acknowledged` for an extended period +without further movement — a prompt to check in with upstream. + +--- + +*Contributions are append-only. Rejected or withdrawn artifacts are retained as +institutional memory — they explain why certain approaches were tried and +dropped.* diff --git a/dashboard/src/docs/debt.md b/dashboard/src/docs/debt.md new file mode 100644 index 0000000..977f533 --- /dev/null +++ b/dashboard/src/docs/debt.md @@ -0,0 +1,91 @@ +--- +title: Technical Debt — Reference +--- + +# Technical Debt — Reference + +The Technical Debt page tracks known quality compromises across all six project +domains — intentional shortcuts, design weaknesses, missing tests, and similar +issues that reduce codebase health but have been consciously deferred. + +--- + +## Debt types + +| Type | Examples | +|------|---------| +| **design** | Architectural decisions that should be revisited | +| **implementation** | Hacky or fragile code that works but shouldn't stay | +| **test** | Missing or incomplete test coverage | +| **docs** | Missing or outdated documentation | +| **dependencies** | Pinned old versions, unused packages, missing lockfiles | +| **performance** | Known bottlenecks not yet worth addressing | +| **security** | Hardcoded values, missing input validation, weak auth | +| **other** | Anything that doesn't fit the above | + +--- + +## Severity levels + +| Severity | Meaning | +|----------|---------| +| **critical** | Blocks release or poses an active risk | +| **high** | Should be resolved before the next major milestone | +| **medium** | Normal triage priority | +| **low** | Nice-to-fix; acceptable to defer indefinitely | + +--- + +## Statuses + +| Status | Meaning | +|--------|---------| +| **open** | Known and unaddressed | +| **in_progress** | Being actively worked on | +| **deferred** | Acknowledged but intentionally postponed | +| **resolved** | Fixed | +| **wont_fix** | Accepted as permanent — documented for future reference | + +Items are sorted by status (open → in_progress → deferred → resolved → wont_fix) +then by severity (critical → high → medium → low) within each group. + +--- + +## Filters + +| Filter | Effect | +|--------|--------| +| **Status** | Multi-select | +| **Severity** | Multi-select | +| **Domain** | Multi-select | +| **Type** | Multi-select | + +--- + +## Registering debt + +Via MCP: + +``` +register_technical_debt( + domain = "custodian", + title = "Hard-coded API URL in data loaders", + debt_type = "implementation", + severity = "high", + description = "All data loaders use http://127.0.0.1:8000 directly. Should read from an env var or config.", + location = "state-hub/dashboard/src/data/*.json.py", + workstream_id = "" # optional +) +``` + +``` +update_td_status(td_uuid="", status="resolved") +``` + +--- + +## Human-readable IDs + +Each debt item carries a human-readable ID in the form `TD--NNN` +(e.g. `TD-CUST-001`). IDs are optional at creation and auto-assigned if omitted. +They appear in the table for easy reference in commit messages and comments. diff --git a/dashboard/src/docs/dependencies.md b/dashboard/src/docs/dependencies.md new file mode 100644 index 0000000..2dd56ed --- /dev/null +++ b/dashboard/src/docs/dependencies.md @@ -0,0 +1,90 @@ +--- +title: Dependencies — Reference +--- + +# Dependencies — Reference + +The Dependencies page shows the directed dependency graph between active +workstreams — which workstreams are waiting on others to reach a satisfactory +state before they can fully proceed. + +--- + +## What is a dependency edge? + +A dependency edge **A → B** means workstream A cannot fully proceed until +workstream B is in a satisfactory state (typically `completed` or `archived`). + +Edges are used to model real sequencing constraints: for example, a shared +library must reach a stable release before downstream domains can build on it. +The Custodian's dependency order is: + +``` +Railiance → Markitect → Coulomb.social → Personhood / Foerster → Custodian +``` + +--- + +## Edge table + +Each row shows: + +| Column | Meaning | +|--------|---------| +| **Depends-on domain** | Domain of the dependent workstream (the one waiting) | +| **Depends-on workstream** | Title of the workstream that has the dependency | +| **→** | Direction arrow | +| **Blocked-by domain** | Domain of the prerequisite workstream | +| **Blocked-by workstream** | Title of the workstream that must complete first | +| **Status** | Current status of the prerequisite (green = active, grey = completed) | + +--- + +## KPI sidebar card + +Shows the total number of edges and the number of distinct workstreams involved +in at least one dependency relationship. + +--- + +## Registering a dependency + +Via MCP: + +``` +create_dependency( + from_workstream_id = "", + to_workstream_id = "", + description = "Cannot build auth layer until shared-library API is stable" +) +``` + +Via REST: + +```bash +curl -X POST http://127.0.0.1:8000/workstreams//dependencies/ \ + -H "Content-Type: application/json" \ + -d '{"to_workstream_id": "", "description": "..."}' +``` + +To list dependencies for a workstream: + +``` +list_dependencies(workstream_id="") +``` + +--- + +## Cycle detection + +The Workstream Health Index (WHI) includes a **Cycle Penalty Index (CPI)** +metric that detects circular dependencies using depth-first search. If CPI = 1, +a cycle exists and the WHI is penalised by 50%. The WHI KPI card on the +[Workstreams](/workstreams) page will display a cycle alert. + +--- + +## Data source + +Dependency edges are derived from the `depends_on` arrays on `open_workstreams` +in `GET /state/summary`. Polls every **15 seconds**. diff --git a/dashboard/src/docs/domains.md b/dashboard/src/docs/domains.md new file mode 100644 index 0000000..2bc9b8a --- /dev/null +++ b/dashboard/src/docs/domains.md @@ -0,0 +1,82 @@ +--- +title: Domains — Reference +--- + +# Domains — Reference + +The Domains page shows all registered project domains and the repositories +associated with each one. Domains are the top-level organisational unit of the +Custodian ecosystem. + +--- + +## What is a domain? + +A domain corresponds to one of the six tracked project areas: + +| Slug | Project | +|------|---------| +| `custodian` | The Custodian agent system itself | +| `railiance` | DevOps & infrastructure reliability | +| `markitect` | Knowledge artifact management | +| `coulomb_social` | Co-creation marketplace | +| `personhood` | Rights & obligations framework | +| `foerster_capabilities` | Agency capability taxonomy | + +Each domain has a slug (URL-friendly identifier), a human-readable name, an +optional description, and a status. + +--- + +## Domain statuses + +| Status | Meaning | +|--------|---------| +| **active** | Live domain — topics, workstreams, and tasks are being tracked | +| **archived** | Soft-deleted; no active work. Fails to archive if active topics exist | + +--- + +## KPI row + +Four counters at the top of the page: + +| Counter | Meaning | +|---------|---------| +| Total domains | All registered domains regardless of status | +| Active | Domains with status `active` | +| Total repos | Sum of all registered repositories across all domains | +| Newest domain | Name of the most recently created domain | + +--- + +## Domain cards + +One card per domain showing: + +- **Slug** — monospace identifier +- **Status badge** — green `active` or grey `archived` +- **Name** — display name +- **Description** — first 160 characters +- **Repos** — list of registered repositories for this domain, each showing name, local path, and remote URL + +--- + +## Managing domains + +Via MCP: + +``` +create_domain(slug="my_project", name="My Project", description="…") +rename_domain(slug="old_slug", new_slug="new_slug", new_name="New Name") +archive_domain(slug="my_project") # fails if active topics exist +``` + +Via Makefile: + +```bash +make add-domain SLUG=my_project NAME="My Project" +make rename-domain OLD_SLUG=my_project NEW_SLUG=myproject NEW_NAME="My Project" +``` + +*Domains are never hard-deleted — only archived.* diff --git a/dashboard/src/docs/extensions.md b/dashboard/src/docs/extensions.md new file mode 100644 index 0000000..08a3615 --- /dev/null +++ b/dashboard/src/docs/extensions.md @@ -0,0 +1,102 @@ +--- +title: Extension Points — Reference +--- + +# Extension Points — Reference + +The Extension Points page tracks known future enhancement opportunities across +all six domains — design forks the system *could* take, parked deliberately +for later consideration rather than acted on immediately. + +--- + +## What is an extension point? + +An extension point (EP) captures a place in the design where additional +capability could be added — an API surface that could be extended, a schema +that could grow, an integration that could be built. Recording an EP +acknowledges the opportunity without committing to it. + +Extension points are distinct from technical debt: debt is a known compromise +that should be fixed; EPs are optional future directions that may or may not +be pursued. + +--- + +## EP types + +| Type | Examples | +|------|---------| +| **api** | New endpoints, query parameters, response fields | +| **schema** | New tables, columns, relationships | +| **mcp** | New MCP tools or resources | +| **dashboard** | New pages, charts, or components | +| **architecture** | Structural changes to the system design | +| **integration** | Connections to external systems | +| **other** | Anything that doesn't fit the above | + +--- + +## Statuses + +| Status | Meaning | +|--------|---------| +| **open** | Identified, not yet acted on | +| **in_progress** | Being implemented as part of an active workstream | +| **addressed** | The capability has been built | +| **deferred** | Intentionally postponed | +| **wont_fix** | Decided not to pursue — kept for documentation | + +Items are sorted by status (open → in_progress → deferred → addressed → wont_fix) +then by priority (critical → high → medium → low). + +--- + +## Priorities + +| Priority | Meaning | +|----------|---------| +| **critical** | Needed to unblock other work | +| **high** | High-value enhancement for the near term | +| **medium** | Would be useful but not urgent | +| **low** | Speculative or long-horizon idea | + +--- + +## Filters + +| Filter | Effect | +|--------|--------| +| **Status** | Multi-select | +| **Priority** | Multi-select | +| **Domain** | Multi-select | +| **Type** | Multi-select | + +--- + +## Registering an extension point + +Via MCP: + +``` +register_extension_point( + domain = "custodian", + title = "Configurable poll interval per dashboard page", + ep_type = "dashboard", + priority = "low", + description = "Each page hard-codes POLL = 15_000. An env var or per-page config would allow slowing down low-priority pages to reduce API load.", + location = "state-hub/dashboard/src/*.md", + workstream_id = "" # optional +) +``` + +``` +update_ep_status(ep_uuid="", status="addressed") +``` + +--- + +## Human-readable IDs + +Each EP carries an ID in the form `EP--NNN` (e.g. `EP-CUST-001`). +IDs are optional at creation and auto-assigned if omitted. diff --git a/dashboard/src/docs/overview.md b/dashboard/src/docs/overview.md new file mode 100644 index 0000000..d0bf8e5 --- /dev/null +++ b/dashboard/src/docs/overview.md @@ -0,0 +1,84 @@ +--- +title: Overview — Reference +--- + +# Overview — Reference + +The Overview page is the operational home screen of the Custodian State Hub. +It shows the live health of the entire ecosystem at a glance — active work, +blocking decisions, and system-derived next-step suggestions. + +--- + +## Sections + +### Open Workstreams by Domain + +A horizontal stacked bar chart showing every active workstream across all six +domains. Each bar is broken into four task-status segments: + +| Colour | Segment | +|--------|---------| +| green | done | +| blue | in progress | +| orange-red | blocked | +| light grey | todo | + +The left axis shows domain labels (one per group of workstreams). The `done/total` +count is printed to the right of each bar. Workstreams with no tasks yet show +a grey "— no tasks yet" label. + +### Contribution & SBOM Health + +Three summary cards linked to the Contributions and SBOM pages: + +| Card | Shows | +|------|-------| +| **Contributions** | Total artifact count; orange warning if any are awaiting upstream response | +| **Licence Risk** | Count of SBOM packages with copyleft licences in direct dependencies | +| **SBOM** | Breakdown by contribution type (BR / FR / EP / UPR) | + +### Status + +Four metric cards: + +| Card | Meaning | +|------|---------| +| **Active Workstreams** | Count of non-completed, non-archived workstreams | +| **Blocking Decisions** | Pending decisions with status `open` or `escalated` — orange border if > 0 | +| **Blocked Tasks** | Click to expand the list with blocking reasons | +| **Events Today** | Progress events created on today's date | + +### What's next? + +System-derived action suggestions from `GET /state/next_steps`. Suggestions are +generated when a decision is resolved or a workstream dependency is cleared, and +they point to the first open task in the relevant workstream. These are derived +on request and never persisted. + +### Blocking Decisions + +Inline resolution form for each pending decision. Expand a card, enter a +rationale and "decided by" name, and click **Record & close**. The decision is +resolved via `POST /decisions/{id}/resolve` and disappears from the list +without a page reload. + +### Registered Projects + +Table of projects registered with `make register-project`, sourced from +`milestone` progress events whose summary starts with +`"Project registered with State Hub:"`. + +### Recent Activity + +Last 20 progress events across all domains, showing time, event type, author, +and summary. + +--- + +## Data source + +Polls `GET /state/summary` every **15 seconds**. Blocking decisions are fetched +separately via `GET /decisions/?decision_type=pending` and only re-fetched +after a successful resolve action — this prevents the inline form from being +wiped on every poll. diff --git a/dashboard/src/docs/reference.md b/dashboard/src/docs/reference.md new file mode 100644 index 0000000..951c844 --- /dev/null +++ b/dashboard/src/docs/reference.md @@ -0,0 +1,119 @@ +--- +title: Reference & Context Help — Reference +--- + +# Reference & Context Help + +The **Reference** section is a collection of in-depth documentation pages +explaining the data model, design conventions, and mechanics of each dashboard +view. Reference pages are readable as standalone articles and also surfaced +inline via the **? context-help button** on dashboard pages. + +--- + +## The ? context-help button + +Every dashboard page exposes one or more **?** buttons — small circular +controls that open the relevant reference page in an overlay without leaving +the current view. + +### Where ? buttons appear + +| Location | Opens | +|----------|-------| +| Page **h1** heading | Reference page for that dashboard view | +| **KPI sidebar cards** | Reference page for the specific metric shown | +| **Live indicator** | [Live Data](/docs/live-data) — poll interval, offline recovery | + +### How to use it + +1. Hover over the element — the **?** button fades in at the top-right corner. +2. Click **?** — the reference page opens in a modal overlay. +3. Read the docs, then dismiss with **✕ close**, **Esc**, or by clicking the + backdrop. + +The overlay does not interrupt the live data polling loop — the dashboard +continues refreshing in the background while the overlay is open. + +--- + +## Overlay behaviour + +| Detail | Value | +|--------|-------| +| Size | `min(780px, 92vw)` wide · `82vh` tall | +| Content | Observable Framework page rendered in an `