Finish CUST-WP-0011 and implement STATE-WP-0061 suggestion backlog.
Add persisted Suggestion entities with WSJF ranking, relevance bumps, REST/MCP write surfaces, /suggestions dashboard page, and daily triage digest integration. Document the cluster operating model, archive the completed migration workplan, and seed WARDEN-WP-0012 routing scenarios.
This commit is contained in:
parent
cac9a6b1e0
commit
f2e042a278
31 changed files with 1537 additions and 66 deletions
79
dashboard/src/suggestions.md
Normal file
79
dashboard/src/suggestions.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
---
|
||||
title: Suggestions
|
||||
---
|
||||
|
||||
```js
|
||||
import {apiFetch, pollDelay, waitForVisible} from "./components/config.js";
|
||||
const POLL = 30_000;
|
||||
```
|
||||
|
||||
```js
|
||||
const sugState = (async function*() {
|
||||
let failures = 0;
|
||||
while (true) {
|
||||
let data = [], ok = false;
|
||||
try {
|
||||
const r = await apiFetch("/suggestions/?rank=wsjf&limit=100");
|
||||
ok = r.ok;
|
||||
data = ok ? await r.json() : [];
|
||||
} catch {}
|
||||
failures = ok ? 0 : failures + 1;
|
||||
yield {data, ok, ts: new Date()};
|
||||
await waitForVisible(pollDelay({ok, base: POLL, failures}));
|
||||
}
|
||||
})();
|
||||
```
|
||||
|
||||
```js
|
||||
const suggestions = sugState.data ?? [];
|
||||
const _ok = sugState.ok ?? false;
|
||||
const _ts = sugState.ts;
|
||||
```
|
||||
|
||||
# Demand-Weighted Suggestions
|
||||
|
||||
```js
|
||||
import {injectTocTop} from "./components/toc-sidebar.js";
|
||||
import {withDocHelp} from "./components/doc-overlay.js";
|
||||
|
||||
const _liveEl = html`<div class="live-indicator">
|
||||
<span style="color:${_ok ? 'var(--theme-foreground-focus)' : 'red'}">●</span>
|
||||
${_ok ? `Live · ${_ts?.toLocaleTimeString()}` : html`<span style="color:red">API offline</span>`}
|
||||
</div>`;
|
||||
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/suggestions"); }
|
||||
|
||||
display(html`<p class="dim">Ranked by WSJF = (base_value + relevance_weight × relevance) / job_size. Gated needs accrue relevance when unmet.</p>`);
|
||||
display(html`<p class="dim"><a href="/wsjf-triage">Daily WSJF triage</a> consumes this backlog in its digest.</p>`);
|
||||
```
|
||||
|
||||
```js
|
||||
const stageBadge = (stage) => {
|
||||
const colors = {
|
||||
suggestion: "#6b7280",
|
||||
requirement: "#2563eb",
|
||||
promoted: "#059669",
|
||||
declined: "#9ca3af",
|
||||
};
|
||||
return html`<span style="font-size:0.75rem;padding:0.1rem 0.45rem;border-radius:4px;background:${colors[stage] ?? "#ccc'};color:#fff">${stage}</span>`;
|
||||
};
|
||||
|
||||
const rows = suggestions.map((s) => html`<tr>
|
||||
<td>${stageBadge(s.stage)}</td>
|
||||
<td><strong>${s.title}</strong>${s.origin_ref ? html`<br/><code style="font-size:0.75rem">${s.origin_ref}</code>` : ""}</td>
|
||||
<td>${s.domain_slug || "—"}</td>
|
||||
<td style="text-align:right">${s.relevance}</td>
|
||||
<td style="text-align:right">${s.wsjf?.toFixed?.(1) ?? s.wsjf}</td>
|
||||
<td style="font-size:0.8rem">${s.last_requested_at ? new Date(s.last_requested_at).toLocaleString() : "—"}</td>
|
||||
</tr>`);
|
||||
|
||||
display(html`<table class="dashboard-table" style="width:100%;font-size:0.88rem">
|
||||
<thead><tr>
|
||||
<th>Stage</th><th>Title</th><th>Domain</th><th>Relevance</th><th>WSJF</th><th>Last requested</th>
|
||||
</tr></thead>
|
||||
<tbody>${rows.length ? rows : html`<tr><td colspan="6" class="dim">No open suggestions yet.</td></tr>`}</tbody>
|
||||
</table>`);
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue