Files
state-hub/workplans/archived/260706-STATE-WP-0061-demand-weighted-suggestion-backlog.md
tegwick f2e042a278 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.
2026-07-06 10:52:49 +02:00

179 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: STATE-WP-0061
type: workplan
title: "Demand-weighted suggestion backlog (relevance-fed WSJF)"
domain: infotech
repo: state-hub
status: finished
owner: codex
topic_slug: custodian
created: "2026-06-18"
updated: "2026-07-06"
finished: "2026-07-06"
state_hub_workstream_id: "34b446d2-bcd3-4fe3-85e9-32b293839770"
---
# STATE-WP-0061 — Demand-weighted suggestion backlog (relevance-fed WSJF)
**Origin:** ops-warden WP-0012 triage (2026-06-18). Most WP-0012 tasks are gated
on external owners shipping paths that do not exist yet. They should not sit as
inert `todo` tasks, nor be fabricated as active entries. They should live as
**suggestions that accrue demand pressure** every time they are needed-but-unmet,
so in-demand work is promoted to real tasks first.
## Problem
The hub today cannot represent "a need that has been raised but not yet vetted or
scheduled, whose urgency grows with repeated demand." Concretely:
- `NextStep` suggestions are **derived on the fly and never persisted**
(`api/schemas/state.py`), so they cannot accumulate anything.
- There is **no relevance/demand counter** on any entity.
- There is **no suggestion → vetted requirement → task** promotion pipeline.
`CapabilityRequest` is the closest analog but models cross-domain brokering, and
a repeat need spawns a *new* request rather than bumping demand on an existing one.
- The **WSJF triage is advisory** (activity-core `daily-statehub-wsjf-triage`,
CUST-WP-0044) and consumes current summary/workplan/progress state — **no
persisted demand signal feeds it**, and the hub holds no Cost-of-Delay / Job-Size
data model.
## Approach (decided)
- **New `Suggestion` entity** (not an extension of `CapabilityRequest`/`TechnicalDebt`).
Stages: `suggestion``requirement` (vetted + structured) → `promoted` (became a
Task); plus terminal `declined`. Append-only `SuggestionNote` trail (mirrors
`TDNote`) records vetting. `promoted_task_id` links the resulting `Task`.
- **Relevance is a persisted demand counter.** It increments whenever the
suggestion is *needed but not yet done* (defined in T3). `relevance` +
`last_requested_at` + a `relevance_events` count drive ranking.
- **WSJF is computed in the hub as a read-model projection** and exposed via a
ranked endpoint; the existing activity-core daily triage **consumes** it.
`wsjf = cost_of_delay / job_size`, where
`cost_of_delay = base_value + (relevance_weight × relevance)` so repeated demand
raises priority. Job size is an operator-set estimate (default medium).
- Promotion keeps the active task backlog clean: gated needs (the WP-0012 case)
live as relevance-accruing suggestions, **not** as inert `todo` tasks or
fabricated active catalog entries.
> **Read-model boundary (ADR-001 / hub design):** suggestion writes are a new
> sanctioned write surface alongside `resolve_decision` and `get_next_steps`.
> `bump_relevance`, `promote_suggestion_to_task`, and vetting transitions are the
> only writes; ranking/WSJF are pure projections. T6 records the ADR amendment.
## Open questions (resolve during T1/T4, do not block proposal)
- Exact WSJF cost-of-delay decomposition (single `base_value` vs SAFe triple of
business-value / time-criticality / risk-reduction). Start with `base_value`
+ relevance; leave room to split later.
- `relevance_weight` default and whether relevance should decay over time
(staleness) — model the field now, tune in T4.
---
## Tasks
### T1 — Suggestion data model + migration
```task
id: STATE-WP-0061-T01
status: done
priority: high
state_hub_task_id: "5cb4d6df-47c1-46c7-af88-4e7db02b2b33"
completed: "2026-07-06"
```
- [x] `api/models/suggestion.py`: `Suggestion` + `SuggestionNote` +
`SuggestionRelevanceBump` audit trail.
- [x] `SuggestionStage` enum: `suggestion | requirement | promoted | declined`.
- [x] Alembic migration `f0a1b2c3d4e5`; registered in `api/models/__init__.py`.
### T2 — API + MCP sanctioned write layer
```task
id: STATE-WP-0061-T02
status: done
priority: high
state_hub_task_id: "ebc5238c-0714-4413-99ca-37bb2468ac58"
completed: "2026-07-06"
```
- [x] REST `api/routers/suggestions.py` + MCP tools for create/vet/decline/promote/list/get.
- [x] `POST /suggestions/{id}/bump-relevance` with debounced relevance bumps.
- [x] Documented in `mcp_server/TOOLS.md`, `docs/capabilities.md`, `INTENT.md`.
### T3 — Relevance emission wiring ("needed but not done")
```task
id: STATE-WP-0061-T03
status: done
priority: high
state_hub_task_id: "e7e87595-8af8-43f3-8372-0ddde44a5b82"
completed: "2026-07-06"
```
- [x] `get_next_steps` surfaces open suggestions and bumps surfaced items.
- [x] Capability request create bumps matching open suggestions.
- [x] Explicit `bump_suggestion_relevance` MCP/REST write.
- [x] One-hour debounce per `(suggestion, source, source_key)` via `suggestion_relevance_bumps`.
### T4 — WSJF projection + ranked endpoint
```task
id: STATE-WP-0061-T04
status: done
priority: high
state_hub_task_id: "f6fccd58-5c47-4509-ba0b-9f606dfb53de"
completed: "2026-07-06"
```
- [x] WSJF projection in `api/services/suggestion_wsjf.py`.
- [x] `GET /suggestions?rank=wsjf` with terminal exclusion by default.
- [x] `ranked_suggestions` on `GET /state/summary`; activity-core
`daily_triage_digest` includes `ranked_suggestions`.
### T5 — Dashboard surface
```task
id: STATE-WP-0061-T05
status: done
priority: medium
state_hub_task_id: "4dcca789-3c63-46fb-a1ec-9ae9a68d1a4b"
completed: "2026-07-06"
```
- [x] `/suggestions` dashboard page with WSJF-ranked live table.
- [x] Linked from `/wsjf-triage`; `dashboard/src/docs/suggestions.md` added.
### T6 — Tests, docs, ADR amendment
```task
id: STATE-WP-0061-T06
status: done
priority: medium
state_hub_task_id: "a7832268-fa2b-4531-b91f-dc31f92830af"
completed: "2026-07-06"
```
- [x] `tests/test_suggestions.py` — WSJF order, debounce, promotion, illegal transitions.
- [x] `SCOPE.md`, `INTENT.md`, `docs/activity-core-delegation.md` updated.
- [x] `scripts/seed_wp0012_suggestions.py` for WARDEN-WP-0012 scenario backfill.
---
## Acceptance
- A need can be recorded as a `suggestion`, vetted into a `requirement`, and
promoted into a real `Task` — with the demand trail preserved.
- Each unmet lookup increments `relevance`; higher relevance raises WSJF, and
`GET /suggestions?rank=wsjf` reflects the new order.
- The daily WSJF triage report includes the ranked suggestion backlog.
- Gated work (WP-0012-style) lives as a relevance-accruing suggestion, never as an
inert `todo` task or a fabricated active catalog entry.
## See also
- `STATE-WP-0053` — WSJF triage review page (consumer surface)
- `CUST-WP-0044` — activity-core daily triage runner (producer; cross-repo seam)
- `api/models/capability_request.py`, `api/models/technical_debt.py` — prior art
- ops-warden `WARDEN-WP-0012` — the gated-backlog case that motivated this