feat(terminology): workplan-first dashboard and retirement backlog (STATE-WP-0069)
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Add the ranked legacy-interface backlog (T01), rename dashboard navigation and
user-facing copy to workplan while preserving wire-compat API keys (T02), and
activate the retirement workplan with T01/T02 marked done.
This commit is contained in:
tegwick 2026-07-08 16:08:32 +02:00
parent 4dc69bb8f5
commit b2264d1f06
44 changed files with 382 additions and 253 deletions

View file

@ -148,10 +148,10 @@ if (_h1) { _h1.style.position = "relative"; withDocHelp(_h1, "/docs/overview");
display(html`<div class="warning" style="display:${summary.error ? '' : 'none'}">⚠️ ${summary.error ?? ''}</div>`);
```
## Workstreams by Repository
## Workplans by Repository
```js
// ── Filter workstreams by selected mode ───────────────────────────────────────
// ── Filter workplans by selected mode ───────────────────────────────────────
// Lifecycle modes match stored canonical status values.
// Health modes are derived labels; they are not stored lifecycle states.
// Time modes filter by updated_at / created_at.
@ -251,8 +251,8 @@ function _setChartMode(value) {
```js
const _modeSelect = html`<select
class="ws-mode-select"
aria-label="Workstream chart mode with matching workstream counts"
title="Choose which workstreams to show; counts are matching workstreams"
aria-label="Workplan chart mode with matching workplan counts"
title="Choose which workplans to show; counts are matching workplans"
>
${_MODE_GROUPS.map(group => html`<optgroup label=${group.label}>
${group.options.map(([value, label]) => html`<option value=${value}>${label} (${_workstreamsForMode(value, wsAll).length})</option>`)}
@ -274,7 +274,7 @@ import * as Plot from "npm:@observablehq/plot";
const _chartModeValue = _modeValue(_chartModeState);
const _chartWsFiltered = _workstreamsForMode(_chartModeValue, wsAll);
// Sort by domain, then repository, then most recently updated workstream.
// Sort by domain, then repository, then most recently updated workplan.
// The axis labels show each domain/repo group once.
const chartWs = [..._chartWsFiltered].sort((a, b) => {
const domainCompare = (a.domain ?? "").localeCompare(b.domain ?? "");
@ -289,7 +289,7 @@ const chartWs = [..._chartWsFiltered].sort((a, b) => {
const _isTimeBased = !_STATUS_MODES.has(_chartModeValue) && !_HEALTH_MODES.has(_chartModeValue);
function _wsWeight(s) { return (isClosedWorkstream(s) || normalizeWorkstreamStatus(s) === "blocked") ? "bold" : "normal"; }
// ── y-axis: domain/repo label for first workstream per repository only ────────
// ── y-axis: domain/repo label for first workplan per repository only ────────
const _yLabels = {};
const _seen = new Set();
for (const w of chartWs) {
@ -321,24 +321,24 @@ function _wsTitle(d) {
// ── Render ────────────────────────────────────────────────────────────────────
if (chartWs.length === 0) {
const _emptyMsg = {
proposed: "No proposed workstreams.",
ready: "No ready workstreams.",
active: "No active workstreams.",
blocked: "No blocked workstreams.",
backlog: "No backlog workstreams.",
finished: "No finished workstreams.",
archived: "No archived workstreams.",
needs_review: "No ready workstreams need review.",
stalled: "No stalled workstreams — everything is moving.",
"1h": "No workstreams changed in the last hour.",
"1d": "No workstreams changed in the last 24 hours.",
"7d": "No workstreams changed in the last 7 days.",
"30d": "No workstreams changed in the last 30 days.",
today: "No workstreams changed today.",
week: "No workstreams changed this week.",
month: "No workstreams changed this month.",
proposed: "No proposed workplans.",
ready: "No ready workplans.",
active: "No active workplans.",
blocked: "No blocked workplans.",
backlog: "No backlog workplans.",
finished: "No finished workplans.",
archived: "No archived workplans.",
needs_review: "No ready workplans need review.",
stalled: "No stalled workplans — everything is moving.",
"1h": "No workplans changed in the last hour.",
"1d": "No workplans changed in the last 24 hours.",
"7d": "No workplans changed in the last 7 days.",
"30d": "No workplans changed in the last 30 days.",
today: "No workplans changed today.",
week: "No workplans changed this week.",
month: "No workplans changed this month.",
};
display(html`<p style="color:gray">${_emptyMsg[_chartModeValue] ?? "No workstreams."}</p>`);
display(html`<p style="color:gray">${_emptyMsg[_chartModeValue] ?? "No workplans."}</p>`);
} else {
display(Plot.plot({
y: {
@ -445,7 +445,7 @@ const decCount = (decisions.open ?? 0) + (decisions.escalated ?? 0);
const statusEl = html`<div>
<div class="grid grid-cols-4" style="gap:1rem;margin-bottom:0.75rem">
<a class="card card-link" href="./workstreams">
<h3>Active Workstreams</h3>
<h3>Active Workplans</h3>
<p class="big-num">${ws.active ?? 0}</p>
<small>${ws.blocked ?? 0} blocked</small>
</a>
@ -510,7 +510,7 @@ const typeBadgeClass = {
};
if (nextSteps.length === 0) {
display(html`<p class="ns-empty">No actionable suggestions right now — all open workstreams are making progress or waiting on decisions.</p>`);
display(html`<p class="ns-empty">No actionable suggestions right now — all open workplans are making progress or waiting on decisions.</p>`);
} else {
display(html`<div class="ns-grid">${nextSteps.map(s => html`
<div class="ns-card">
@ -543,7 +543,7 @@ if (regs.length === 0) {
```
```js
// Registered domains with no workstreams yet — show a getting-started hint
// Registered domains with no workplans yet — show a getting-started hint
const regs = pageState.milestones ?? [];
const registeredDomains = new Set(regs.map(e => e.detail?.domain).filter(Boolean));
const emptyRegistered = (summary.topics ?? []).filter(t =>
@ -553,9 +553,9 @@ const emptyRegistered = (summary.topics ?? []).filter(t =>
if (emptyRegistered.length > 0) {
display(html`<div class="hint-box">
<strong>💡 Getting started</strong>
<p>These registered projects have no workstreams yet:</p>
<p>These registered projects have no workplans yet:</p>
<ul>${emptyRegistered.map(t => html`<li>
<strong>${t.domain_slug}</strong> — open repo in Claude Code and say <em>"Hi!"</em> to kick off first session, or run <code>custodian create-workstream --domain ${t.domain_slug} --title "My first workstream"</code> manually
<strong>${t.domain_slug}</strong> — open repo in Claude Code and say <em>"Hi!"</em> to kick off first session, or create a workplan file under <code>workplans/</code> and run <code>statehub fix-consistency</code>
</li>`)}</ul>
</div>`);
}