The registrar is reachable and always was: state-hub-primary binds [::1]:8000 while the workstation API listens on 127.0.0.1:8000, so an IPv4 probe hits the local hub and reads as a dead tunnel. But the two databases have diverged by roughly a third -- 987 vs 649 workplans, 5710 vs 4037 tasks -- and the registrar runs pre-rename code. Nine registrar sync requests from three agents have queued since 2026-08-20. Re-checked the chain: RMGR-WP-0004-T08 is done, so RMGR-WP-0005-T03's stated blocker has closed. Its substantive blocker has not -- ADR-007 Migration option 2 permits historical identifier collisions, and UUIDv5 derivation from a non-unique identifier manufactures collisions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
15 KiB
| id | type | title | domain | repo | status | owner | topic_slug | created | updated | parent_project | parent_workplan | related | state_hub_workstream_id | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| STATE-WP-0079 | workplan | State Hub retirement strangler and disposition execution | infotech | state-hub | active | codex | infotech | 2026-08-09 | 2026-08-19 | prj-state-hub-retirement | SHR-WP-0001 |
|
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(seerepo-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
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
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
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
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.
Owner-readiness correction (2026-08-20): T02's slice plan marked all 167
repo-manager items receivable because RMGR-WP-0001/0002/0003 are finished.
That reads readiness off workplan status, which is too coarse. Verified against
repo-manager/src/repo_manager/: those workplans delivered work-record
dual-run, repository observation, and scaffolding. parse/ holds only
workplan.py, and the source tree has no reference to sbom, repo_goal,
contribution, technical_debt, or extension_point.
So only A1 (partial) and A2 are receivable — 86 items, not 165. A3 (registers, 49), A4 (UI, 22) and A5 (topic spine, 8) have no owner-side surface at all, and RMGR-WP-0004/0005 do not cover them.
Critical path is now repo-manager-side, not here: a repo-manager workplan
must build the register, UI, and topic surfaces before 79 Wave A items can move.
Building them in State Hub is inadmissible under policies/retirement-freeze.md
— it would be new permanent ownership in the component being retired.
A2 checked the same way (2026-08-20): the State Hub adapter exposes exactly
three operations — rm_update_task_status, rm_reconcile, rm_scaffold. There
is no adapter for workplan CRUD, intake, decision, dependency, or human-flag
records, and no repo-manager parser for them either. A2 therefore splits into
A2a (task status + reconcile, ~6 items, receivable) and A2b–A2e (~32 items,
not receivable).
A2a is the only slice in the whole plan executable today. Its next step is
operational rather than code: rmgr dual-run-status shows dual-run entirely off
(config_exists: false, writeback/reconcile false, no pilot repos; the 23
metered mutations are RMGR-WP-0002/0003 pilot residue). Moving A2a from off to
dual means creating ~/.repo-manager/dual-run.yaml, enabling RM_WRITEBACK
and RM_RECONCILE for one pilot repo, and watching the mutation meter for
divergence. Reversible by clearing the flags; needs an operator decision because
it changes how live task writes flow.
Net: of 425 inventory items, roughly 6 can move today. The retirement is
gated almost entirely on owner-side capability in repo-manager and hub-core.
A2a executed (2026-08-20) — first live cutover slice. Dual-run is on for the pilot repo:
~/.repo-manager/dual-run.yamlinstalled fromrepo-manager/config/dual-run.pilot.example.yaml—writeback: true,reconcile: true,writeback_push: false,pilot_repos: [repo-manager]- State Hub API reloaded via its own
--reloadwatcher (no service restart, the operator's foregroundmake apiwas left alone) statehub fix-consistency --repo repo-managerreportedRM dual-run reconcile ok: index=/home/worsch/repo-manager/.repo-manager/index.json- mutation meter grew 23 → 24 with
{"kind": "reconcile", "repo_slug": "repo-manager", "workplan_count": 8, "task_count": 53, "record_count": 61}
Divergence check: rmgr's index holds 8 workplans; the hub has
rmgr-wp-0001–0007 registered. The single-item delta is RMGR-WP-0008, whose
C-06 registration was skipped because this instance is not the identifier
registrar (ADR-007 interim). That is a known registrar limitation, not
dual-run divergence — rmgr correctly indexed the file that exists on disk.
Slice state: A2a is dual, not owner — the hub remains authoritative and
rmgr executes checkout mutation alongside it. Promotion to owner needs a full
consistency-sweep cycle at dual with zero divergence, per this plan's adapter
rules. Rollback is rm ~/.repo-manager/dual-run.yaml.
Registrar bottleneck — status check 2026-08-21
The registrar is reachable, and was all along. bridge status shows all
seven tunnels connected and LIVE, including state-hub-primary. It binds
[::1]:8000 — IPv6 localhost — while the workstation API listens on
127.0.0.1:8000 (IPv4). Same port number, different stacks, so a 127.0.0.1
probe silently hits the local hub and looks like the tunnel is down. Anything
checking registrar reachability must use the bracketed IPv6 form.
But the two databases have diverged badly:
workstation (127.0.0.1:8000) |
registrar ([::1]:8000) |
|
|---|---|---|
| workplans | 987 | 649 |
| tasks | 5 710 | 4 037 |
| decisions | 119 | 80 |
The registrar is 338 workplans and 1 673 tasks behind. It also runs older
code — its /state/summary still returns open_workstreams where the
workstation returns open_workplans, so it predates the terminology rename.
This is exactly the ADR-007 defect RMGR-WP-0005 describes, at fleet scale:
two instances over two databases, each minting database-local primary keys into
shared git artifacts.
A queue has formed. Ten unread messages to state-hub, nine of them
registrar sync requests from three agents (agentic-resources, glas-harness,
kaizen-agentic), all dated 2026-08-20 — plus this repo's own RMGR-WP-0008.
Agents fleet-wide are now blocked on identifier registration.
The dependency chain, re-checked:
RMGR-WP-0004-T08(prefix uniqueness enforcement) —done2026-08-18RMGR-WP-0004-T09(assign shared prefixes) — executed 2026-08-17 as an authorized exception, stillwait, with one stated residual: "Not yet reconciled — the workstation is not the registrar"RMGR-WP-0005-T03(deterministic UUIDv5 derivation) — stillwait, but its stated blockerRMGR-WP-0004-T08has closed
T03 is the permanent fix: derived identifiers make writeback idempotent, so any
number of hubs write identical bytes and the queue disappears. Its formal gate is
clear. Its substantive gate is not — ADR-007 § Migration option 2 lets
historical files keep colliding identifiers (CUST-WP, RAILIANCE-WP, and
PRJ-WP in prj-forgejo-org-refactor all still present in the 2026-08-18 scan),
and deriving UUIDv5 from a non-unique identifier manufactures collisions rather
than removing them. That tension needs a ruling before T03 can start.
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
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
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
- Freeze policy documented
- Strangler plan covers all inventory capabilities
- 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