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 ae03a7970c
commit 2cd061c1d1
19 changed files with 36 additions and 84 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; }

View File

@@ -3,12 +3,13 @@ id: CUST-WP-0004
type: workplan type: workplan
title: "Extension Points & Technical Debt Tracking" title: "Extension Points & Technical Debt Tracking"
domain: custodian domain: custodian
status: active status: completed
owner: custodian owner: custodian
topic_slug: custodian topic_slug: custodian
state_hub_workstream_id: e84ecd70-b276-4a93-a298-1ef1299b7c22 state_hub_workstream_id: e84ecd70-b276-4a93-a298-1ef1299b7c22
created: "2026-02-27" created: "2026-02-27"
updated: "2026-02-28" updated: "2026-03-11"
completed: "2026-03-11"
--- ---
# Extension Points & Technical Debt Tracking # Extension Points & Technical Debt Tracking
@@ -87,7 +88,7 @@ endpoints).
```task ```task
id: CUST-WP-0004-T05 id: CUST-WP-0004-T05
state_hub_task_id: 00492d87-3bb7-4df9-8f12-a06da0307fbc state_hub_task_id: 00492d87-3bb7-4df9-8f12-a06da0307fbc
status: todo status: done
priority: medium priority: medium
``` ```
@@ -99,7 +100,7 @@ Register findings as `TD-RAIL-xxx` items via the `/technical-debt/` API.
```task ```task
id: CUST-WP-0004-T06 id: CUST-WP-0004-T06
state_hub_task_id: 3069aa1d-3d5f-4b9f-be8b-dfa0ffe690fe state_hub_task_id: 3069aa1d-3d5f-4b9f-be8b-dfa0ffe690fe
status: todo status: done
priority: medium priority: medium
``` ```
@@ -112,7 +113,7 @@ gaps, integration opportunities.
```task ```task
id: CUST-WP-0004-T07 id: CUST-WP-0004-T07
state_hub_task_id: 3a355a74-237f-4572-af05-683eceb32694 state_hub_task_id: 3a355a74-237f-4572-af05-683eceb32694
status: todo status: done
priority: low priority: low
``` ```
@@ -123,7 +124,7 @@ Identify and register extension points in railiance as `EP-RAIL-xxx` items.
```task ```task
id: CUST-WP-0004-T08 id: CUST-WP-0004-T08
state_hub_task_id: a9b5d39e-706b-4f71-8f5d-ce4f927b3e4c state_hub_task_id: a9b5d39e-706b-4f71-8f5d-ce4f927b3e4c
status: todo status: done
priority: high priority: high
``` ```
@@ -136,7 +137,7 @@ variable.
```task ```task
id: CUST-WP-0004-T09 id: CUST-WP-0004-T09
state_hub_task_id: 7050eb96-964d-4c06-a9f5-6d49db6b9c98 state_hub_task_id: 7050eb96-964d-4c06-a9f5-6d49db6b9c98
status: todo status: done
priority: high priority: high
``` ```
@@ -149,7 +150,7 @@ technical_debt) to prevent memory exhaustion.
```task ```task
id: CUST-WP-0004-T10 id: CUST-WP-0004-T10
state_hub_task_id: c54553be-d947-4b8d-9c16-eb1a0e9ae3a0 state_hub_task_id: c54553be-d947-4b8d-9c16-eb1a0e9ae3a0
status: todo status: done
priority: medium priority: medium
``` ```