# State Hub retirement — cutover slice plan **Workplan task:** `STATE-WP-0079-T02` **Inventory:** `SHR-INV-0001` (`prj-state-hub-retirement/inventory/`), 425 items, v0.1.1 **Created:** 2026-08-19 Groups every dispositioned inventory item into an ordered cutover slice. Each slice names its adapter flag, the owner-side API that receives the traffic, and its rollback. Slices are the execution unit for `STATE-WP-0079-T04`. ## Method Slices are cut on **capability**, not on route or table, because a capability is the smallest unit with a single owner and a coherent contract. The inventory's `capability` field yields 51 groups; those are bundled into waves by owner, and ordered within a wave by dependency and blast radius. Two rules constrain the ordering: 1. **A slice cannot move before its owner can receive it.** Owner readiness is the owner repo's own workplan status, not an assertion here. 2. **Read-only projections move before write paths** in the same capability, so a failed cutover degrades to stale reads rather than lost writes. ## Owner readiness (verified 2026-08-19) | Owner | Items | Gating workplan | Status | Can receive? | | --- | --- | --- | --- | --- | | `repo-manager` | 167 | RMGR-WP-0001/0002/0003 | `finished` | **Yes** | | `hub-core` | 128 | HUB-WP-0004 runtime + extension contract | `proposed` | No | | `state-hub-until-cutover` | 55 | — (keep/retire in place) | — | n/a | | `activity-core` | 18 | ACTIVITY-WP-0029 hub port alignment | `active` | Partial | | `ops-hub` | 13 | OPS-WP-0003 extension contract alignment | `proposed` | No | | `fin-hub` | 10 | FIN-WP-0003 fabric authority boundary | `proposed` | No | | `archive` | 30 | legacy-meter evidence | see Wave E | Conditional | | `ops-bridge` | 2 | — (`keep`) | — | n/a | | `the-custodian` | 2 | — | — | Yes | Only the `repo-manager` lane is fully unblocked today. **Wave A is the whole of the near-term executable surface**; everything else is correctly waiting on an owner-side contract, not on State Hub. ## Adapter mechanism Wave A reuses the dual-run strangler proven in RMGR-WP-0002/0003 rather than introducing a new mechanism: - Adapter: `api/services/repo_manager_dual_run.py` - Existing flags: `RM_WRITEBACK`, `RM_WRITEBACK_PUSH`, `RM_RECONCILE`, `RM_PILOT_REPOS`, `RM_METER_PATH`, `RM_DUAL_RUN_CONFIG` - Reference: `repo-manager/docs/dual-run.md`, evidence `repo-manager/docs/evidence/wp0002-completion.md` New slices add a **slice-scoped flag** of the form `RM_SLICE_` with three states — `off` (hub only), `dual` (both, hub authoritative, divergence metered), `owner` (owner authoritative, hub proxies). Rollback for every Wave A slice is therefore the same operation: set the slice flag back one state. No slice advances to `owner` until it has run a full consistency-sweep cycle at `dual` with zero divergence. Waves B–D must not invent hub-side adapters ahead of their owner contract. Per the T01 freeze rule, building an adapter for an owner that cannot yet receive establishes new permanent ownership here — the exact thing retirement is removing. --- ## Wave A — repo-manager (167 items, executable now) ### A1 · Repository registry — 27 items `repository-registry` (24: 16 route, 7 mcp_tool, 1 dataset) · `repo-reconciliation` (1) · `repo-doi` (1) · `repo-onboarding` (1) | | | | --- | --- | | Flag | `RM_SLICE_REGISTRY` | | Owner API | `rmgr` repo registry + classification spine | | Rollback | flag → `dual`, then `off`; hub tables retain rows throughout | First because every other repo-manager slice keys off repo identity, and because `repo-onboarding` is already delegated (`STATE-WP-0080-T02`, `rmgr scaffold`), so the slice starts partly cut over. ### A2 · Work records — 59 items `work-records` (20) · `work-records-workplan` (12) · `work-records-intake` (9) · `work-records-decision` (8, move+replace) · `work-records-deps` (2) · `work-records-task` (8, repo-manager portion) | | | | --- | --- | | Flag | `RM_SLICE_WORKRECORDS` | | Owner API | `rmgr` file-backed work-record index + PATCH `/tasks` writeback | | Rollback | flag → `dual`; C-15 writeback already reconciles file↔DB drift | Highest blast radius in the program — this is the surface every agent session touches. Split the flag by record kind if divergence appears; do not cut the whole slice at once. `work-records-task` is owner-split: the route/dataset portion is repo-manager, the job portion is `activity-core` (Wave C1). Cut the repo-manager portion first and leave the job dual-running. ### A3 · Registers — 49 items `sbom-inventory` (10) · `repo-goals` (9) · `upstream-contributions` (9) · `register-technical-debt` (9) · `register-extension-points` (6) · `register-entries` (6) | | | | --- | --- | | Flag | `RM_SLICE_REGISTERS` | | Owner API | `rmgr` register endpoints | | Rollback | flag → `off`; these are append-mostly, so replay is cheap | Low coupling and low write volume — the natural place to prove the `RM_SLICE_*` pattern before A2's blast radius. ### A4 · Work + repo UI — 22 items `work-coordination-ui` (14, `replace`) · `repo-ui` (8) | | | | --- | --- | | Flag | `RM_SLICE_UI` | | Owner API | owner-side dashboard; hub pages redirect | | Rollback | remove redirect; hub pages are unchanged behind it | `replace`, not `move`: the contract changes. Must follow A1–A3, since the pages render what those slices serve. ### A5 · Topic spine — 8 items `topic-spine` (8, `replace`) | | | | --- | --- | | Flag | `RM_SLICE_TOPICSPINE` | | Owner API | `rmgr` topic/classification contract | | Rollback | flag → `off` | Last in Wave A: it is a `replace` under a changed contract, and A1–A4 read it. --- ## Wave B — hub-core (128 items) · gated on HUB-WP-0004 HUB-WP-0004 is `proposed`. **No hub-side adapter work until it is at least `ready`.** Slices are listed to fix scope and order, not to start. | Slice | Capabilities | Items | | --- | --- | --- | | B1 catalogs | `capability-catalog` (18), `cross-domain-registry` (16) | 34 | | B2 messaging + interface log | `agent-messaging` (11), `interface-change-log` (11) | 22 | | B3 telemetry | `token-telemetry` (14), `third-party-software-catalog` (10), `progress-telemetry` (8) | 32 | | B4 projections + policy | `state-projections` (9), `domain-goals` (6), `policy-integration` (2), `write-idempotency` (1), `event-bus` (1), `recently-on-scope` (1) | 20 | | B5 hub UI | `hub-projection-ui` (14, replace), `agent-integration` (2), `dashboard` (1) | 17 | Order B1 → B4 → B2 → B3 → B5: catalogs and projections are read-mostly and carry the contract; messaging and telemetry are write paths; UI last. ## Wave C — activity-core (18 items) · ACTIVITY-WP-0029 `active` | Slice | Capabilities | Items | | --- | --- | --- | | C1 execution | `workplan-execution-queue` (7, replace), `task-flow-engine` (6, replace) | 13 | | C2 jobs | `ops-run-projection` (1), `repo-consistency` (3), `work-records-task` activity-core portion (1) | 5 | Both are `replace` — schedulers move to activity-core under a different contract. C2 must follow A2, since the consistency job writes work records. ## Wave D — remaining owners (25 items) · all gated | Slice | Owner | Capabilities | Items | Gate | | --- | --- | --- | --- | --- | | D1 | `ops-hub` | `service-catalog` (9), `service-catalog-ui` (2) | 11 | OPS-WP-0003 `proposed` | | D2 | `fin-hub` | `fabric-graph` (10) | 10 | FIN-WP-0003 `proposed` | | D3 | `the-custodian` | `kaizen-agents` (2) | 2 | none — can go anytime | D3 is two MCP tools with no dependants; take it opportunistically. ## Wave E — retire (42 items) | Slice | Capability | Items | Gate | | --- | --- | --- | --- | | E1 | `suggestion-backlog` → archive | 17 | caller check; no successor runtime | | E2 | `work-records-workplan-legacy` → archive | 13 | legacy-meter — see caveat below | | E3 | `dashboard-meta` | 1 | none | | E4 | `legacy-meter` itself | 9 | **last** — it is the instrument for E2 | E4 retires only after every other legacy surface is closed; retiring the meter first destroys the evidence for the retirements it gates. ### Caveat on E2's evidence The 2026-08-19 review (`docs/evidence/legacy-meter-weekly-review-20260819.json`) reports 19 retirement candidates, all `replacement_verified: true` with zero calls in window. But the window is `06:00Z → 14:00Z` — **8 hours, not the 7 days `cadence: weekly` implies**. Several candidates have very large all-time counts (`GET /workstreams/{id}` 511 406, `GET /workstreams/` 144 086, `POST /workstreams/{id}/dependencies/` 255 865). An 8-hour quiet window is not sufficient evidence to retire an interface at that call volume. Either the window computation is a defect or the cadence label is wrong; either way **E2 should not proceed on the current evidence**. Resolving this is a prerequisite, and it is worth checking before Wave A rather than after, since the same meter gates T05. ## Keep — 43 items `schema-history` (43 migrations, `keep` until cutover) plus `ops-automation`, `connectivity` (ops-bridge). Not slices: they persist until the T06 freeze window and are dispositioned by the final dump and archive. ## Coverage Computed against `state-hub-disposition.yaml`, not hand-counted. Every item is assigned by `capability`; `work-records-task` splits on `owner`. | Wave | Slices | Items | | --- | --- | --- | | A repo-manager | A1 27 · A2 59 · A3 49 · A4 22 · A5 8 | 165 | | B hub-core | B1 34 · B2 22 · B3 32 · B4 20 · B5 17 | 125 | | C activity-core | C1 13 · C2 5 | 18 | | D ops-hub / fin-hub / the-custodian | D1 11 · D2 10 · D3 2 | 23 | | E retire | E1 17 · E2 13 · E3 1 · E4 9 | 40 | | Keep | `schema-history` 43, `ops-automation` 1, `connectivity` 1 | 45 | | Ops dependencies | see below | 9 | | **Total** | | **425** | Wave A totals 165 rather than the owner table's 167, and Wave B 125 rather than 128: the difference is `ops_dependency` items, which carry no `capability` and so attach to no capability slice. ### Ops dependencies (9 items, no capability) | Owner | Disposition | Items | Handling | | --- | --- | --- | --- | | `hub-core` | move / replace / keep | 3 | ride with Wave B; confirm at B4 | | `ops-hub` | replace | 2 | ride with D1 | | `state-hub-until-cutover` | retire | 2 | T06 freeze window | | `activity-core` | keep | 1 | keep to cutover | | `ops-bridge` | keep | 1 | keep — external dependency, survives retirement | These are infrastructure couplings (DB, scheduler, tunnel), not API surface. They cannot be strangled behind a flag and are dispositioned at the freeze window instead. Worth an explicit owner confirmation during T04 so none is discovered live at cutover. ## Next actions 1. Resolve the legacy-meter window discrepancy above — it gates E2 and T05. 2. Implement `RM_SLICE_REGISTERS` (A3) first as the pattern proof. 3. Do not open Wave B until HUB-WP-0004 reaches `ready`.