generated from coulomb/repo-seed
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.
179 lines
6.8 KiB
Markdown
179 lines
6.8 KiB
Markdown
---
|
||
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
|