the-custodian/state-hub/dashboard/src/dependencies.md
tegwick 235355eb58 feat(dashboard): nav restructure, full context-help coverage, 11 new ref docs
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 <h1>, 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 <noreply@anthropic.com>
2026-03-01 23:46:26 +01:00

6.9 KiB

title
Dependencies
const API  = "http://127.0.0.1:8000";
const POLL = 15_000;
// 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));
  }
})();
const wsMap = depState.wsMap ?? {};
const edges  = depState.edges  ?? [];
const _ok    = depState.ok     ?? false;
const _ts    = depState.ts;

Dependencies

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`<div class="kpi-infobox">
  <div class="kpi-infobox-title">Dependencies</div>
  <div class="kpi-row">
    <span class="kpi-row-label">edges</span>
    <div class="kpi-row-right"><div class="kpi-row-value">${edges.length}</div></div>
  </div>
  <div class="kpi-row">
    <span class="kpi-row-label">workstreams involved</span>
    <div class="kpi-row-right"><div class="kpi-row-value">${_wsWithDeps.size}</div></div>
  </div>
</div>`;

const _liveEl = html`<div class="live-indicator">
  <span style="color:${_ok ? 'var(--theme-foreground-focus)' : 'red'}">●</span>
  ${_ok
    ? `Live · updated ${_ts?.toLocaleTimeString()}`
    : html`<span style="color:red">Offline — run: <code>make api</code></span>`}
</div>`;

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.

if (edges.length === 0) {
  display(html`<p class="dim">No dependency edges registered.</p>`);
} 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`<table class="dep-table">
    <thead>
      <tr>
        <th>Depends-on domain</th>
        <th>Depends-on workstream</th>
        <th></th>
        <th>Blocked-by domain</th>
        <th>Blocked-by workstream</th>
        <th>Status</th>
      </tr>
    </thead>
    <tbody>${rows.map(r => html`
      <tr>
        <td class="dep-domain">${r.from_domain}</td>
        <td class="dep-title">${r.from_title}</td>
        <td class="dep-arrow">→</td>
        <td class="dep-domain">${r.to_domain}</td>
        <td class="dep-title">${r.to_title}</td>
        <td><span class="dep-status dep-status-${r.to_status}">${r.to_status}</span></td>
      </tr>
    `)}</tbody>
  </table>`);
}