History is archived fleet-side, so the read surface has no remaining job. - api/routers/suggestions.py: whole prefix 410s with a pointer to intake and to the archive; the router drops from 176 lines to a stub - mcp_server: the 6 suggestion tools removed outright rather than stubbed -- a retired tool that still appears in the tool list costs every agent session context on every call, which is the opposite of retiring it - write_idempotency: 5 /suggestions rules dropped - dashboard: suggestions.md deleted, nav entry removed, reference.md and wsjf-triage.md updated; docs/suggestions.md rewritten as archive pointer - tests: two tests pinned the old read-live behaviour and now pin the retirement contract instead Tables stay: they are retire/archive in SHR-INV-0001 and are captured by the final dump at T06. Untouched, and worth knowing during cutover: ui-feedback.md / todo.md 'suggestions' are Shift+click dashboard feedback backed by technical_debt, a different entity that shares the word. E3 (dashboard-meta) is that page; its owner is state-hub-until-cutover so it retires at the T06 window, not now. Full suite 612 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
225 lines
9.3 KiB
Markdown
225 lines
9.3 KiB
Markdown
---
|
||
id: STATE-WP-0079
|
||
type: workplan
|
||
title: "State Hub retirement strangler and disposition execution"
|
||
domain: infotech
|
||
repo: state-hub
|
||
status: active
|
||
owner: codex
|
||
topic_slug: infotech
|
||
created: "2026-08-09"
|
||
updated: "2026-08-19"
|
||
parent_project: prj-state-hub-retirement
|
||
parent_workplan: SHR-WP-0001
|
||
related:
|
||
- STATE-WP-0074
|
||
- SHR-INV-0001
|
||
- RMGR-WP-0001
|
||
- HUB-WP-0004
|
||
state_hub_workstream_id: "749beac6-3c62-4284-aab5-9ed7fce900c2"
|
||
---
|
||
|
||
# State Hub retirement strangler and disposition execution
|
||
|
||
## Goal
|
||
|
||
Execute the **keep/move/replace/retire** dispositions from
|
||
`prj-state-hub-retirement/inventory` via strangler adapters and owner handoffs,
|
||
without adding new permanent authorities here. End state: freeze window with no
|
||
normal traffic, then archive.
|
||
|
||
## Dual-run handoff (RMGR-WP-0002)
|
||
|
||
Repo Manager Stage B dual-run is available:
|
||
|
||
- Flags: `RM_WRITEBACK`, `RM_RECONCILE`, `RM_PILOT_REPOS` (see `repo-manager/docs/dual-run.md`)
|
||
- SH adapter: `api/services/repo_manager_dual_run.py` (PATCH `/tasks` + C-15 writeback + reconcile proxy)
|
||
- Evidence: `repo-manager/docs/evidence/wp0002-completion.md`
|
||
|
||
Expand pilot list here as cutover progresses; do not add new checkout mutators in State Hub.
|
||
|
||
## Freeze policy for new scope
|
||
|
||
```task
|
||
id: STATE-WP-0079-T01
|
||
status: done
|
||
priority: high
|
||
state_hub_task_id: "375c0b66-98b0-4d5b-98dd-4ad2883a4051"
|
||
```
|
||
|
||
Document and enforce: changes during retirement must preserve compatibility,
|
||
enable extraction, reduce scope, or fix operational risk — not establish new
|
||
permanent ownership (INTENT retirement status).
|
||
|
||
**Result (2026-08-20):** `policies/retirement-freeze.md`, following the existing
|
||
`policies/` convention. States the four admissible reasons, the inadmissible
|
||
case (new permanent ownership here), and the operative test — *where does this
|
||
live after cutover?* — since most inadmissible changes are otherwise good
|
||
changes. Uses `STATE-WP-0080`'s retarget as the worked example.
|
||
|
||
**Enforced by review, not by new automation.** A freeze-checker built inside
|
||
State Hub would be new permanent automation in the component being retired, so
|
||
the policy would violate itself. Gates attach to points where changes are
|
||
already reviewed: new workplan goal statements, non-trivial task starts, and
|
||
slice execution. New tables, routes, or MCP tools carry a presumption against
|
||
and are the one hard stop.
|
||
|
||
## Capability strangler plan
|
||
|
||
```task
|
||
id: STATE-WP-0079-T02
|
||
status: done
|
||
priority: high
|
||
state_hub_task_id: "595c3936-20b6-453f-92b5-84f86798054a"
|
||
```
|
||
|
||
Group SHR-INV-0001 items into cutover slices (repo/work → repo-manager;
|
||
messages/progress/projections → hub-core; catalogs → owners; retire aliases).
|
||
Per slice: adapter flag, owner API, rollback.
|
||
|
||
**Result (2026-08-19):** `docs/retirement-cutover-slice-plan.md`. All 425
|
||
inventory items assigned to 19 slices in five waves plus Keep, computed against
|
||
`state-hub-disposition.yaml` rather than hand-counted. Each slice carries an
|
||
`RM_SLICE_*` flag (off/dual/owner), owner API, and rollback; Wave A reuses the
|
||
RMGR-WP-0002/0003 dual-run adapter rather than adding a mechanism.
|
||
|
||
Owner readiness verified: only **Wave A (repo-manager, 165 items)** is
|
||
executable — RMGR-WP-0001/0002/0003 are `finished`. Wave B is gated on
|
||
HUB-WP-0004 (`proposed`), D1 on OPS-WP-0003 (`proposed`), D2 on FIN-WP-0003
|
||
(`proposed`). Recommended first cut is A3 (registers), lowest blast radius.
|
||
|
||
Two findings for T04/T05: the 9 `ops_dependency` items carry no capability and
|
||
cannot be strangled behind a flag — they resolve at the T06 freeze window; and
|
||
the legacy-meter review window is 8 hours despite `cadence: weekly`, which is
|
||
not sufficient evidence to retire high-volume `/workstreams/` interfaces (E2).
|
||
|
||
## Register CLI and GOAL.md support
|
||
|
||
```task
|
||
id: STATE-WP-0079-T03
|
||
status: done
|
||
priority: medium
|
||
state_hub_task_id: "55716f12-fb12-4a1e-a770-076c490db111"
|
||
```
|
||
|
||
Adapt `statehub register` / scaffolding so `prj-` repos with `GOAL.md` and
|
||
`repo_flavor: project` are valid without inventing durable-product `INTENT.md`
|
||
(residual from SHR-WP-0001-T01).
|
||
|
||
**Result (2026-08-19):** Implemented by `STATE-WP-0080-T01/T02`. Project
|
||
flavor is detected from classification, `GOAL.md`, or the `prj-` slug; a repo
|
||
with an existing `GOAL.md` is registered without durable scaffolding, while a
|
||
fresh project delegates scaffolding to `rmgr scaffold`. The focused State Hub
|
||
registration suite passes (`20 passed`).
|
||
|
||
## Execute move/replace slices
|
||
|
||
```task
|
||
id: STATE-WP-0079-T04
|
||
status: todo
|
||
priority: high
|
||
state_hub_task_id: "db5291f2-3801-40fa-abf2-ceb0e77687c9"
|
||
```
|
||
|
||
Implement strangler handoffs coordinated with RMGR-WP-0001 and HUB-WP-0004.
|
||
Compatibility tests for each dispositioned route family.
|
||
|
||
**Inherited (2026-08-19) from `STATE-WP-0080-T04`:** this slice now also owns
|
||
deletion of the templating path in `statehub_register.py` — `INTENT.md`
|
||
generation, the `AGENTS.md` / `CLAUDE.md` / `.custodian-brief.md` templates,
|
||
`project_rules/`, and bootstrap-workplan generation. The code is dead-but-present:
|
||
project repos are already routed to `rmgr scaffold`. Gate the deletion on
|
||
`RMGR-WP-0004-T06` proving byte-identical durable output and idempotent
|
||
re-scaffolding.
|
||
|
||
## Retire legacy surfaces
|
||
|
||
```task
|
||
id: STATE-WP-0079-T05
|
||
status: todo
|
||
priority: medium
|
||
state_hub_task_id: "02e508ed-3cde-4487-907e-d324a8a877d6"
|
||
```
|
||
|
||
Complete retirement of suggestions, workstream aliases, and other `retire`
|
||
inventory items once meters/callers allow. Keep historical rows archive-readable.
|
||
|
||
**Blocker identified (2026-08-20):** the legacy-meter evidence this task gates
|
||
on was being captured over an 8-hour band, not a week —
|
||
`capture_legacy_meter_evidence.py` fell back to `hours=8` whenever `--days` was
|
||
omitted, so 39 of 40 captures sampled only 06:00Z–14:00Z. Interfaces called
|
||
outside that band read as unused. Three candidates on the 2026-08-19 capture had
|
||
live callers, including `GET /tasks/?workstream_id` (seen 2026-08-18).
|
||
|
||
Script default corrected to `days=7`; corrected capture
|
||
`docs/evidence/legacy-meter-weekly-review-20260820.json`.
|
||
|
||
**Candidate rule tightened (2026-08-20):** `_retirement_state` now requires a
|
||
quiet period scaled to all-time call volume (`RETIREMENT_QUIET_LADDER`: 7d under
|
||
100 calls, 30d under 10 000, 60d above). On the 2026-08-20 capture this makes 15
|
||
of 19 legacy interfaces retirable — all `workstream` MCP tools, the `state://`
|
||
resource, and the low-volume REST aliases — while holding the three six-figure
|
||
read paths (`GET /workstreams/`, `GET /workstreams/{id}`,
|
||
`GET /workstreams/{id}/dependencies/`) and `GET /tasks/?workstream_id`, which
|
||
still has live traffic. Covered by 7 new tests in `tests/test_legacy_meter.py`.
|
||
|
||
**Slice E2 executed (2026-08-20):** the 15 evidenced interfaces are retired in
|
||
the legacy meter — all four `workstream` MCP tools, the `state://` resource, and
|
||
the low-volume REST aliases including `PATCH /workstreams/{id}` (571 calls),
|
||
`POST /workstreams/` (824) and `POST /workstreams/{id}/dependencies/` (4 971).
|
||
Meter now reads 16 retired / 4 legacy, 0 outstanding candidates. Evidence:
|
||
`docs/evidence/legacy-meter-weekly-review-20260820.json`.
|
||
|
||
Retirements are journalled fleet-side at
|
||
`the-custodian/docs/retired-legacy-interfaces.md` — deliberately outside this
|
||
repo, since State Hub is being archived and the record must outlive it.
|
||
|
||
**Remaining work for this task:** the 4 held interfaces
|
||
(`GET /workstreams/`, `GET /workstreams/{id}`,
|
||
`GET /workstreams/{id}/dependencies/`, `GET /tasks/?workstream_id`) clear as
|
||
their quiet counters run; earliest is 18 more silent days. Re-capture before
|
||
deciding — the ladder evaluates at review time. **Slice E1 part-executed (2026-08-20):** `suggestion-backlog` history archived
|
||
to `the-custodian/docs/archived-suggestion-backlog.md` — 10 suggestions, 10
|
||
notes, 5 bumps, all `declined` in the 2026-07-21 intake migration, none
|
||
promoted. Mutations already 410 since `CUST-WP-0061-T06`; reads were open only
|
||
to keep that history reachable, which the archive now replaces.
|
||
|
||
**E1 removal executed the same day:** the 8 read routes 410 with a pointer to
|
||
intake and the archive, the 6 MCP tools are removed outright, the 5
|
||
`/suggestions` write-idempotency rules are dropped, and the dashboard page is
|
||
deleted with nav, `reference.md` and `wsjf-triage.md` updated;
|
||
`dashboard/src/docs/suggestions.md` is now the archive pointer. Tables remain
|
||
for the T06 dump. Two tests that pinned the old read-live behaviour were
|
||
rewritten to pin the retirement contract.
|
||
|
||
Naming trap for cutover: `ui-feedback.md` / `todo.md` "suggestions" are
|
||
Shift+click dashboard feedback backed by `technical_debt`, **not** this
|
||
capability. Untouched.
|
||
|
||
**E3 (`dashboard-meta`) is `/ui-feedback`** — that same feedback surface. Owner
|
||
is `state-hub-until-cutover` and it is still in use, so it retires at the T06
|
||
freeze window, not now.
|
||
|
||
Then `dashboard-meta` (E3, 1); `legacy-meter` itself (E4, 9) retires last,
|
||
being the instrument.
|
||
|
||
## Stabilization window and archive prep
|
||
|
||
```task
|
||
id: STATE-WP-0079-T06
|
||
status: todo
|
||
priority: high
|
||
state_hub_task_id: "d52c95c6-af3f-4b2c-804e-e07763c9a8ab"
|
||
```
|
||
|
||
With T06 gates: zero normal read/write window, final dump, backup/restore
|
||
evidence, stop service, archive repo read-only. Residuals → live owners.
|
||
|
||
## Acceptance
|
||
|
||
- [x] Freeze policy documented
|
||
- [x] Strangler plan covers all inventory capabilities
|
||
- [x] prj-/GOAL.md register path works
|
||
- [ ] Move/replace slices evidenced with tests/counts
|
||
- [ ] Retire list closed or residual-owned
|
||
- [ ] Stabilization + archive prep ready for project retirement
|