generated from coulomb/repo-seed
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:
@@ -0,0 +1,458 @@
|
||||
---
|
||||
id: CUST-WP-0011
|
||||
type: workplan
|
||||
title: "Pragmatic State Hub Migration to railiance01"
|
||||
domain: infotech
|
||||
repo: state-hub
|
||||
status: finished
|
||||
owner: custodian
|
||||
topic_slug: custodian
|
||||
created: "2026-03-11"
|
||||
updated: "2026-07-06"
|
||||
finished: "2026-07-06"
|
||||
state_hub_workstream_id: "967baafb-d92d-405a-ba0b-0d00d37c4940"
|
||||
supersedes_intent_from: "Migrate Custodian State Hub to ThreePhoenix Cluster"
|
||||
follow_up_workplan: CUST-WP-0038
|
||||
---
|
||||
|
||||
# Pragmatic State Hub Migration to railiance01
|
||||
|
||||
## Goal
|
||||
|
||||
Move the Custodian State Hub (FastAPI + PostgreSQL) from the WSL2 operator
|
||||
workstation to the current railiance01 Kubernetes environment, using the
|
||||
Railiance production-readiness path that exists now:
|
||||
|
||||
- CloudNative PG (`cnpg`) for the State Hub database in the `databases`
|
||||
namespace.
|
||||
- State Hub as an S5 workload in `railiance-apps`.
|
||||
- Platform/database ownership in `railiance-platform`.
|
||||
- Access through the existing private tunnel/ops-bridge model, not public
|
||||
exposure.
|
||||
- WSL2 retained as a disaster-recovery fallback until the cluster deployment
|
||||
has proven stable.
|
||||
|
||||
This is a deliberate pragmatic step. It improves durability and multi-machine
|
||||
access before the full ThreePhoenix target is ready. The ultimate multi-node,
|
||||
replicated, long-term cluster goal is preserved in `CUST-WP-0038`.
|
||||
|
||||
## Context Update
|
||||
|
||||
The original 2026-03-11 version of this workplan targeted a future
|
||||
ThreePhoenix cluster with Railiance01/02/03, Longhorn, and full HA gates before
|
||||
starting. That was correct as an end-state, but it blocks useful progress now.
|
||||
|
||||
The current Railiance architecture has moved on:
|
||||
|
||||
- `railiance-infra/workplans/RAIL-HO-WP-0004-production-readiness.md`
|
||||
supersedes the older Bitnami PostgreSQL HA platform baseline.
|
||||
- CloudNative PG is the deployed database operator.
|
||||
- `RAIL-HO-WP-0004-T09` is the Railiance-side task for deploying State Hub to
|
||||
the cluster, and it still requires human decisions before live data
|
||||
migration.
|
||||
|
||||
This workplan is now the Custodian-side coordination and safety plan for that
|
||||
T09 effort.
|
||||
|
||||
## Safety Contract
|
||||
|
||||
State Hub is irreplaceable episodic memory. This migration may prepare, deploy,
|
||||
test, and compare as much as needed, but it must not make the cluster the only
|
||||
source of truth until the explicit cutover gate is satisfied.
|
||||
|
||||
Rules:
|
||||
|
||||
- A fresh WSL2 backup and restore drill is mandatory before data migration.
|
||||
- The WSL2 State Hub remains available as rollback until stabilisation passes.
|
||||
- Any task that changes the live writer endpoint requires explicit human
|
||||
approval.
|
||||
- A failed cluster deploy must leave the WSL2 instance untouched and usable.
|
||||
- Row counts and key API checks must match before cutover.
|
||||
|
||||
## Target Architecture After This Workplan
|
||||
|
||||
```
|
||||
Operator workstation / COULOMBCORE / other agent hosts
|
||||
-> local MCP server subprocess
|
||||
-> http://127.0.0.1:8000 or configured API_BASE
|
||||
-> private tunnel / ops-bridge
|
||||
-> railiance01 k3s
|
||||
-> state-hub Service
|
||||
-> FastAPI Deployment
|
||||
-> state-hub-db CloudNative PG Cluster
|
||||
```
|
||||
|
||||
Key properties:
|
||||
|
||||
- Single-node pragmatic deployment on railiance01.
|
||||
- No public unauthenticated exposure.
|
||||
- Database managed by cnpg, not an ad-hoc Postgres StatefulSet.
|
||||
- WSL2 retained as DR fallback during stabilisation.
|
||||
- Future multi-node HA and storage replication are deferred to `CUST-WP-0038`.
|
||||
|
||||
## Open Human Decisions
|
||||
|
||||
Resolve these before T04/T05 can become live migration work:
|
||||
|
||||
1. Final State Hub hostname or tunnel-only endpoint.
|
||||
2. Container registry choice: Gitea registry vs external interim registry.
|
||||
3. Exposure model: ClusterIP plus tunnel, private ingress, or both.
|
||||
4. Approval window for freezing WSL2 writes and migrating the production DB.
|
||||
5. Stabilisation duration before WSL2 can be considered non-primary fallback.
|
||||
|
||||
## Tasks
|
||||
|
||||
### T01 — Drill WSL2 State Hub backup restore
|
||||
|
||||
```task
|
||||
id: CUST-WP-0011-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "b0caf112-dc1d-43a8-9f27-d627dd4aa2bf"
|
||||
completed: "2026-05-02"
|
||||
```
|
||||
|
||||
Take a fresh State Hub backup from the current WSL2 instance and restore it
|
||||
into an isolated test PostgreSQL instance.
|
||||
|
||||
Minimum checks:
|
||||
|
||||
- Restore completes without errors.
|
||||
- Core table row counts match the live WSL2 database.
|
||||
- `/state/summary` can be served from the restored copy if wired to a test API.
|
||||
- Drill result is recorded in State Hub progress and, if useful, episodic
|
||||
memory.
|
||||
|
||||
**Done when:** backup and restore are proven within 24 hours of live migration
|
||||
work.
|
||||
|
||||
Result: completed 2026-05-02. A fresh dump from `infra-postgres-1` restored
|
||||
into disposable container `state-hub-restore-test` on `127.0.0.1:5433`.
|
||||
Application health and summary checks against the restored database returned
|
||||
HTTP 200. Restored row counts matched production exactly, including 117
|
||||
workstreams, 989 tasks, 1423 progress events, and 208 token events.
|
||||
|
||||
---
|
||||
|
||||
### T02 — Align with Railiance deployment plan
|
||||
|
||||
```task
|
||||
id: CUST-WP-0011-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "24887dd9-7d50-4cc4-add7-bffa1454b80c"
|
||||
completed: "2026-05-02"
|
||||
```
|
||||
|
||||
Update the cross-repo plan so this Custodian workplan and
|
||||
`RAIL-HO-WP-0004-T09` point to the same architecture.
|
||||
|
||||
Expected outputs:
|
||||
|
||||
- `RAIL-HO-WP-0004-T09` remains the Railiance-side execution task.
|
||||
- This workplan remains the Custodian-side safety/cutover task list.
|
||||
- Any stale Longhorn/Postgres StatefulSet assumptions are removed from the
|
||||
near-term migration plan.
|
||||
- The future HA goal is referenced through `CUST-WP-0038`.
|
||||
|
||||
**Done when:** both workplans describe compatible responsibilities and gates.
|
||||
|
||||
Result: completed 2026-05-02. `RAIL-HO-WP-0004-T09` now names the same
|
||||
pragmatic railiance01 path: cnpg database, S5 State Hub workload, restore drill
|
||||
precondition, empty deploy before data copy, explicit human approval before
|
||||
freezing WSL2 writes, and WSL2 retained as fallback. Full ThreePhoenix HA stays
|
||||
deferred to `CUST-WP-0038`.
|
||||
|
||||
---
|
||||
|
||||
### T03 — Build and publish State Hub container image
|
||||
|
||||
```task
|
||||
id: CUST-WP-0011-T03
|
||||
status: done
|
||||
priority: high
|
||||
completed: "2026-06-25"
|
||||
state_hub_task_id: "79908ade-3e38-451b-a403-2361a16a3f3a"
|
||||
```
|
||||
|
||||
Package this repository as a production image.
|
||||
|
||||
Requirements:
|
||||
|
||||
- Dockerfile builds from the current Python/uv project.
|
||||
- Alembic and runtime dependencies are available inside the image.
|
||||
- Image exposes the FastAPI service on port 8000.
|
||||
- Image tag is pushed to the chosen registry.
|
||||
- Build provenance is documented in the commit/workplan.
|
||||
|
||||
**Done when:** railiance01 can pull the image and a dry-run deployment resolves
|
||||
it.
|
||||
|
||||
Progress 2026-05-03: added `Dockerfile`,
|
||||
`.dockerignore`, and `docs/container-image.md`. Built
|
||||
local image `state-hub:local` successfully:
|
||||
`sha256:e96dbd1e7d2b63e4fb17584c8c2216088a2c9937bfe880c2ad565c7a9f51c0fc`
|
||||
(~106 MB). Verified container `/state/health` returns HTTP 200 against the
|
||||
current database when run locally with host networking. Verified Alembic is
|
||||
available in-image and reports current revision `r5m6n7o8p9q0 (head)`.
|
||||
|
||||
Progress 2026-05-03: registry target decision resolved to the self-hosted
|
||||
Gitea registry. A local SSH tunnel to the NodePort can reach Gitea, but Docker
|
||||
login/push still receives HTTP 404 from `/v2/`. Runtime inspection shows the
|
||||
live Gitea `app.ini` has no `[packages]` section, so package registry
|
||||
enablement/routing must be applied before publishing `state-hub:local`.
|
||||
|
||||
Progress 2026-05-15: rebuilt the image from current State Hub sources as
|
||||
`state-hub:local` with digest
|
||||
`sha256:039d29654ccb3754c6ecdbe497c6364bbd8452edcdcb7fa937dd9debf5b734ff`
|
||||
(106004480 bytes). Verified `/state/health` returns
|
||||
`{"status":"ok","db":"connected"}` from a temporary container on host port
|
||||
18000 and confirmed in-image Alembic reports `t7o8p9q0r1s2 (head)`. Build
|
||||
provenance is recorded in `docs/container-image.md`.
|
||||
|
||||
Completed 2026-06-25: adapted the Dockerfile for the current `hub-core`
|
||||
runtime dependency by installing it from the named Docker build context
|
||||
`hub_core_src=/home/worsch/hub-core`. Built current commit `b536741` as
|
||||
`state-hub:local`, `state-hub:b536741`, and
|
||||
`gitea.coulomb.social/coulomb/state-hub:b536741`. Verified in-image imports,
|
||||
Alembic head `e9f0a1b2c3d4`, and `/state/health` from a temporary container on
|
||||
port 18082. Pushed the image to the self-hosted Gitea registry with digest
|
||||
`sha256:3184dfd67f127cf8bd5303d7a210d6dc32e7ab05a5da5d51eab5b9a37dab4d4e`
|
||||
and verified railiance01 can pull it with `sudo crictl pull`.
|
||||
|
||||
---
|
||||
|
||||
### T04 — Define State Hub database and app manifests
|
||||
|
||||
```task
|
||||
id: CUST-WP-0011-T04
|
||||
status: done
|
||||
priority: high
|
||||
completed: "2026-06-25"
|
||||
state_hub_task_id: "a7baf2eb-abd7-4aa3-b2cb-a5370ac09844"
|
||||
```
|
||||
|
||||
Create the cluster-side deployment assets using current Railiance boundaries:
|
||||
|
||||
- `railiance-platform`: `state-hub-db` cnpg cluster and database credentials.
|
||||
- `railiance-apps`: State Hub Deployment, Service, ConfigMap, Secret/External
|
||||
Secret reference, and optional private Ingress.
|
||||
- Health probes use `GET /state/health`.
|
||||
- Environment includes `DATABASE_URL` and any required API settings.
|
||||
|
||||
**Done when:** manifests lint/apply in a non-destructive dry run and ownership
|
||||
boundaries are documented.
|
||||
|
||||
Completed 2026-06-25: added a source-owned Railiance deployment handoff under
|
||||
`deploy/railiance/` with platform manifests for `state-hub-db` CNPG, database
|
||||
credentials, database NetworkPolicies, an app Helm chart, production values, and
|
||||
a `state-hub-env` Secret template. Added Make targets for rendering,
|
||||
client-side dry-run validation, and namespace-aware server-side dry-run
|
||||
validation. Verified:
|
||||
|
||||
- `make railiance-state-hub-render`
|
||||
- `make railiance-state-hub-client-dry-run`
|
||||
- `make railiance-state-hub-server-dry-run`
|
||||
|
||||
The server dry-run validates platform resources and the Namespace manifest
|
||||
against the live cluster API. Because the `state-hub` namespace does not yet
|
||||
exist, it explicitly falls back to client dry-run for namespaced app manifests;
|
||||
Kubernetes cannot persist a dry-run Namespace for subsequent namespaced
|
||||
server-side validation. Ownership boundaries and promotion notes are documented
|
||||
in `deploy/railiance/README.md`.
|
||||
|
||||
---
|
||||
|
||||
### T05 — Deploy empty State Hub and run migrations on railiance01
|
||||
|
||||
```task
|
||||
id: CUST-WP-0011-T05
|
||||
status: done
|
||||
priority: high
|
||||
completed: "2026-06-25"
|
||||
state_hub_task_id: "a307dd46-a8e2-49df-b016-c187759ebcf1"
|
||||
```
|
||||
|
||||
Deploy State Hub against an empty `state-hub-db` cnpg database and run Alembic
|
||||
migrations in the cluster environment.
|
||||
|
||||
Checks:
|
||||
|
||||
- Pod reaches Ready.
|
||||
- `/state/health` returns healthy through the intended private access path.
|
||||
- Alembic reports head.
|
||||
- Logs show no repeated crash/restart loop.
|
||||
|
||||
**Done when:** an empty but structurally valid State Hub runs on railiance01.
|
||||
|
||||
Completed 2026-06-25: deployed an empty State Hub stack to railiance01.
|
||||
Created the `state-hub` namespace, generated live-only database and app runtime
|
||||
Secrets, created the dedicated `state-hub-db` CNPG cluster, and applied database
|
||||
NetworkPolicies. Fixed the State Hub database egress policy to allow the
|
||||
in-cluster Kubernetes API service on TCP 443 as well as 6443, which CNPG
|
||||
needed during initdb. Ran Alembic migrations in a one-shot Kubernetes Job
|
||||
using image `gitea.coulomb.social/coulomb/state-hub:b536741`; migrations
|
||||
completed through `e9f0a1b2c3d4 (head)`. Installed the Helm release
|
||||
`state-hub` into the pre-created namespace with `namespace.create=false`.
|
||||
Verified Deployment rollout, zero pod restarts, service creation, pod logs,
|
||||
in-pod Alembic current revision, and `/state/health` via temporary port-forward
|
||||
returning `{"status":"ok","db":"connected"}`.
|
||||
|
||||
---
|
||||
|
||||
### T06 — Restore WSL2 data copy into cluster and compare
|
||||
|
||||
```task
|
||||
id: CUST-WP-0011-T06
|
||||
status: done
|
||||
priority: high
|
||||
completed: "2026-06-25"
|
||||
state_hub_task_id: "03753b88-824c-4448-97b2-f7315d145060"
|
||||
```
|
||||
|
||||
Restore a fresh WSL2 dump into the cluster database while WSL2 remains the live
|
||||
source of truth.
|
||||
|
||||
Required comparison:
|
||||
|
||||
- Table row counts match.
|
||||
- Representative workstreams, tasks, decisions, progress events, repos, and
|
||||
token events are queryable.
|
||||
- Dashboard and MCP summary calls return expected data through the cluster API.
|
||||
- Any mismatch is investigated before proceeding.
|
||||
|
||||
**Done when:** cluster data is a verified copy of WSL2, but not yet the only
|
||||
writer.
|
||||
|
||||
Completed 2026-06-25: restored a fresh WSL2 State Hub PostgreSQL dump into
|
||||
the cluster `state-hub-db` database while WSL2 remained the live source of truth.
|
||||
Scaled the cluster API to zero during restore, restored the newer dump after a
|
||||
one-row live `progress_events` drift was detected, then scaled the API back
|
||||
to one replica. Verified all public table row counts match between WSL2 and
|
||||
the cluster, including `workplans=569`, `tasks=3673`,
|
||||
`decisions=71`, `progress_events=6232`, `managed_repos=71`,
|
||||
and `token_events=1881`. Representative restored-data checks found this
|
||||
workplan, the T06 task, decisions, progress events, the `state-hub` repo,
|
||||
and token events queryable. Cluster API `/state/health` returned
|
||||
`{"status":"ok","db":"connected"}` through a temporary port-forward,
|
||||
and `/state/summary` returned expected totals. Temporary dump files and
|
||||
port-forwards were removed after verification.
|
||||
|
||||
---
|
||||
|
||||
### T07 — Cut over private access to cluster State Hub
|
||||
|
||||
```task
|
||||
id: CUST-WP-0011-T07
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "ff1de25e-c301-4b86-9420-84dfe72e565e"
|
||||
```
|
||||
|
||||
With human approval, freeze WSL2 writes, take a final dump, restore it to the
|
||||
cluster, compare counts again, and redirect the active private access path to
|
||||
the cluster API.
|
||||
|
||||
Accepted approaches:
|
||||
|
||||
- Keep local MCP config pointed at `http://127.0.0.1:8000` and move that port
|
||||
to an ops-bridge/SSH tunnel.
|
||||
- Or set the MCP server `API_BASE` to the chosen private cluster endpoint.
|
||||
|
||||
**Done when:** `get_state_summary()` and dashboard live data are served by the
|
||||
cluster State Hub, and WSL2 is no longer receiving normal writes.
|
||||
|
||||
Result: completed 2026-07-03 with explicit operator approval ("go forward with
|
||||
1 and 2 and 3"). Sequence: cluster image refreshed to `ea1fd23` (adds the
|
||||
add_progress_event workplan_id alias; schema head `e9f0a1b2c3d4` unchanged);
|
||||
WSL2 uvicorn stopped (freeze); final `pg_dump` from `infra-postgres-1`
|
||||
restored into CNPG `state-hub-db`/`state_hub` with `SET ROLE state_hub`
|
||||
ownership; row counts matched exactly (633 workplans, 3964 tasks, 8192
|
||||
progress events, 14 topics, 1933 token events); private access rewired via
|
||||
ops-bridge `state-hub-primary` forward tunnel so `127.0.0.1:8000` serves the
|
||||
cluster hub. The railiance01 automation chain (`:18000`) verified intact.
|
||||
First primary-served write: progress event `56aab39b`. WSL2 fallback restart:
|
||||
`bridge down state-hub-primary && cd ~/state-hub && make api`.
|
||||
|
||||
---
|
||||
|
||||
### T08 — Stabilise with WSL2 retained as fallback
|
||||
|
||||
```task
|
||||
id: CUST-WP-0011-T08
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "e06a59a0-5310-4c1c-9ba5-7cfaadda62e2"
|
||||
completed: "2026-07-06"
|
||||
```
|
||||
|
||||
Run the cluster State Hub as primary while keeping the WSL2 instance available
|
||||
as a fallback.
|
||||
|
||||
Monitor:
|
||||
|
||||
- State Hub pod restarts.
|
||||
- cnpg cluster health.
|
||||
- Backup job success.
|
||||
- Dashboard and MCP behavior from each operator machine.
|
||||
- Consistency sync behavior for file-backed workplans.
|
||||
|
||||
**Done when:** the agreed stabilisation window passes without data loss or
|
||||
unresolved operational defects.
|
||||
|
||||
Completed 2026-07-06: three days post-cutover (2026-07-03) with no data loss
|
||||
or blocking operational defects. Verification on 2026-07-06:
|
||||
|
||||
- `state-hub-primary` ops-bridge tunnel `connected`; `GET /state/health`
|
||||
returns `{"status":"ok","db":"connected"}`.
|
||||
- Deployment `state-hub` 1/1 Ready (one restart 2d8h ago, currently stable).
|
||||
- CNPG `state-hub-db` reports `Cluster in healthy state`.
|
||||
- Hub totals growing normally (635 workstreams, 3975 tasks); recent writes
|
||||
from 2026-07-04 confirmed.
|
||||
- Manual `POST /consistency/sweep/remote-all` completed with `exit_code: 0`.
|
||||
- WSL2 fallback path documented and retained (`bridge down state-hub-primary &&
|
||||
make api`).
|
||||
|
||||
Follow-ups outside this workplan: scheduled activity-core sweeps paused after
|
||||
cutover (service-inventory gap); CNPG scheduled backups not yet configured.
|
||||
|
||||
---
|
||||
|
||||
### T09 — Document operating model and defer final WSL2 retirement
|
||||
|
||||
```task
|
||||
id: CUST-WP-0011-T09
|
||||
status: done
|
||||
priority: low
|
||||
state_hub_task_id: "d75a2d49-f3b1-4bdd-b9e1-a1c6a9744681"
|
||||
completed: "2026-07-06"
|
||||
```
|
||||
|
||||
Document the new operating model:
|
||||
|
||||
- How agents reach State Hub.
|
||||
- How backups and restores work.
|
||||
- How to roll back to WSL2 if needed.
|
||||
- Which parts remain pragmatic/single-node.
|
||||
- Which long-term requirements moved to `CUST-WP-0038`.
|
||||
|
||||
Do not permanently retire WSL2 in this workplan unless a separate human
|
||||
decision is recorded. Retirement belongs after proven stability or in the
|
||||
future HA workplan.
|
||||
|
||||
**Done when:** runbooks and project instructions match the deployed reality.
|
||||
|
||||
Completed 2026-07-06: added `docs/cluster-operating-model.md` covering cluster
|
||||
access (`state-hub-primary`), remote tunnel mesh, manual backup/restore paths,
|
||||
WSL2 rollback procedure, consistency sync, pragmatic single-node limits, and
|
||||
`CUST-WP-0038` deferrals. Updated `README.md`, `AGENTS.md`,
|
||||
`docs/onboarding.md`, and `infra/README.md` to reference the new runbook.
|
||||
WSL2 retirement explicitly deferred.
|
||||
|
||||
## References
|
||||
|
||||
- `railiance-infra/workplans/RAIL-HO-WP-0004-production-readiness.md`
|
||||
- `RAIL-HO-WP-0004-T09` — Railiance-side State Hub deployment task
|
||||
- `CUST-WP-0038` — future full ThreePhoenix HA State Hub migration
|
||||
- Constitution constraint: production data migration and fallback retirement
|
||||
require explicit human approval
|
||||
@@ -0,0 +1,178 @@
|
||||
---
|
||||
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
|
||||
Reference in New Issue
Block a user