feat(ep-td+dashboard): complete CUST-WP-0004 EP/TD tracking workstream

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>
This commit is contained in:
2026-03-11 01:40:52 +01:00
parent 7b665a5d66
commit 9f744dd7f3
18 changed files with 27 additions and 76 deletions

View File

@@ -1,7 +1,7 @@
import uuid import uuid
from datetime import datetime from datetime import datetime
from fastapi import APIRouter, Depends, status from fastapi import APIRouter, Depends, Query, status
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
@@ -19,7 +19,7 @@ async def list_progress(
task_id: uuid.UUID | None = None, task_id: uuid.UUID | None = None,
event_type: str | None = None, event_type: str | None = None,
since: datetime | None = None, since: datetime | None = None,
limit: int = 100, limit: int = Query(100, le=1000),
session: AsyncSession = Depends(get_session), session: AsyncSession = Depends(get_session),
) -> list[ProgressEvent]: ) -> list[ProgressEvent]:
q = select(ProgressEvent) q = select(ProgressEvent)

View File

@@ -69,6 +69,13 @@ export default {
}, },
], ],
theme: ["air", "near-midnight"], theme: ["air", "near-midnight"],
head: `<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🗄️</text></svg>">`, head: `<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🗄️</text></svg>">
<style>
.kpi-infobox { background: var(--theme-background-alt, #f9f9f9); border: 1px solid var(--theme-foreground-faint, #e0e0e0); border-radius: 10px; padding: 0.75rem 1rem; position: relative; box-shadow: 0 1px 6px rgba(0,0,0,0.07); margin-bottom: 1.25rem; }
.kpi-infobox-title { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--theme-foreground-muted, #888); margin-bottom: 0.55rem; padding-right: 1.6rem; }
.filter-bar { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-bottom: 1rem; }
.filter-search, .filter-owner { display: flex; align-items: center; }
.filter-search input, .filter-owner input { height: 30px; font-size: 0.85rem; padding: 0.25rem 0.5rem; border-radius: 6px; border: 1px solid var(--theme-foreground-faint, #ccc); background: var(--theme-background, #fff); font-family: inherit; color: inherit; }
</style>`,
footer: "Custodian State Hub — local-first, append-only, sovereignty-preserving.", footer: "Custodian State Hub — local-first, append-only, sovereignty-preserving.",
}; };

View File

@@ -0,0 +1,2 @@
export const API = "http://127.0.0.1:8000";
export const POLL = 15_000;

View File

@@ -3,7 +3,7 @@ title: Contributions
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API} from "./components/config.js";
const POLL = 30_000; const POLL = 30_000;
``` ```

View File

@@ -3,8 +3,7 @@ title: Decisions
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API, POLL} from "./components/config.js";
const POLL = 15_000;
``` ```
```js ```js
@@ -392,25 +391,6 @@ if (escalated.length > 0) {
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
} }
/* ── KPI infobox ──────────────────────────────────────────────────────────── */
.kpi-infobox {
background: var(--theme-background-alt, #f9f9f9);
border: 1px solid var(--theme-foreground-faint, #e0e0e0);
border-radius: 10px;
padding: 0.75rem 1rem;
position: relative;
box-shadow: 0 1px 6px rgba(0,0,0,0.07);
margin-bottom: 1.25rem;
}
.kpi-infobox-title {
font-size: 0.68rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--theme-foreground-muted, #888);
margin-bottom: 0.55rem;
padding-right: 1.6rem; /* room for ? button */
}
.kpi-row { .kpi-row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -445,10 +425,6 @@ if (escalated.length > 0) {
/* ── Utility ──────────────────────────────────────────────────────────────── */ /* ── Utility ──────────────────────────────────────────────────────────────── */
.dim { color: gray; font-style: italic; } .dim { color: gray; font-style: italic; }
/* ── Filter bar ───────────────────────────────────────────────────────────── */
.filter-bar { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-bottom: 1rem; }
.filter-search { display: flex; align-items: center; }
.filter-search input { height: 30px; font-size: 0.85rem; padding: 0.25rem 0.5rem; border-radius: 6px; border: 1px solid var(--theme-foreground-faint, #ccc); background: var(--theme-background, #fff); font-family: inherit; color: inherit; }
/* ── Decision list ────────────────────────────────────────────────────────── */ /* ── Decision list ────────────────────────────────────────────────────────── */
.dec-list { display: flex; flex-direction: column; gap: 0.5rem; } .dec-list { display: flex; flex-direction: column; gap: 0.5rem; }

View File

@@ -3,8 +3,7 @@ title: Dependencies
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API, POLL} from "./components/config.js";
const POLL = 15_000;
``` ```
```js ```js
@@ -136,8 +135,6 @@ if (edges.length === 0) {
.live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; } .live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; }
/* ── KPI infobox ──────────────────────────────────────────────────────────── */ /* ── KPI infobox ──────────────────────────────────────────────────────────── */
.kpi-infobox { background: var(--theme-background-alt, #f9f9f9); border: 1px solid var(--theme-foreground-faint, #e0e0e0); border-radius: 10px; padding: 0.75rem 1rem; position: relative; box-shadow: 0 1px 6px rgba(0,0,0,0.07); margin-bottom: 1.25rem; }
.kpi-infobox-title { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--theme-foreground-muted, #888); margin-bottom: 0.55rem; }
.kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; } .kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; }
.kpi-row + .kpi-row { border-top: 1px solid var(--theme-foreground-faint, #eee); } .kpi-row + .kpi-row { border-top: 1px solid var(--theme-foreground-faint, #eee); }
.kpi-row-label { font-size: 0.8rem; color: var(--theme-foreground-muted, #666); white-space: nowrap; } .kpi-row-label { font-size: 0.8rem; color: var(--theme-foreground-muted, #666); white-space: nowrap; }

View File

@@ -3,8 +3,7 @@ title: Domains
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API, POLL} from "./components/config.js";
const POLL = 15_000;
``` ```
```js ```js

View File

@@ -3,8 +3,7 @@ title: Extension Points
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API, POLL} from "./components/config.js";
const POLL = 15_000;
``` ```
```js ```js
@@ -210,8 +209,6 @@ display(html`<div class="ep-list">${filtered.map(ep => html`
.live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; } .live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; }
/* ── KPI infobox ──────────────────────────────────────────────────────────── */ /* ── KPI infobox ──────────────────────────────────────────────────────────── */
.kpi-infobox { background: var(--theme-background-alt, #f9f9f9); border: 1px solid var(--theme-foreground-faint, #e0e0e0); border-radius: 10px; padding: 0.75rem 1rem; position: relative; box-shadow: 0 1px 6px rgba(0,0,0,0.07); margin-bottom: 1.25rem; }
.kpi-infobox-title { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--theme-foreground-muted, #888); margin-bottom: 0.55rem; }
.kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; border-top: 1px solid var(--theme-foreground-faint, #eee); } .kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; border-top: 1px solid var(--theme-foreground-faint, #eee); }
.kpi-row:first-of-type { border-top: none; } .kpi-row:first-of-type { border-top: none; }
.kpi-row-label { font-size: 0.8rem; color: var(--theme-foreground-muted, #666); } .kpi-row-label { font-size: 0.8rem; color: var(--theme-foreground-muted, #666); }
@@ -225,7 +222,6 @@ display(html`<div class="ep-list">${filtered.map(ep => html`
.chart-row { display: flex; flex-wrap: wrap; gap: 1.5rem; align-items: flex-start; } .chart-row { display: flex; flex-wrap: wrap; gap: 1.5rem; align-items: flex-start; }
/* ── Filters ──────────────────────────────────────────────────────────────── */ /* ── Filters ──────────────────────────────────────────────────────────────── */
.filter-bar { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-bottom: 1rem; }
/* ── EP list ──────────────────────────────────────────────────────────────── */ /* ── EP list ──────────────────────────────────────────────────────────────── */
.ep-list { display: flex; flex-direction: column; gap: 0.5rem; } .ep-list { display: flex; flex-direction: column; gap: 0.5rem; }

View File

@@ -3,7 +3,7 @@ title: Goals
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API} from "./components/config.js";
const POLL = 20_000; const POLL = 20_000;
``` ```
@@ -253,8 +253,6 @@ if (unlinkedRepoGoals.length > 0) {
.live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; } .live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; }
/* ── KPI infobox ──────────────────────────────────────────────────────────── */ /* ── KPI infobox ──────────────────────────────────────────────────────────── */
.kpi-infobox { background: var(--theme-background-alt, #f9f9f9); border: 1px solid var(--theme-foreground-faint, #e0e0e0); border-radius: 10px; padding: 0.75rem 1rem; position: relative; box-shadow: 0 1px 6px rgba(0,0,0,0.07); margin-bottom: 1.25rem; }
.kpi-infobox-title { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--theme-foreground-muted, #888); margin-bottom: 0.55rem; }
.kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; } .kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; }
.kpi-row + .kpi-row { border-top: 1px solid var(--theme-foreground-faint, #eee); } .kpi-row + .kpi-row { border-top: 1px solid var(--theme-foreground-faint, #eee); }
.kpi-row-label { font-size: 0.8rem; color: var(--theme-foreground-muted, #666); white-space: nowrap; } .kpi-row-label { font-size: 0.8rem; color: var(--theme-foreground-muted, #666); white-space: nowrap; }

View File

@@ -3,8 +3,7 @@ title: Overview
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API, POLL} from "./components/config.js";
const POLL = 15_000;
``` ```
```js ```js

View File

@@ -3,8 +3,7 @@ title: Interventions
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API, POLL} from "./components/config.js";
const POLL = 15_000;
``` ```
```js ```js
@@ -193,8 +192,6 @@ if (closed.length === 0) {
.live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; } .live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; }
/* ── KPI infobox ──────────────────────────────────────────────────────────── */ /* ── KPI infobox ──────────────────────────────────────────────────────────── */
.kpi-infobox { background: var(--theme-background-alt, #f9f9f9); border: 1px solid var(--theme-foreground-faint, #e0e0e0); border-radius: 10px; padding: 0.75rem 1rem; position: relative; box-shadow: 0 1px 6px rgba(0,0,0,0.07); margin-bottom: 1.25rem; }
.kpi-infobox-title { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--theme-foreground-muted, #888); margin-bottom: 0.55rem; }
.kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; } .kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; }
.kpi-row + .kpi-row { border-top: 1px solid var(--theme-foreground-faint, #eee); } .kpi-row + .kpi-row { border-top: 1px solid var(--theme-foreground-faint, #eee); }
.kpi-row-label { font-size: 0.8rem; color: var(--theme-foreground-muted, #666); white-space: nowrap; } .kpi-row-label { font-size: 0.8rem; color: var(--theme-foreground-muted, #666); white-space: nowrap; }

View File

@@ -3,8 +3,7 @@ title: Progress
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API, POLL} from "./components/config.js";
const POLL = 15_000;
``` ```
```js ```js

View File

@@ -3,7 +3,7 @@ title: Repos
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API} from "./components/config.js";
``` ```
```js ```js

View File

@@ -3,7 +3,7 @@ title: SBOM
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API} from "./components/config.js";
``` ```
```js ```js

View File

@@ -3,8 +3,7 @@ title: Tasks
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API, POLL} from "./components/config.js";
const POLL = 15_000;
``` ```
```js ```js
@@ -235,8 +234,6 @@ display(buildEntityTable(
.live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; } .live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; }
/* ── KPI infobox ──────────────────────────────────────────────────────────── */ /* ── KPI infobox ──────────────────────────────────────────────────────────── */
.kpi-infobox { background: var(--theme-background-alt, #f9f9f9); border: 1px solid var(--theme-foreground-faint, #e0e0e0); border-radius: 10px; padding: 0.75rem 1rem; position: relative; box-shadow: 0 1px 6px rgba(0,0,0,0.07); margin-bottom: 1.25rem; }
.kpi-infobox-title { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--theme-foreground-muted, #888); margin-bottom: 0.55rem; padding-right: 1.6rem; }
.kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; } .kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; }
.kpi-row + .kpi-row { border-top: 1px solid var(--theme-foreground-faint, #eee); } .kpi-row + .kpi-row { border-top: 1px solid var(--theme-foreground-faint, #eee); }
.kpi-row-label { font-size: 0.8rem; color: var(--theme-foreground-muted, #666); white-space: nowrap; } .kpi-row-label { font-size: 0.8rem; color: var(--theme-foreground-muted, #666); white-space: nowrap; }
@@ -245,9 +242,6 @@ display(buildEntityTable(
.kpi-row-sub { font-size: 0.68rem; color: var(--theme-foreground-faint, #aaa); line-height: 1.2; } .kpi-row-sub { font-size: 0.68rem; color: var(--theme-foreground-faint, #aaa); line-height: 1.2; }
/* ── Filters ──────────────────────────────────────────────────────────────── */ /* ── Filters ──────────────────────────────────────────────────────────────── */
.filter-bar { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-bottom: 1rem; }
.filter-owner { display: flex; align-items: center; }
.filter-owner input { height: 30px; font-size: 0.85rem; padding: 0.25rem 0.5rem; border-radius: 6px; border: 1px solid var(--theme-foreground-faint, #ccc); background: var(--theme-background, #fff); font-family: inherit; color: inherit; }
/* ── Blocked task cards ───────────────────────────────────────────────────── */ /* ── Blocked task cards ───────────────────────────────────────────────────── */
.task-blocked-list { display: flex; flex-direction: column; gap: 0.5rem; } .task-blocked-list { display: flex; flex-direction: column; gap: 0.5rem; }

View File

@@ -3,8 +3,7 @@ title: Technical Debt
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API, POLL} from "./components/config.js";
const POLL = 15_000;
``` ```
```js ```js
@@ -246,8 +245,6 @@ display(html`<div class="td-list">${filtered.map(t => html`
.live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; } .live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; }
/* ── KPI infobox ──────────────────────────────────────────────────────────── */ /* ── KPI infobox ──────────────────────────────────────────────────────────── */
.kpi-infobox { background: var(--theme-background-alt, #f9f9f9); border: 1px solid var(--theme-foreground-faint, #e0e0e0); border-radius: 10px; padding: 0.75rem 1rem; position: relative; box-shadow: 0 1px 6px rgba(0,0,0,0.07); margin-bottom: 1.25rem; }
.kpi-infobox-title { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--theme-foreground-muted, #888); margin-bottom: 0.55rem; }
.kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; border-top: 1px solid var(--theme-foreground-faint, #eee); } .kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; border-top: 1px solid var(--theme-foreground-faint, #eee); }
.kpi-row:first-of-type { border-top: none; } .kpi-row:first-of-type { border-top: none; }
.kpi-row-label { font-size: 0.8rem; color: var(--theme-foreground-muted, #666); } .kpi-row-label { font-size: 0.8rem; color: var(--theme-foreground-muted, #666); }
@@ -259,7 +256,6 @@ display(html`<div class="td-list">${filtered.map(t => html`
.chart-row { display: flex; flex-wrap: wrap; gap: 1.5rem; align-items: flex-start; } .chart-row { display: flex; flex-wrap: wrap; gap: 1.5rem; align-items: flex-start; }
/* ── Filters ──────────────────────────────────────────────────────────────── */ /* ── Filters ──────────────────────────────────────────────────────────────── */
.filter-bar { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-bottom: 1rem; }
/* ── TD list ──────────────────────────────────────────────────────────────── */ /* ── TD list ──────────────────────────────────────────────────────────────── */
.td-list { display: flex; flex-direction: column; gap: 0.5rem; } .td-list { display: flex; flex-direction: column; gap: 0.5rem; }

View File

@@ -3,8 +3,7 @@ title: Todo
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API, POLL} from "./components/config.js";
const POLL = 15_000;
const THIS_REPO = "the-custodian"; const THIS_REPO = "the-custodian";
``` ```
@@ -204,8 +203,6 @@ if (thirdParty.length === 0) {
.live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; } .live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; }
/* ── KPI infobox ──────────────────────────────────────────────────────────── */ /* ── KPI infobox ──────────────────────────────────────────────────────────── */
.kpi-infobox { background: var(--theme-background-alt, #f9f9f9); border: 1px solid var(--theme-foreground-faint, #e0e0e0); border-radius: 10px; padding: 0.75rem 1rem; position: relative; box-shadow: 0 1px 6px rgba(0,0,0,0.07); margin-bottom: 1.25rem; }
.kpi-infobox-title { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--theme-foreground-muted, #888); margin-bottom: 0.55rem; }
.kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; } .kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; }
.kpi-row + .kpi-row { border-top: 1px solid var(--theme-foreground-faint, #eee); } .kpi-row + .kpi-row { border-top: 1px solid var(--theme-foreground-faint, #eee); }
.kpi-row-label { font-size: 0.8rem; color: var(--theme-foreground-muted, #666); white-space: nowrap; } .kpi-row-label { font-size: 0.8rem; color: var(--theme-foreground-muted, #666); white-space: nowrap; }

View File

@@ -3,8 +3,7 @@ title: Workstreams
--- ---
```js ```js
const API = "http://127.0.0.1:8000"; import {API, POLL} from "./components/config.js";
const POLL = 15_000;
``` ```
```js ```js
@@ -330,8 +329,6 @@ if (wsWithDeps.length === 0) {
.live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; } .live-indicator { font-size: 0.8rem; color: gray; position: relative; padding: 0.55rem 1.8rem 0.55rem 0.7rem; margin-bottom: 0.75rem; }
/* ── KPI infobox base (shared) ───────────────────────────────────────────── */ /* ── KPI infobox base (shared) ───────────────────────────────────────────── */
.kpi-infobox { background: var(--theme-background-alt, #f9f9f9); border: 1px solid var(--theme-foreground-faint, #e0e0e0); border-radius: 10px; padding: 0.75rem 1rem; position: relative; box-shadow: 0 1px 6px rgba(0,0,0,0.07); margin-bottom: 1.25rem; }
.kpi-infobox-title { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--theme-foreground-muted, #888); margin-bottom: 0.55rem; padding-right: 1.6rem; }
.kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; } .kpi-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.3rem 0; }
.kpi-muted { color: var(--theme-foreground-faint, #aaa); font-style: italic; font-size: 0.8rem; } .kpi-muted { color: var(--theme-foreground-faint, #aaa); font-style: italic; font-size: 0.8rem; }
@@ -352,9 +349,6 @@ if (wsWithDeps.length === 0) {
.whi-domain-name { flex: 1; font-size: 0.75rem; color: var(--theme-foreground-muted, #666); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .whi-domain-name { flex: 1; font-size: 0.75rem; color: var(--theme-foreground-muted, #666); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.whi-domain-score { font-variant-numeric: tabular-nums; font-weight: 600; font-size: 0.75rem; } .whi-domain-score { font-variant-numeric: tabular-nums; font-weight: 600; font-size: 0.75rem; }
.dim { color: gray; font-style: italic; } .dim { color: gray; font-style: italic; }
.filter-bar { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-bottom: 1rem; }
.filter-owner { display: flex; align-items: center; }
.filter-owner input { height: 30px; font-size: 0.85rem; padding: 0.25rem 0.5rem; border-radius: 6px; border: 1px solid var(--theme-foreground-faint, #ccc); background: var(--theme-background, #fff); font-family: inherit; color: inherit; }
.dep-grid { display: flex; flex-direction: column; gap: 0.75rem; } .dep-grid { display: flex; flex-direction: column; gap: 0.75rem; }
.dep-card { border: 1px solid #e0e0e0; border-radius: 6px; padding: 0.75rem 1rem; background: var(--theme-background-alt, #fafafa); } .dep-card { border: 1px solid #e0e0e0; border-radius: 6px; padding: 0.75rem 1rem; background: var(--theme-background-alt, #fafafa); }
.dep-title { font-weight: 600; margin-bottom: 0.25rem; } .dep-title { font-weight: 600; margin-bottom: 0.25rem; }