diff --git a/README.md b/README.md index 9b96378..8b3055b 100644 --- a/README.md +++ b/README.md @@ -256,7 +256,7 @@ use `/state/health`, not `/state/summary`. |--------|-----------| | `/topics` | CRUD (soft-delete: `archived`) | | `/workplans` | Preferred CRUD surface for repo-backed workplans (soft-delete: `archived`) | -| `/workstreams` | Legacy compatibility CRUD surface; usage is recorded by legacy-meter | +| `/workstreams` | Retired REST routes (410); use `/workplans`. Rejected calls remain metered | | `/tasks` | CRUD (soft-delete: `cancel`); `PATCH` updates status | | `/decisions` | CRUD (soft-delete: `superseded`); auto-escalation | | `/progress` | `GET` list + `POST` append — no DELETE | diff --git a/dashboard/src/docs/goals.md b/dashboard/src/docs/goals.md index 39a4a20..5ee237c 100644 --- a/dashboard/src/docs/goals.md +++ b/dashboard/src/docs/goals.md @@ -127,7 +127,7 @@ The Goals page groups everything by domain: Workplans carry an optional `repo_goal_id` field. Setting it traces *why* a workplan exists — which specific repo goal it contributes to. This connection is currently recorded in the DB but is not yet visualised in the Workplans page. -To set the link when creating a workplan through the preferred API, pass `repo_goal_id`. To update an existing one, use `PATCH /workplans/{id}/` with `{"repo_goal_id": ""}`. Legacy `create_workstream` and `/workstreams/{id}/` callers remain compatibility-supported while they are metered. +To set the link when creating a workplan through the preferred API, pass `repo_goal_id`. To update an existing one, use `PATCH /workplans/{id}/` with `{"repo_goal_id": ""}`. The legacy `create_workstream` tool is removed and `/workstreams/{id}` returns 410; use `create_workplan` and `/workplans/{id}`. --- diff --git a/docs/evidence/STATE-WP-0079-caller-and-receiver-review-20260905.md b/docs/evidence/STATE-WP-0079-caller-and-receiver-review-20260905.md new file mode 100644 index 0000000..eaf6bb9 --- /dev/null +++ b/docs/evidence/STATE-WP-0079-caller-and-receiver-review-20260905.md @@ -0,0 +1,92 @@ +# STATE-WP-0079 caller and receiver review — 2026-09-05 + +## Actions and scope + +User authorized the proposed SBOM meter retirements, caller tracing/migration, +and refreshed cutover checklist. No application image, production writer, +network policy, runtime deployment, or compatibility handler was changed. + +A fresh seven-day primary review re-confirmed both candidates before mutation. +Direct Nexus licence-report and snapshot reads returned HTTP 200 from the State +Hub pod. Both meter records were patched with STATE-WP-0079-T05 provenance. +The saved before/after receipt is +`STATE-WP-0079-sbom-retirement-20260905.json`: eight retired, seven legacy, +zero remaining candidates. Handler deletion is not claimed by meter retirement. + +## Caller trace + +Searched non-hidden source trees under `/home/worsch` for `workstream_id`, +`/workstreams`, and the two SBOM endpoints, covering Python, shell, JS/TS and +YAML while excluding tests, dependency trees, docs, archives and fixtures. +517 matching lines were triaged by receiver and operation. A second Markdown +instruction scan excluded workplans, generated indexes, docs, archives and +session evidence. Matching strings alone were not treated as runtime callers. + +- Repo Manager's licence-report client uses explicit `SBOM_NEXUS_URL`; it is a + direct Nexus client, not evidence of a State Hub caller. +- SBOM Nexus importer intentionally reads source and target snapshots/licences + for migration comparison. Its matching paths do not establish ongoing use. +- The bundled State Hub skill advertised removed `create_workstream` and + `POST /workstreams/`; README and dashboard goals documentation also described + retired routes as supported. Corrected these to current workplan contracts, + file-first sync and component attribution. +- No current hard-coded State Hub workstream route caller was found in the + scanned runtime sources. This does not cover hidden installations, remote + checkouts, dynamically built paths or past one-off commands. + +Targeted local agent-session review extracted only tool-call timestamp, working +repository and legacy path. Full session contents were not copied into evidence. + +| Time (UTC) | Caller context | Observed operation | Confidence / correction | +| --- | --- | --- | --- | +| 2026-09-04 17:21:25.339 | Claude agent, fluid-telegram | Task query with legacy `workstream_id`, targeting `f2373858-c932-5a4d-81b6-db9a3595135a` | Actual command in session history; within four seconds of meter last_seen 17:21:29.173850. Strong correlation, not request-ID proof. AGENTS.md now prescribes canonical query and `X-StateHub-Component: fluid-telegram` | +| 2026-08-29 12:57:27.083 | Claude agent, ops-warden | Task query with legacy `workstream_id`, targeting `ae3ff76f-883d-5e2f-b6aa-144d61e8fdef` | Actual command in session history. AGENTS.md now prescribes canonical query and `X-StateHub-Component: ops-warden` | + +Caller guidance commits: fluid-telegram `f7af151`, ops-warden `96dbb22`. +Both changes are restricted to the repository-owned AGENTS.md extension. + +Replayed only the **canonical** task queries with those component headers: +HTTP 200, 8 and 5 tasks respectively. No legacy query was replayed, so the +retirement clocks were not reset by verification. Agent instructions reduce +future misuse; they do not prove that future sessions will follow them. + +Remaining attribution: the meter's 11 task-filter calls are not all individually +explained, seven workstream-detail calls remain unidentified, and the one SBOM +index call remains unidentified. These are STATE-WP-0079-T07 live work, not +claimed completed migrations. + +## Log retention limit + +Requested seven days of `deployment/state-hub` pod logs. Available logs start +2026-09-04T20:10:36Z, after the relevant last-seen timestamps. No matching legacy +requests appear in that retained interval. Kubernetes has no previous container +log for this pod. Hence these logs cannot retrospectively identify the calls; +absence in this interval is not evidence of seven quiet days. + +## Receiving runtime probe + +The live State Hub deployment image was `main-cdff3b7`; its configmap directs +both SBOM read and write compatibility paths to the cluster Nexus service. + +`core-hub-api` and `core-hub-api-candidate` deployments both reported 1/1 ready. +From the State Hub pod, requests to both Core Hub service addresses on port 8010 +were connection-refused. Cause was not established and no connectivity change +was made. From candidate pod loopback, `/openapi.json` returned a **Hub Core +Runtime** schema with 31 paths. It exposes `/api/v2/interaction-events`, but no +`/messages`, `/progress`, `/token-events` or `/interface-changes` family. + +Shared Python routers exist for messages/progress, but importing them into +State Hub does not deploy them on the owner runtime or migrate their rows. +HUB-WP-0005's successful Core Hub `/api/v2` absorption must not be used as proof +of the State Hub family cutover. STATE-WP-0079-T08 owns the bounded follow-up: +identify/expose one deployed message-read contract, historical data projection, +transport and parity/rollback evidence before changing any reader/writer. + +## Validation + +- Live primary identity and fresh seven-day eligibility verified before PATCH. +- Direct receiving SBOM reads: 200; canonical caller task reads: 200. +- State Hub skill passes the skill-creator frontmatter validator; this also + corrected an existing unquoted colon that made its description invalid YAML. +- Documentation and caller-instruction edits pass `git diff --check`. +- No executable application code changed; no database test suite was needed. diff --git a/docs/evidence/STATE-WP-0079-sbom-retirement-20260905.json b/docs/evidence/STATE-WP-0079-sbom-retirement-20260905.json new file mode 100644 index 0000000..3dd3e0a --- /dev/null +++ b/docs/evidence/STATE-WP-0079-sbom-retirement-20260905.json @@ -0,0 +1,1572 @@ +{ + "captured_at": "2026-09-05T08:04:47.855699+00:00", + "workplan": "STATE-WP-0079", + "api_base": "http://127.0.0.1:8000", + "health": { + "status": "ok", + "db": "connected", + "instance_role": "primary", + "instance_label": "railiance01", + "schema": { + "status": "ok", + "applied": "b5e6f7a8c9d0", + "expected": "b5e6f7a8c9d0" + } + }, + "before": { + "generated_at": "2026-09-05T08:04:48.276126Z", + "window_start": "2026-08-29T08:04:48.185252Z", + "window_end": "2026-09-05T08:04:48.185252Z", + "cadence": "weekly", + "activity_core_handoff": { + "activity_id": "statehub-legacy-interface-review", + "cadence": "weekly", + "source_endpoint": "/legacy-meter/weekly-review", + "state_owner": "state-hub", + "scheduler_owner": "activity-core" + }, + "interfaces": [ + { + "interface": { + "id": "8051fb40-66c9-4dc4-a413-13e8573e04a3", + "interface_key": "event_subject:org.statehub.workstream.completed", + "interface_kind": "event_subject", + "legacy_since": "2026-06-04T06:09:42.198193Z", + "replacement_ref": "org.statehub.workplan.completed", + "owner_component": "state-hub.events", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "Retired by STATE-WP-0069 closeout", + "retired_at": "2026-08-31T08:55:06.871333Z", + "created_at": "2026-06-04T06:09:42.198193Z", + "updated_at": "2026-08-31T08:55:06.756087Z" + }, + "all_time": { + "calls": 247, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 247 + }, + "users": { + "unknown": 247 + }, + "components": { + "state-hub.events": 247 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-07-13T13:22:00.748140Z", + "retirement_candidate": false, + "retirement_reason": "already retired" + }, + { + "interface": { + "id": "cd9342fd-27e7-4a46-a1be-d41499e106a3", + "interface_key": "rest_api:DELETE /workstreams/{workstream_id}", + "interface_kind": "rest_api", + "legacy_since": "2026-06-06T17:28:13.052813Z", + "replacement_ref": "/workplans/{workplan_id}", + "owner_component": "state-hub.api", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "Retired by STATE-WP-0069 closeout", + "retired_at": "2026-08-31T08:55:08.200322Z", + "created_at": "2026-06-06T17:28:13.052813Z", + "updated_at": "2026-08-31T08:55:08.089714Z" + }, + "all_time": { + "calls": 1, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 1 + }, + "users": { + "unknown": 1 + }, + "components": { + "unknown": 1 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-06-06T17:28:13.047529Z", + "retirement_candidate": false, + "retirement_reason": "already retired" + }, + { + "interface": { + "id": "d0294dce-7913-4268-94c3-1b2502aefb0f", + "interface_key": "rest_api:GET /sbom/", + "interface_kind": "rest_api", + "legacy_since": "2026-08-22T17:06:38.703608Z", + "replacement_ref": "sbom-nexus:/sbom/", + "owner_component": "state-hub.sbom-compat", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-08-22T17:06:38.703608Z", + "updated_at": "2026-08-22T17:23:43.730661Z" + }, + "all_time": { + "calls": 6, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 6 + }, + "users": { + "unknown": 6 + }, + "components": { + "unknown": 6 + } + }, + "window": { + "calls": 1, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 1 + }, + "users": { + "unknown": 1 + }, + "components": { + "unknown": 1 + } + }, + "last_seen_at": "2026-08-31T08:18:41.571768Z", + "retirement_candidate": false, + "retirement_reason": "1 call(s) in review window" + }, + { + "interface": { + "id": "2f77ac12-8c97-4974-a2f9-2996927982d1", + "interface_key": "rest_api:GET /sbom/report/licences/", + "interface_kind": "rest_api", + "legacy_since": "2026-08-22T17:06:41.978426Z", + "replacement_ref": "sbom-nexus:/sbom/", + "owner_component": "state-hub.sbom-compat", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-08-22T17:06:41.978426Z", + "updated_at": "2026-08-22T17:23:44.794796Z" + }, + "all_time": { + "calls": 5, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 5 + }, + "users": { + "unknown": 5 + }, + "components": { + "unknown": 5 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-28T18:46:10.642164Z", + "retirement_candidate": true, + "retirement_reason": "no usage in review window; quiet 7d (>= 7d required for 5 all-time call(s))" + }, + { + "interface": { + "id": "b627edf5-3302-4366-ba2e-547d32f634b5", + "interface_key": "rest_api:GET /sbom/snapshots/", + "interface_kind": "rest_api", + "legacy_since": "2026-08-22T17:06:37.057774Z", + "replacement_ref": "sbom-nexus:/sbom/", + "owner_component": "state-hub.sbom-compat", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-08-22T17:06:37.057774Z", + "updated_at": "2026-08-22T17:22:15.080213Z" + }, + "all_time": { + "calls": 7, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 7 + }, + "users": { + "unknown": 7 + }, + "components": { + "unknown": 7 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-28T18:46:10.694185Z", + "retirement_candidate": true, + "retirement_reason": "no usage in review window; quiet 7d (>= 7d required for 7 all-time call(s))" + }, + { + "interface": { + "id": "d03ee274-cd6f-4cd5-8f22-e4d298416299", + "interface_key": "rest_api:GET /sbom/snapshots/{snapshot_id}", + "interface_kind": "rest_api", + "legacy_since": "2026-08-22T17:23:45.969876Z", + "replacement_ref": "sbom-nexus:/sbom/", + "owner_component": "state-hub.sbom-compat", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "Retired by STATE-WP-0069 closeout", + "retired_at": "2026-08-31T08:55:08.470106Z", + "created_at": "2026-08-22T17:23:45.969876Z", + "updated_at": "2026-08-31T08:55:08.456139Z" + }, + "all_time": { + "calls": 1, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 1 + }, + "users": { + "unknown": 1 + }, + "components": { + "unknown": 1 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-22T17:23:45.967492Z", + "retirement_candidate": false, + "retirement_reason": "already retired" + }, + { + "interface": { + "id": "a6cb291e-8312-42f8-8a66-fde369482881", + "interface_key": "rest_api:GET /sbom/{repo_slug}", + "interface_kind": "rest_api", + "legacy_since": "2026-08-22T17:23:45.089542Z", + "replacement_ref": "sbom-nexus:/sbom/", + "owner_component": "state-hub.sbom-compat", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "Retired by STATE-WP-0069 closeout", + "retired_at": "2026-08-31T08:55:08.754914Z", + "created_at": "2026-08-22T17:23:45.089542Z", + "updated_at": "2026-08-31T08:55:08.750826Z" + }, + "all_time": { + "calls": 1, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 1 + }, + "users": { + "unknown": 1 + }, + "components": { + "unknown": 1 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-22T17:23:45.670511Z", + "retirement_candidate": false, + "retirement_reason": "already retired" + }, + { + "interface": { + "id": "cad13f19-ddb1-49b9-86d0-8789b7599f3a", + "interface_key": "rest_api:GET /tasks/?workstream_id", + "interface_kind": "rest_api", + "legacy_since": "2026-08-29T10:01:31.546213Z", + "replacement_ref": "/tasks/?workplan_id=", + "owner_component": "state-hub.api", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-08-29T10:01:31.546213Z", + "updated_at": "2026-08-29T10:01:31.546213Z" + }, + "all_time": { + "calls": 11, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 11 + }, + "users": { + "unknown": 11 + }, + "components": { + "unknown": 11 + } + }, + "window": { + "calls": 11, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 11 + }, + "users": { + "unknown": 11 + }, + "components": { + "unknown": 11 + } + }, + "last_seen_at": "2026-09-04T17:21:29.173850Z", + "retirement_candidate": false, + "retirement_reason": "11 call(s) in review window" + }, + { + "interface": { + "id": "3e4a3d0b-08fa-45c3-91e2-4f514a914b97", + "interface_key": "rest_api:GET /workstreams/", + "interface_kind": "rest_api", + "legacy_since": "2026-06-04T00:26:14.533764Z", + "replacement_ref": "/workplans/", + "owner_component": "state-hub.api", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-06-04T00:26:14.533764Z", + "updated_at": "2026-06-04T00:26:14.533764Z" + }, + "all_time": { + "calls": 830269, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 830269 + }, + "users": { + "unknown": 830269 + }, + "components": { + "unknown": 830269 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-21T13:50:37.994459Z", + "retirement_candidate": false, + "retirement_reason": "quiet 14d of 60d required for 830269 all-time call(s)" + }, + { + "interface": { + "id": "e4c140ec-7479-46f9-b185-4b533f19d639", + "interface_key": "rest_api:GET /workstreams/workplan-index", + "interface_kind": "rest_api", + "legacy_since": "2026-06-04T05:20:58.321869Z", + "replacement_ref": "/workplans/index", + "owner_component": "state-hub.api", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "Retired by STATE-WP-0069 closeout", + "retired_at": "2026-08-31T08:55:08.939572Z", + "created_at": "2026-06-04T05:20:58.321869Z", + "updated_at": "2026-08-31T08:55:08.933482Z" + }, + "all_time": { + "calls": 40, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 40 + }, + "users": { + "unknown": 40 + }, + "components": { + "unknown": 40 + } + }, + "window": { + "calls": 1, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 1 + }, + "users": { + "unknown": 1 + }, + "components": { + "unknown": 1 + } + }, + "last_seen_at": "2026-09-01T18:42:55.879795Z", + "retirement_candidate": false, + "retirement_reason": "already retired" + }, + { + "interface": { + "id": "3a00b19e-e7db-4403-98aa-a77c9ab61ecc", + "interface_key": "rest_api:GET /workstreams/{workstream_id}", + "interface_kind": "rest_api", + "legacy_since": "2026-06-04T00:25:59.014966Z", + "replacement_ref": "/workplans/{workplan_id}", + "owner_component": "state-hub.api", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-06-04T00:25:59.014966Z", + "updated_at": "2026-06-04T00:25:59.014966Z" + }, + "all_time": { + "calls": 3411477, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 3411477 + }, + "users": { + "unknown": 3411477 + }, + "components": { + "unknown": 3411477 + } + }, + "window": { + "calls": 7, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 7 + }, + "users": { + "unknown": 7 + }, + "components": { + "unknown": 7 + } + }, + "last_seen_at": "2026-09-03T22:02:35.348363Z", + "retirement_candidate": false, + "retirement_reason": "7 call(s) in review window" + }, + { + "interface": { + "id": "69e1a255-a8b9-4101-8a68-b1bed23abfda", + "interface_key": "rest_api:GET /workstreams/{workstream_id}/dependencies/", + "interface_kind": "rest_api", + "legacy_since": "2026-06-04T00:25:59.291135Z", + "replacement_ref": "/workplans/{workplan_id}/dependencies/", + "owner_component": "state-hub.api", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-06-04T00:25:59.291135Z", + "updated_at": "2026-06-04T00:25:59.291135Z" + }, + "all_time": { + "calls": 1643952, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 1643952 + }, + "users": { + "unknown": 1643952 + }, + "components": { + "unknown": 1643952 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-21T13:50:30.082480Z", + "retirement_candidate": false, + "retirement_reason": "quiet 14d of 60d required for 1643952 all-time call(s)" + }, + { + "interface": { + "id": "b1aae931-b51f-4a85-a723-865dd65f6132", + "interface_key": "rest_api:PATCH /workstreams/{workstream_id}", + "interface_kind": "rest_api", + "legacy_since": "2026-06-04T06:09:41.901035Z", + "replacement_ref": "/workplans/{workplan_id}", + "owner_component": "state-hub.api", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-06-04T06:09:41.901035Z", + "updated_at": "2026-06-04T06:09:41.901035Z" + }, + "all_time": { + "calls": 584, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 584 + }, + "users": { + "unknown": 584 + }, + "components": { + "unknown": 584 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-16T16:33:34.045078Z", + "retirement_candidate": false, + "retirement_reason": "quiet 19d of 30d required for 584 all-time call(s)" + }, + { + "interface": { + "id": "ed6451c9-d2cc-4486-9afe-d3852782cabc", + "interface_key": "rest_api:POST /workstreams/", + "interface_kind": "rest_api", + "legacy_since": "2026-06-04T07:40:24.168535Z", + "replacement_ref": "/workplans/", + "owner_component": "state-hub.api", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-06-04T07:40:24.168535Z", + "updated_at": "2026-06-04T07:40:24.168535Z" + }, + "all_time": { + "calls": 811, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 811 + }, + "users": { + "unknown": 811 + }, + "components": { + "unknown": 811 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-16T01:03:41.093965Z", + "retirement_candidate": false, + "retirement_reason": "quiet 20d of 30d required for 811 all-time call(s)" + }, + { + "interface": { + "id": "281fd706-b192-4194-8055-2d8733c115f9", + "interface_key": "rest_api:POST /workstreams/{workstream_id}/dependencies/", + "interface_kind": "rest_api", + "legacy_since": "2026-06-04T22:57:37.149207Z", + "replacement_ref": "/workplans/{workplan_id}/dependencies/", + "owner_component": "state-hub.api", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "Retired by STATE-WP-0069 closeout", + "retired_at": "2026-08-31T08:55:09.130713Z", + "created_at": "2026-06-04T22:57:37.149207Z", + "updated_at": "2026-08-31T08:55:09.123888Z" + }, + "all_time": { + "calls": 4975, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 4975 + }, + "users": { + "unknown": 4975 + }, + "components": { + "unknown": 4975 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-07-07T20:31:35.389791Z", + "retirement_candidate": false, + "retirement_reason": "already retired" + } + ], + "retirement_candidates": [ + { + "interface": { + "id": "2f77ac12-8c97-4974-a2f9-2996927982d1", + "interface_key": "rest_api:GET /sbom/report/licences/", + "interface_kind": "rest_api", + "legacy_since": "2026-08-22T17:06:41.978426Z", + "replacement_ref": "sbom-nexus:/sbom/", + "owner_component": "state-hub.sbom-compat", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-08-22T17:06:41.978426Z", + "updated_at": "2026-08-22T17:23:44.794796Z" + }, + "all_time": { + "calls": 5, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 5 + }, + "users": { + "unknown": 5 + }, + "components": { + "unknown": 5 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-28T18:46:10.642164Z", + "retirement_candidate": true, + "retirement_reason": "no usage in review window; quiet 7d (>= 7d required for 5 all-time call(s))" + }, + { + "interface": { + "id": "b627edf5-3302-4366-ba2e-547d32f634b5", + "interface_key": "rest_api:GET /sbom/snapshots/", + "interface_kind": "rest_api", + "legacy_since": "2026-08-22T17:06:37.057774Z", + "replacement_ref": "sbom-nexus:/sbom/", + "owner_component": "state-hub.sbom-compat", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-08-22T17:06:37.057774Z", + "updated_at": "2026-08-22T17:22:15.080213Z" + }, + "all_time": { + "calls": 7, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 7 + }, + "users": { + "unknown": 7 + }, + "components": { + "unknown": 7 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-28T18:46:10.694185Z", + "retirement_candidate": true, + "retirement_reason": "no usage in review window; quiet 7d (>= 7d required for 7 all-time call(s))" + } + ] + }, + "retired_interfaces": [ + { + "id": "2f77ac12-8c97-4974-a2f9-2996927982d1", + "interface_key": "rest_api:GET /sbom/report/licences/", + "interface_kind": "rest_api", + "legacy_since": "2026-08-22T17:06:41.978426Z", + "replacement_ref": "sbom-nexus:/sbom/", + "owner_component": "state-hub.sbom-compat", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "STATE-WP-0079-T05: user-authorized retirement 2026-09-05; fresh seven-day gate passed, replacement_verified=true; direct SBOM Nexus endpoints returned HTTP 200 from the State Hub pod. Compatibility storage/routes retained for the final cutover.", + "retired_at": "2026-09-05T08:04:48.423727Z", + "created_at": "2026-08-22T17:06:41.978426Z", + "updated_at": "2026-09-05T08:04:48.418993Z" + }, + { + "id": "b627edf5-3302-4366-ba2e-547d32f634b5", + "interface_key": "rest_api:GET /sbom/snapshots/", + "interface_kind": "rest_api", + "legacy_since": "2026-08-22T17:06:37.057774Z", + "replacement_ref": "sbom-nexus:/sbom/", + "owner_component": "state-hub.sbom-compat", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "STATE-WP-0079-T05: user-authorized retirement 2026-09-05; fresh seven-day gate passed, replacement_verified=true; direct SBOM Nexus endpoints returned HTTP 200 from the State Hub pod. Compatibility storage/routes retained for the final cutover.", + "retired_at": "2026-09-05T08:04:48.506447Z", + "created_at": "2026-08-22T17:06:37.057774Z", + "updated_at": "2026-09-05T08:04:48.501706Z" + } + ], + "days": 7, + "after": { + "generated_at": "2026-09-05T08:04:48.937717Z", + "window_start": "2026-08-29T08:04:48.702204Z", + "window_end": "2026-09-05T08:04:48.702204Z", + "cadence": "weekly", + "activity_core_handoff": { + "activity_id": "statehub-legacy-interface-review", + "cadence": "weekly", + "source_endpoint": "/legacy-meter/weekly-review", + "state_owner": "state-hub", + "scheduler_owner": "activity-core" + }, + "interfaces": [ + { + "interface": { + "id": "8051fb40-66c9-4dc4-a413-13e8573e04a3", + "interface_key": "event_subject:org.statehub.workstream.completed", + "interface_kind": "event_subject", + "legacy_since": "2026-06-04T06:09:42.198193Z", + "replacement_ref": "org.statehub.workplan.completed", + "owner_component": "state-hub.events", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "Retired by STATE-WP-0069 closeout", + "retired_at": "2026-08-31T08:55:06.871333Z", + "created_at": "2026-06-04T06:09:42.198193Z", + "updated_at": "2026-08-31T08:55:06.756087Z" + }, + "all_time": { + "calls": 247, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 247 + }, + "users": { + "unknown": 247 + }, + "components": { + "state-hub.events": 247 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-07-13T13:22:00.748140Z", + "retirement_candidate": false, + "retirement_reason": "already retired" + }, + { + "interface": { + "id": "cd9342fd-27e7-4a46-a1be-d41499e106a3", + "interface_key": "rest_api:DELETE /workstreams/{workstream_id}", + "interface_kind": "rest_api", + "legacy_since": "2026-06-06T17:28:13.052813Z", + "replacement_ref": "/workplans/{workplan_id}", + "owner_component": "state-hub.api", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "Retired by STATE-WP-0069 closeout", + "retired_at": "2026-08-31T08:55:08.200322Z", + "created_at": "2026-06-06T17:28:13.052813Z", + "updated_at": "2026-08-31T08:55:08.089714Z" + }, + "all_time": { + "calls": 1, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 1 + }, + "users": { + "unknown": 1 + }, + "components": { + "unknown": 1 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-06-06T17:28:13.047529Z", + "retirement_candidate": false, + "retirement_reason": "already retired" + }, + { + "interface": { + "id": "d0294dce-7913-4268-94c3-1b2502aefb0f", + "interface_key": "rest_api:GET /sbom/", + "interface_kind": "rest_api", + "legacy_since": "2026-08-22T17:06:38.703608Z", + "replacement_ref": "sbom-nexus:/sbom/", + "owner_component": "state-hub.sbom-compat", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-08-22T17:06:38.703608Z", + "updated_at": "2026-08-22T17:23:43.730661Z" + }, + "all_time": { + "calls": 6, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 6 + }, + "users": { + "unknown": 6 + }, + "components": { + "unknown": 6 + } + }, + "window": { + "calls": 1, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 1 + }, + "users": { + "unknown": 1 + }, + "components": { + "unknown": 1 + } + }, + "last_seen_at": "2026-08-31T08:18:41.571768Z", + "retirement_candidate": false, + "retirement_reason": "1 call(s) in review window" + }, + { + "interface": { + "id": "2f77ac12-8c97-4974-a2f9-2996927982d1", + "interface_key": "rest_api:GET /sbom/report/licences/", + "interface_kind": "rest_api", + "legacy_since": "2026-08-22T17:06:41.978426Z", + "replacement_ref": "sbom-nexus:/sbom/", + "owner_component": "state-hub.sbom-compat", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "STATE-WP-0079-T05: user-authorized retirement 2026-09-05; fresh seven-day gate passed, replacement_verified=true; direct SBOM Nexus endpoints returned HTTP 200 from the State Hub pod. Compatibility storage/routes retained for the final cutover.", + "retired_at": "2026-09-05T08:04:48.423727Z", + "created_at": "2026-08-22T17:06:41.978426Z", + "updated_at": "2026-09-05T08:04:48.418993Z" + }, + "all_time": { + "calls": 5, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 5 + }, + "users": { + "unknown": 5 + }, + "components": { + "unknown": 5 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-28T18:46:10.642164Z", + "retirement_candidate": false, + "retirement_reason": "already retired" + }, + { + "interface": { + "id": "b627edf5-3302-4366-ba2e-547d32f634b5", + "interface_key": "rest_api:GET /sbom/snapshots/", + "interface_kind": "rest_api", + "legacy_since": "2026-08-22T17:06:37.057774Z", + "replacement_ref": "sbom-nexus:/sbom/", + "owner_component": "state-hub.sbom-compat", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "STATE-WP-0079-T05: user-authorized retirement 2026-09-05; fresh seven-day gate passed, replacement_verified=true; direct SBOM Nexus endpoints returned HTTP 200 from the State Hub pod. Compatibility storage/routes retained for the final cutover.", + "retired_at": "2026-09-05T08:04:48.506447Z", + "created_at": "2026-08-22T17:06:37.057774Z", + "updated_at": "2026-09-05T08:04:48.501706Z" + }, + "all_time": { + "calls": 7, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 7 + }, + "users": { + "unknown": 7 + }, + "components": { + "unknown": 7 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-28T18:46:10.694185Z", + "retirement_candidate": false, + "retirement_reason": "already retired" + }, + { + "interface": { + "id": "d03ee274-cd6f-4cd5-8f22-e4d298416299", + "interface_key": "rest_api:GET /sbom/snapshots/{snapshot_id}", + "interface_kind": "rest_api", + "legacy_since": "2026-08-22T17:23:45.969876Z", + "replacement_ref": "sbom-nexus:/sbom/", + "owner_component": "state-hub.sbom-compat", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "Retired by STATE-WP-0069 closeout", + "retired_at": "2026-08-31T08:55:08.470106Z", + "created_at": "2026-08-22T17:23:45.969876Z", + "updated_at": "2026-08-31T08:55:08.456139Z" + }, + "all_time": { + "calls": 1, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 1 + }, + "users": { + "unknown": 1 + }, + "components": { + "unknown": 1 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-22T17:23:45.967492Z", + "retirement_candidate": false, + "retirement_reason": "already retired" + }, + { + "interface": { + "id": "a6cb291e-8312-42f8-8a66-fde369482881", + "interface_key": "rest_api:GET /sbom/{repo_slug}", + "interface_kind": "rest_api", + "legacy_since": "2026-08-22T17:23:45.089542Z", + "replacement_ref": "sbom-nexus:/sbom/", + "owner_component": "state-hub.sbom-compat", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "Retired by STATE-WP-0069 closeout", + "retired_at": "2026-08-31T08:55:08.754914Z", + "created_at": "2026-08-22T17:23:45.089542Z", + "updated_at": "2026-08-31T08:55:08.750826Z" + }, + "all_time": { + "calls": 1, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 1 + }, + "users": { + "unknown": 1 + }, + "components": { + "unknown": 1 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-22T17:23:45.670511Z", + "retirement_candidate": false, + "retirement_reason": "already retired" + }, + { + "interface": { + "id": "cad13f19-ddb1-49b9-86d0-8789b7599f3a", + "interface_key": "rest_api:GET /tasks/?workstream_id", + "interface_kind": "rest_api", + "legacy_since": "2026-08-29T10:01:31.546213Z", + "replacement_ref": "/tasks/?workplan_id=", + "owner_component": "state-hub.api", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-08-29T10:01:31.546213Z", + "updated_at": "2026-08-29T10:01:31.546213Z" + }, + "all_time": { + "calls": 11, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 11 + }, + "users": { + "unknown": 11 + }, + "components": { + "unknown": 11 + } + }, + "window": { + "calls": 11, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 11 + }, + "users": { + "unknown": 11 + }, + "components": { + "unknown": 11 + } + }, + "last_seen_at": "2026-09-04T17:21:29.173850Z", + "retirement_candidate": false, + "retirement_reason": "11 call(s) in review window" + }, + { + "interface": { + "id": "3e4a3d0b-08fa-45c3-91e2-4f514a914b97", + "interface_key": "rest_api:GET /workstreams/", + "interface_kind": "rest_api", + "legacy_since": "2026-06-04T00:26:14.533764Z", + "replacement_ref": "/workplans/", + "owner_component": "state-hub.api", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-06-04T00:26:14.533764Z", + "updated_at": "2026-06-04T00:26:14.533764Z" + }, + "all_time": { + "calls": 830269, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 830269 + }, + "users": { + "unknown": 830269 + }, + "components": { + "unknown": 830269 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-21T13:50:37.994459Z", + "retirement_candidate": false, + "retirement_reason": "quiet 14d of 60d required for 830269 all-time call(s)" + }, + { + "interface": { + "id": "e4c140ec-7479-46f9-b185-4b533f19d639", + "interface_key": "rest_api:GET /workstreams/workplan-index", + "interface_kind": "rest_api", + "legacy_since": "2026-06-04T05:20:58.321869Z", + "replacement_ref": "/workplans/index", + "owner_component": "state-hub.api", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "Retired by STATE-WP-0069 closeout", + "retired_at": "2026-08-31T08:55:08.939572Z", + "created_at": "2026-06-04T05:20:58.321869Z", + "updated_at": "2026-08-31T08:55:08.933482Z" + }, + "all_time": { + "calls": 40, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 40 + }, + "users": { + "unknown": 40 + }, + "components": { + "unknown": 40 + } + }, + "window": { + "calls": 1, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 1 + }, + "users": { + "unknown": 1 + }, + "components": { + "unknown": 1 + } + }, + "last_seen_at": "2026-09-01T18:42:55.879795Z", + "retirement_candidate": false, + "retirement_reason": "already retired" + }, + { + "interface": { + "id": "3a00b19e-e7db-4403-98aa-a77c9ab61ecc", + "interface_key": "rest_api:GET /workstreams/{workstream_id}", + "interface_kind": "rest_api", + "legacy_since": "2026-06-04T00:25:59.014966Z", + "replacement_ref": "/workplans/{workplan_id}", + "owner_component": "state-hub.api", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-06-04T00:25:59.014966Z", + "updated_at": "2026-06-04T00:25:59.014966Z" + }, + "all_time": { + "calls": 3411477, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 3411477 + }, + "users": { + "unknown": 3411477 + }, + "components": { + "unknown": 3411477 + } + }, + "window": { + "calls": 7, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 7 + }, + "users": { + "unknown": 7 + }, + "components": { + "unknown": 7 + } + }, + "last_seen_at": "2026-09-03T22:02:35.348363Z", + "retirement_candidate": false, + "retirement_reason": "7 call(s) in review window" + }, + { + "interface": { + "id": "69e1a255-a8b9-4101-8a68-b1bed23abfda", + "interface_key": "rest_api:GET /workstreams/{workstream_id}/dependencies/", + "interface_kind": "rest_api", + "legacy_since": "2026-06-04T00:25:59.291135Z", + "replacement_ref": "/workplans/{workplan_id}/dependencies/", + "owner_component": "state-hub.api", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-06-04T00:25:59.291135Z", + "updated_at": "2026-06-04T00:25:59.291135Z" + }, + "all_time": { + "calls": 1643952, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 1643952 + }, + "users": { + "unknown": 1643952 + }, + "components": { + "unknown": 1643952 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-21T13:50:30.082480Z", + "retirement_candidate": false, + "retirement_reason": "quiet 14d of 60d required for 1643952 all-time call(s)" + }, + { + "interface": { + "id": "b1aae931-b51f-4a85-a723-865dd65f6132", + "interface_key": "rest_api:PATCH /workstreams/{workstream_id}", + "interface_kind": "rest_api", + "legacy_since": "2026-06-04T06:09:41.901035Z", + "replacement_ref": "/workplans/{workplan_id}", + "owner_component": "state-hub.api", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-06-04T06:09:41.901035Z", + "updated_at": "2026-06-04T06:09:41.901035Z" + }, + "all_time": { + "calls": 584, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 584 + }, + "users": { + "unknown": 584 + }, + "components": { + "unknown": 584 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-16T16:33:34.045078Z", + "retirement_candidate": false, + "retirement_reason": "quiet 19d of 30d required for 584 all-time call(s)" + }, + { + "interface": { + "id": "ed6451c9-d2cc-4486-9afe-d3852782cabc", + "interface_key": "rest_api:POST /workstreams/", + "interface_kind": "rest_api", + "legacy_since": "2026-06-04T07:40:24.168535Z", + "replacement_ref": "/workplans/", + "owner_component": "state-hub.api", + "status": "legacy", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": null, + "retired_at": null, + "created_at": "2026-06-04T07:40:24.168535Z", + "updated_at": "2026-06-04T07:40:24.168535Z" + }, + "all_time": { + "calls": 811, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 811 + }, + "users": { + "unknown": 811 + }, + "components": { + "unknown": 811 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-08-16T01:03:41.093965Z", + "retirement_candidate": false, + "retirement_reason": "quiet 20d of 30d required for 811 all-time call(s)" + }, + { + "interface": { + "id": "281fd706-b192-4194-8055-2d8733c115f9", + "interface_key": "rest_api:POST /workstreams/{workstream_id}/dependencies/", + "interface_kind": "rest_api", + "legacy_since": "2026-06-04T22:57:37.149207Z", + "replacement_ref": "/workplans/{workplan_id}/dependencies/", + "owner_component": "state-hub.api", + "status": "retired", + "replacement_verified": true, + "manual_hold": false, + "hold_reason": null, + "notes": "Retired by STATE-WP-0069 closeout", + "retired_at": "2026-08-31T08:55:09.130713Z", + "created_at": "2026-06-04T22:57:37.149207Z", + "updated_at": "2026-08-31T08:55:09.123888Z" + }, + "all_time": { + "calls": 4975, + "tenant_count": 1, + "user_count": 1, + "component_count": 1, + "tenants": { + "unknown": 4975 + }, + "users": { + "unknown": 4975 + }, + "components": { + "unknown": 4975 + } + }, + "window": { + "calls": 0, + "tenant_count": 0, + "user_count": 0, + "component_count": 0, + "tenants": {}, + "users": {}, + "components": {} + }, + "last_seen_at": "2026-07-07T20:31:35.389791Z", + "retirement_candidate": false, + "retirement_reason": "already retired" + } + ], + "retirement_candidates": [] + } +} diff --git a/docs/retirement-cutover-slice-plan.md b/docs/retirement-cutover-slice-plan.md index 26a4e56..71b8257 100644 --- a/docs/retirement-cutover-slice-plan.md +++ b/docs/retirement-cutover-slice-plan.md @@ -1,472 +1,93 @@ # 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` | **Partial — see correction below** | -| `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 unblocked today, and **only partly** — see the -correction immediately below. Everything else is waiting on an owner-side -contract, not on State Hub. - -### Correction (2026-08-20): repo-manager readiness is per-capability - -The table above reads owner readiness off workplan status, which is too coarse. -RMGR-WP-0001/0002/0003 are `finished`, but what they delivered is **work-record -dual-run, repository observation, and scaffolding** — not a general register -surface. Verified against `repo-manager/src/repo_manager/` on 2026-08-20: - -| `rmgr` surface | Covers | Slice | -| --- | --- | --- | -| `observe`, `load_classification` | repository representation, classification | A1 (partial) | -| `reconcile`, `index_store`, `parse/workplan.py`, `task-status`, `dual_run` | work records | A2 | -| `conform`, `standards.py`, `scaffold` | standards, onboarding | A1 `repo-onboarding` (already cut over) | -| `prefix` | workplan prefix registry | A1 (partial) | - -`parse/` contains **only** `workplan.py`. A grep for `sbom`, `repo_goal`, -`contribution`, `technical_debt`, and `extension_point` across the whole source -tree returns **nothing**. - -**Consequence:** A3, A4, and A5 have no owner-side surface to receive them. - -| Slice | Items | Receivable? | -| --- | --- | --- | -| A1 repository registry | 27 | Partial — `observe`/`conform` cover representation; registry CRUD does not exist | -| A2 work records | 59 | **Yes** — this is exactly what RMGR-WP-0002/0003 proved | -| A3 registers | 49 | **No** — no register surface of any kind in repo-manager | -| A4 work + repo UI | 22 | **No** — `rmgr` is a CLI; there is no owner-side dashboard | -| A5 topic spine | 8 | **No** — no topic/classification contract beyond `observe` | - -The near-term executable surface is therefore **A1 + A2, not 165 items**, and -A2 is the only slice with a proven adapter. - -**A3 is not the low-blast-radius first cut this plan originally recommended** — -it is the least ready slice in Wave A. The recommendation is withdrawn. - -**Do not build the register surface in State Hub.** New register capability here -would be new permanent ownership in the component being retired, which -`policies/retirement-freeze.md` makes inadmissible. A3/A4/A5 need a -`repo-manager` workplan; RMGR-WP-0004 (standards conformance) and RMGR-WP-0005 -(registrar consolidation) do not cover them. - -### Owner readiness refresh (2026-08-31) - -The original readiness table is retained above as planning history. Current -workplan status changes which owners can receive a slice, but it does not count -as State Hub cutover evidence by itself: - -| Owner | Current owner evidence | State Hub cutover consequence | -| --- | --- | --- | -| `repo-manager` | RMGR-WP-0005 finished deterministic identifier migration; RMGR-WP-0008 finished register/classification surfaces | Receiving-side identity and register gates are satisfied; each State Hub writer/read switch still needs its own parity and rollback receipt. | -| `hub-core` | HUB-WP-0005 finished and its production runtime owns all declared hub-core route/writer groups with exercised rollback | Receiving runtime is available; B1-B5 are not complete until State Hub caller/route switches and counts are evidenced. | -| `activity-core` | ACTIVITY-WP-0029 finished schedule, sweep, interaction-event, and execution-boundary work | The launch-request defect is retired; remaining C-slice caller switches still require explicit evidence. | -| `ops-hub` | OPS-WP-0003 finished; owner package and implemented conformance profile pass | D1 receiving-side gate is satisfied. State Hub route/caller switch, count parity, rollback, and writer authorization remain cutover evidence. | -| `fin-hub` | FIN-WP-0003 remains `proposed`; 0/3 tasks done | D2 cannot move. Do not implement the fabric authority contract here. | -| `archive` | 2026-08-31 central meter: 6 newly retired, 9 legacy, 0 candidates | E2/E4 remain gated; live callers and volume-scaled quiet windows prohibit retirement. | - -Accordingly, `STATE-WP-0079-T04` is waiting on the missing owner workplans, -the still-unrecorded State Hub route-switch receipts, and explicit production -writer-cutover authorization. `T05` waits on fresh central meter evidence, and -`T06` cannot begin while State Hub remains the healthy, active `railiance01` -primary. - -## 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. - -### A2 readiness detail (2026-08-20) - -Same check as for A3, applied one level down. The State Hub adapter -(`api/services/repo_manager_dual_run.py`) exposes exactly three operations: - -| Adapter function | Routes to | Covers | -| --- | --- | --- | -| `rm_update_task_status` | `rmgr task-status` | task status writes | -| `rm_reconcile` | `rmgr reconcile` | work-record index rebuild | -| `rm_scaffold` | `rmgr scaffold` | repo scaffolding (A1 `repo-onboarding`, already cut over) | - -There is no adapter for workplan create/update/delete, intake, decision, -dependency, or human-flag records — and no parser for them on the repo-manager -side either (`parse/` is `workplan.py` only). So A2 splits: - -| Sub-slice | Items | Receivable | -| --- | --- | --- | -| **A2a** task status + reconcile | ~6 (`update_task_status`, `bulk_update_task_statuses`, `PATCH /tasks/{id}`, `POST /tasks/bulk-status-sync`, index refresh job, `table:tasks`) | **Yes — adapter exists and was proven by RMGR-WP-0002/0003** | -| A2b workplan CRUD | ~9 routes + `table:workplans` | No adapter; repo-manager parses workplans but exposes no write path | -| A2c intake | 9 | No parser, no adapter | -| A2d decision | 8 | No parser, no adapter | -| A2e dependencies + human-flag | ~6 | No parser, no adapter | - -**A2a is the only slice in the entire plan that is executable today**, and it is -roughly 6 items, not 59. - -### Dual-run is currently off - -`rmgr dual-run-status` on 2026-08-20: - -``` -config_exists: false writeback: false reconcile: false pilot_repos: null -meter: {state-hub: 15, repo-manager: 8, total: 23} -``` - -The 23 metered mutations are the RMGR-WP-0002/0003 pilot runs. Nothing is -dual-running now — the adapter is proven but switched off, and there is no -`~/.repo-manager/dual-run.yaml`. - -**So the concrete next executable step is operational, not code:** create the -dual-run config and enable `RM_WRITEBACK` + `RM_RECONCILE`, then watch the -mutation meter for divergence. That is A2a moving from `off` to `dual`. - -**The pilot repo is already established — `repo-manager`**, per RMGR-WP-0003 and -the shipped `repo-manager/config/dual-run.pilot.example.yaml`. No new decision -is needed about which repo; the procedure is documented in -`repo-manager/docs/dual-run.md`: - -```bash -mkdir -p ~/.repo-manager -cp ~/repo-manager/config/dual-run.pilot.example.yaml ~/.repo-manager/dual-run.yaml -# then restart the State Hub API — settings are cached per process -``` - -Rollback is `rm ~/.repo-manager/dual-run.yaml` plus a restart, or setting -`writeback`/`reconcile` false. - -**Requires an operator**, for one reason only: it changes how live task writes -flow and needs a State Hub API restart to take effect. Piloting on -`repo-manager` keeps the blast radius to a single repo. - -### 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 ...`; `rmgr sbom scan|licence-report` | -| Rollback | flag → `off`; retain current State Hub projection | - -**Owner ready (2026-08-21).** Finished RMGR-WP-0008 supplies the shared six-kind -register spine, governed compatibility adapter, lockfile-derived SBOM and -licence/copyleft report. Historical SBOM rows are cache provenance; rebuild -from current repository sources rather than copying them into authority. - -### A4 · Work + repo UI — 22 items -`work-coordination-ui` (14, `replace`) · `repo-ui` (8) - -| | | -| --- | --- | -| Flag | `RM_SLICE_UI` | -| Owner API | hub-core projection/query plus hub/ops UI clients | -| 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. RMGR-ADR-003 corrects the earlier inventory -assignment: Repo Manager owns the repository contracts, not a dashboard. A4's -UI implementation therefore rides with B5 even though these 22 inventory items -remain grouped here for traceability. - -### A5 · Topic spine — 8 items -`topic-spine` (8, `replace`) - -| | | -| --- | --- | -| Flag | `RM_SLICE_TOPICSPINE` | -| Owner API | `rmgr` topic/classification contract | -| Rollback | flag → `off` | - -The versioned Repo Manager classification contract is published and accepted by -finished HUB-WP-0004. Consumer route switching remains a State Hub cutover -action. - ---- - -## Wave B — hub-core (128 items) · HUB-WP-0004 owner gate satisfied - -HUB-WP-0004 finished 2026-08-21 and accepted the project architecture, runtime, -ports, conformance scaffold, and absorption plan. Individual production -cutovers still require their slice evidence and operator gates. - -| 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) · fin-hub gated - -| Slice | Owner | Capabilities | Items | Gate | -| --- | --- | --- | --- | --- | -| D1 | `ops-hub` | `service-catalog` (9), `service-catalog-ui` (2) | 11 | OPS-WP-0003 `finished`; receiving contract ready, cutover receipt pending | -| 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 | +**Execution owner:** STATE-WP-0079-T04/T05 +**Re-baselined:** 2026-09-05 +**Inventory:** SHR-INV-0001, original 425-item inventory in +`prj-state-hub-retirement/inventory/`. Counts below identify historical slices; +current owner contracts take precedence over the original owner assignments. + +This checklist replaces the obsolete August 19–20 readiness snapshot. That +snapshot and the original off/dual/owner proposal remain in Git history. +`RM_SLICE_*` names were proposed controls, not proof of deployed flags. Do not +execute the old workstation dual-run instructions against the cluster service: +ADR-002 forbids central services from mutating workstation checkouts. + +## Evidence required to switch a slice + +For each route family, identify the current caller and destination endpoint, +compare representative identities/content/counts in a read-only parity run, +record the exact deployment/source revisions, exercise rollback, and retain the +switch receipt. An owner workplan marked finished or an imported Python router +proves neither deployed endpoint availability nor data migration. Production +writer changes require a concrete recoverable cutover, within user authorization. +No new permanent receiving authority belongs in State Hub during retirement. + +## Current route-family checklist + +| Slice / historical items | Receiver and contract evidence | Caller / route-switch evidence | Remaining gate and rollback | | --- | --- | --- | --- | -| E1 | `suggestion-backlog` → archive | 17 | history archived 2026-08-20; read surface + dashboard remain | -| E2 | `work-records-workplan-legacy` → archive | 13 | legacy-meter — candidate rule, see below | -| E3 | `dashboard-meta` (`/ui-feedback`) | 1 | T06 freeze window — owner is `state-hub-until-cutover` | -| E4 | `legacy-meter` itself | 9 | **last** — it is the instrument for E2 | +| A1 registry / 27 | Repo Manager observation/scaffolding; STATE-WP-0081 and 0086 establish forge-derived projection | `rmgr sync` verified pushed commit `74a3b22` on primary/railiance01 in the preceding session | Inventory remaining registry callers per endpoint. Keep current projection while proving the owner read; do not restore checkout mutation in central | +| A2 work records / 59 | RMGR-WP-0008 implements file-backed workplan, intake, decision, dependency and task mutations; RMGR-WP-0005 identifiers finished | Repository files + `rmgr sync` are the current write convention. Two ad-hoc task-filter callers identified and guidance corrected in this review | Work-record caller inventory and per-kind writer receipts still required. Revert source commit and reconcile for a reviewed rollback; no second file authority | +| A3 registers / 49 | RMGR-WP-0008 register spine; **SBOM Nexus** is the current SBOM product owner | Live State Hub config uses Nexus for both SBOM reads/writes. Direct Nexus licence and snapshot reads return 200. Two more compatibility interfaces retired in the meter September 5 | Other registers need individual caller/parity receipts. Preserve SBOM history; remaining `/sbom/` traffic and retained compatibility handlers prevent deleting the whole router | +| A4 work/repo UI / 22 | hub-core projection + UI clients; RMGR-ADR-003 excludes a Repo Manager dashboard | No complete State Hub UI redirect receipt established | First prove backing reads; rollback redirects to the retained UI | +| A5 topic spine / 8 | Published Repo Manager classification contract, accepted by HUB-WP-0004 | No complete State Hub caller-switch receipt established | Prove deployed consumer contract and parity; retain source-backed classification projection | +| B1 catalogs / 34 | HUB-WP-0004/0005 receiving architecture and `/api/v2` absorption | Completion covers the Core Hub route groups, not every State Hub capability/registry route | Map each State Hub catalog to a deployed endpoint and migration receipt | +| B2 messaging/interface log / 22 | Shared `hub_core.routers.messages` exists; State Hub interface log is a separate family | Live Hub Core OpenAPI has **no `/messages` or `/interface-changes`**. `/api/v2/interaction-events` exists but is not asserted equivalent | **Next bounded read slice:** STATE-WP-0079-T08. Owner must expose/identify the message read contract and history projection before a parity run or switch. Retain current reads/writer | +| B3 telemetry / 32 | Shared `hub_core.routers.progress` exists; token/software catalogs need separate mappings | Live Hub Core OpenAPI has **no `/progress` or `/token-events`** | Owner runtime assembly/mapping plus historical identity/count parity; writer exclusivity and rollback receipts remain necessary | +| B4 projection/policy / 20 | HUB-WP-0004/0005; policy publication belongs to policy-nexus | State Hub still serves its projection routes; no full family receipt established | Map individual projections and policy publication; retain source/runtime boundaries and current reads | +| B5 hub UI / 17 | Hub Core runtime/console exists | Core Hub absorption does not establish replacement of State Hub dashboard pages | Prove A4/B1–B4 data contracts before redirect; retain UI rollback | +| C1 execution / 13 | ACTIVITY-WP-0029 finished; Activity Core owns scheduling/ops_run | STATE-WP-0079 records launch-request rejection (410), replacement semantics, and corrected unconsumed requests | Verify every remaining execution caller uses the owner contract. Do not revive the obsolete launch queue for rollback | +| C2 jobs / 5 | ACTIVITY-WP-0029 finished; scheduler is Activity Core, reconciliation engine is Repo Manager | Receiving/sweep handoffs recorded in owner workplan | Attach remaining per-job deployment and caller receipts; keep scheduler ownership distinct from work-record authority | +| D1 service catalog / 11 | OPS-WP-0003 finished; reviewed packages and `/api/v2` conformance gate | Owner contract completion established; State Hub family-switch receipt still missing | Compare catalog identities and UI bindings; preserve current reads until switched | +| D2 Fabric / 10 | **railiance-fabric** authority, FIN-WP-0003 consumer gate | August 31 dual-read import: 131 nodes/117 edges; green hash/count/provenance checks, rollback and roll-forward exercised | Hosted runtime/persistence/auth/freshness remains RAIL-FAB-WP-0028 (`proposed`). Keep retained import rollback; do not claim the local authority is a hosted production receiver | +| D3 kaizen / 2 | the-custodian, original two-tool disposition | No fresh removal/delegation receipt collected in this review | Locate current tools/consumers and owner contract before removal | +| E1 suggestions / 17 | Archived history in the-custodian | STATE-WP-0079 records eight read routes at 410, six MCP tools removed, dashboard removed | Completed surface removal; tables retained for final dump | +| E2 legacy workplans / 13 | Preferred `/workplans` routes and `workplan_id` parameters | Workstream REST handlers return 410 and meter rejected calls. Task query alias still accepted | Seven legacy meter records remain after today's two SBOM retirements; observe volume-scaled quiet periods and complete T07 attribution | +| E3 UI feedback / 1 | State Hub until cutover | Feedback still needed during transition | T06 zero-normal-traffic gate; do not confuse it with retired suggestions | +| E4 meter / 9 | State Hub until all other retirement evidence complete | Eight retired / seven legacy interfaces, zero current candidates after September 5 action | Retire last, after the remaining interfaces and final evidence are closed | -E4 retires only after every other legacy surface is closed; retiring the meter -first destroys the evidence for the retirements it gates. +## What today's probes establish -### E1 status (2026-08-20) +- Central health identifies `primary/railiance01`. +- The live State Hub configuration has `SBOM_NEXUS_READ_MODE=nexus` and + `SBOM_NEXUS_WRITE_MODE=nexus`, using the cluster SBOM Nexus service. +- Direct Nexus `/sbom/report/licences/` and `/sbom/snapshots/` returned 200 from + the State Hub pod. Their State Hub meter entries are now retired. This does + **not** delete the compatibility handlers or historical data. +- Hub Core candidate runtime OpenAPI, read from its own pod loopback, has 31 + paths and lacks the four State Hub families identified above. The State Hub + pod's probes to both Core Hub service addresses were connection-refused; + the deployments reported 1/1 ready. Transport readiness must be resolved as + part of any proxy-based pilot; this review did not diagnose its cause. +- Session trace identifies ad-hoc legacy task reads in fluid-telegram and + ops-warden. Their corrected canonical queries return 200 with 8 and 5 tasks. + No evidence identifies the other historical callers yet. -Already half-retired before this plan: mutations have returned HTTP 410 since -2026-07-21 (`CUST-WP-0061-T06`), pointing at `POST /intakes/`. Reads were kept -live so the historical record stayed reachable. +Evidence: `docs/evidence/STATE-WP-0079-sbom-retirement-20260905.json` and +`docs/evidence/STATE-WP-0079-caller-and-receiver-review-20260905.md`. -**History is now archived** at `the-custodian/docs/archived-suggestion-backlog.md` -— all 10 suggestions, 10 notes, and 5 relevance bumps, with each entry's -migration target. Every entry was `declined` in the intake migration and none -was promoted, so nothing forward-looking is lost by removing the surface. +## Next executable work -**Executed 2026-08-20.** The 8 read routes now 410 with a pointer to intake and -to the archive, the 6 MCP tools are removed (not stubbed — a retired tool that -still appears in the tool list costs every agent session context on every call), -the 5 `/suggestions` write-idempotency rules are gone, and the dashboard page is -deleted with its nav entry, `reference.md` row, and `wsjf-triage.md` link -updated. `dashboard/src/docs/suggestions.md` is rewritten as the archive -pointer. Tables stay for the T06 dump. +1. T07: observe the remaining legacy readers with component attribution. The + known caller repositories and bundled State Hub skill now prescribe canonical + reads. Preserve the quiet clocks; do not exercise legacy URLs to test them. +2. T08: obtain one **deployed message-read contract** from hub-core, including + access/transport and historical projection. Use the State Hub inbox reader as + the bounded parity client. Compare message IDs and read/archive flags against + one consistent snapshot; do not change message writes during this proof. + Only after parity and rollback evidence should a concrete reader switch run. +3. RAIL-FAB-WP-0028-T01 remains the owner decision for runtime, persistent store, + auth, backup/restore and deployment repository. Its T02–T04 deliver deployment, + freshness and final direct-consumer receipts; no new Fabric authority here. +4. Re-evaluate eligible interfaces from a fresh seven-day meter capture. The + quiet ladder is 7 days for 1–99 historical calls, 30 for 100–9,999, and 60 for + 10,000+. A new call restarts the relevant clock, including rejected requests. -One naming trap worth recording: **`ui-feedback.md` and `todo.md` "suggestions" -are a different entity** — Shift+click dashboard feedback backed by -`technical_debt`, not the suggestion backlog. They are untouched. Anything -grepping for `suggestion` during cutover will hit them. +## Freeze and infrastructure boundary -### E3 status (2026-08-20) - -`dashboard-meta` is the `/ui-feedback` page — the feedback scratch surface just -mentioned. Its owner is `state-hub-until-cutover` and it is still in active use, -so it retires **at the T06 freeze window**, not before. Removing it now would -delete a working feedback loop during the retirement it serves. No action. - -### E2 evidence — defect found and partly fixed (2026-08-20) - -The window discrepancy flagged on 2026-08-19 was a real defect in -`scripts/capture_legacy_meter_evidence.py`, not a labelling nit. - -`_review_query` fell back to `hours=8` whenever `--days` was omitted, and -`--hours` defaulted to `8`. Every unattended capture therefore sampled an -8-hour band (06:00Z–14:00Z) while writing a file named `weekly-review` with -`cadence: weekly` in the payload. **39 of 40 captures ran this way** — only the -first, 2026-07-08, used a true 7-day window. - -Calls in the other 16 hours of each day were never sampled, so quiet-in-band -interfaces were reported as retirement candidates while still having live -callers. On the 2026-08-19 capture three candidates had recent traffic: - -| Interface | All-time calls | Last seen | -| --- | --- | --- | -| `GET /tasks/?workstream_id` | 594 | 2026-08-18 | -| `GET /progress/?workstream_id` | 2 | 2026-08-12 | -| `GET /workstreams/` | 144 086 | 2026-08-10 | - -**Fixed:** the script now defaults to `days=7`, matching the endpoint name and -the cadence label. `--hours` is retained for spot checks and documented as not -retirement evidence. Corrected capture: -`docs/evidence/legacy-meter-weekly-review-20260820.json` — 18 candidates of 20 -interfaces, `GET /tasks/?workstream_id` correctly excluded. - -**Candidate rule tightened (2026-08-20).** `_retirement_state` now requires a -quiet period scaled to all-time call volume, because a clean window only proves -silence for that window — for a heavily used interface an infrequent caller can -fall outside it entirely. `RETIREMENT_QUIET_LADDER` in -`api/services/legacy_meter.py`: - -| All-time calls | Days since `last_seen_at` required | -| --- | --- | -| 0 | none — never used | -| 1 – 99 | 7 | -| 100 – 9 999 | 30 | -| 10 000+ | 60 | - -Applied to the 2026-08-20 capture this splits the 19 legacy interfaces cleanly: - -**15 retirable** — all four `workstream` MCP tools, the `state://` resource, and -the low-volume REST aliases, plus `PATCH /workstreams/{id}` (571 calls, quiet -42d), `POST /workstreams/` (824, quiet 42d) and -`POST /workstreams/{id}/dependencies/` (4 971, quiet 49d). - -**4 held:** - -| Interface | All-time calls | Reason | -| --- | --- | --- | -| `GET /tasks/?workstream_id` | 595 | traffic inside the window | -| `GET /workstreams/` | 144 086 | quiet 9d of 60d | -| `GET /workstreams/{id}` | 511 406 | quiet 19d of 60d | -| `GET /workstreams/{id}/dependencies/` | 255 865 | quiet 42d of 60d | - -**Executed 2026-08-20:** the 15 are retired; the meter reads 16 retired / 4 -legacy with 0 outstanding candidates. Journalled at -`the-custodian/docs/retired-legacy-interfaces.md`. The four -held are the three six-figure read paths and the one with live traffic — exactly -the set where a false retirement would hurt most. They clear on their own as the -quiet counter runs, provided no new traffic arrives; the earliest, -`GET /workstreams/{id}/dependencies/`, needs 18 more silent days. - -Re-check before executing E2: the ladder is evaluated at review time, so a -capture older than the retirement decision is not evidence for it. - -## 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~~ — done 2026-08-20; E1 and the - evidenced part of E2 are executed. -2. ~~Build the Repo Manager receiving surface for A3/A5 and settle A4 - ownership~~ — finished in RMGR-WP-0008. A4 belongs to hub-core projection UI, - not Repo Manager. -3. Cut **A2a** (task status + reconcile, ~6 items) — the only slice in the plan - executable today. First step is operational: create `~/.repo-manager/dual-run.yaml` - and enable writeback/reconcile for one pilot repo, then watch the mutation - meter. A2b–A2e need repo-manager parsers and adapters that do not exist. -4. HUB-WP-0004's owner-readiness gate is satisfied; advance Wave B only through - its per-slice dual-run and operator gates. -5. Do not build register, UI, or topic capability in State Hub to unblock - items 2–3 — `policies/retirement-freeze.md` makes that inadmissible. +T06 remains blocked until move/replace families and legacy retirement complete, +followed by a fresh zero-normal-read/write window. Final dump, restore proof, +service stop and repository archive then form one separately reviewable +operation. The original schema-history/keep items and nine infrastructure +couplings (database, scheduler, connectivity) remain explicit final-window +checks; they are not route flags. Ops Bridge connectivity survives retirement. diff --git a/skills/state-hub/SKILL.md b/skills/state-hub/SKILL.md index 6381b9b..736cd59 100644 --- a/skills/state-hub/SKILL.md +++ b/skills/state-hub/SKILL.md @@ -1,6 +1,6 @@ --- name: state-hub -description: Use when coordinating with Custodian State Hub: orienting with domain summaries, checking agent inbox messages, updating workplan-backed task status, recording decisions/progress, or batching task status sync through MCP/REST without re-discovering tool schemas. +description: Coordinate repository-backed work through State Hub REST or optional MCP, including canonical workplan reads, inbox checks, task status sync, and progress logging. --- # State Hub Coordination @@ -11,21 +11,32 @@ work structure in the hub when a workplan file is the canon. ## Session Flow -1. Orient with `get_domain_summary(domain_slug)` when working inside one domain - repo. Use `get_state_summary()` only for cross-domain/custodian-wide work. +1. Prefer REST/`statehub` for Codex. Verify `/state/health` identifies the expected + primary, then read `/workplans/` with repo/topic filters. MCP is opt-in; use + `get_domain_summary(domain_slug)` when enabled. 2. Check inbox with `get_messages(to_agent=, unread_only=true)`. Mark acted-on messages with `mark_message_read(message_id)`. -3. During work, edit the workplan file first. Mirror task/workstream status to - the hub at checkpoints. +3. During work, edit the workplan file first. Use `workplan_id` in API payloads + and task queries; legacy `workstream_id` is accepted only for compatibility. 4. Prefer `bulk_update_task_statuses(...)` for checkpoint syncs with multiple task updates. Use `update_task_status(...)` for one-off changes. -5. Close with one concise `add_progress_event(...)`, then run the repo's - `make fix-consistency REPO=` command when workplan files changed. +5. Close with one concise progress event. After committing workplan file changes, + run `uv run --project ~/repo-manager rmgr sync --path . --push`. Reserve + `statehub fix-consistency` for a separate deep audit. 6. If finishing a workplan with leftovers: create **residual** work records first (intake with `origin: residual` + `origin_ref: `, or a child workplan). Residual is a role, not a kind; do not leave backlog only in finished-file prose or `SCOPE.md`. Canon: work-record-types § Residuals. +## Canonical reads and caller attribution + +Use `GET /workplans/{id}` and `GET /tasks/?workplan_id=`. +The `/workstreams/` REST routes return 410 and the `create_workstream` MCP tool +has been removed. Create repository-owned records in files before syncing. +For direct HTTP calls, send `X-StateHub-Component: ` so a remaining +legacy call can be attributed to its caller. Do not replace authoritative +`state_hub_workstream_id` frontmatter fields; those are a separate file contract. + ## High-Frequency MCP Signatures ```text @@ -33,12 +44,12 @@ get_domain_summary(domain_slug: str) -> str get_messages(to_agent?: str, from_agent?: str, unread_only: bool = false, limit: int = 20) -> str send_message(from_agent: str, to_agent: str, subject: str, body: str, thread_id?: str) -> str -create_workstream(topic_id: str, title: str, slug?: str, description?: str, owner?: str, due_date?: str, repo_id?: str, planning_priority?: str, planning_order?: int) -> str -create_task(workstream_id: str, title: str, priority: str = "medium", description?: str, assignee?: str, due_date?: str) -> str +create_workplan(repo_id: str, title: str, topic_id?: str, slug?: str, description?: str, owner?: str, due_date?: str, planning_priority?: str, planning_order?: int) -> str +create_task(workplan_id: str, title: str, priority: str = "medium", description?: str, assignee?: str, due_date?: str) -> str update_task_status(task_id: str, status: str, blocking_reason?: str, tokens_in?: int, tokens_out?: int, workplan_tokens_in?: int, workplan_tokens_out?: int, note?: str, model?: str, agent?: str, session_id?: str) -> str bulk_update_task_statuses(updates: list[dict], author?: str = "custodian", session_id?: str) -> str -add_progress_event(summary: str, event_type: str = "note", topic_id?: str, workstream_id?: str, task_id?: str, detail?: dict | str) -> str -record_decision(title: str, decision_type: str = "pending", topic_id?: str, workstream_id?: str, description?: str, rationale?: str, decided_by?: str, deadline?: str) -> str +add_progress_event(summary: str, event_type: str = "note", topic_id?: str, workplan_id?: str, task_id?: str, detail?: dict | str) -> str +record_decision(title: str, decision_type: str = "pending", topic_id?: str, workplan_id?: str, description?: str, rationale?: str, decided_by?: str, deadline?: str) -> str ``` `bulk_update_task_statuses` updates `N` task statuses in one call: diff --git a/skills/state-hub/references/tool-signatures.md b/skills/state-hub/references/tool-signatures.md index 18decfa..562b954 100644 --- a/skills/state-hub/references/tool-signatures.md +++ b/skills/state-hub/references/tool-signatures.md @@ -10,8 +10,8 @@ MCP: get_domain_summary(domain_slug) REST: GET /state/summary then filter by topic/domain when MCP is unavailable ``` -Use `get_domain_summary("custodian")` inside State Hub work. It returns the -domain topic, active workstreams, blocking decisions, recent progress, repos, +Use `get_domain_summary("infotech")` inside State Hub work. It returns the +domain topic, active workplans, blocking decisions, recent progress, repos, and compact capability hints. ## Agent Messages @@ -30,19 +30,25 @@ REST: PATCH /messages/{message_id}/read Use repo slugs as agent names. Use `broadcast` only for genuinely shared coordination. -## Workstreams and Tasks +## Workplans and Tasks ```text -MCP: create_workstream(topic_id, title, slug?, description?, owner?, due_date?, repo_id?, planning_priority?, planning_order?) -REST: POST /workstreams/ +MCP: create_workplan(repo_id, title, topic_id?, slug?, description?, owner?, due_date?, planning_priority?, planning_order?) +REST: POST /workplans/ -MCP: create_task(workstream_id, title, priority="medium", description?, assignee?, due_date?) +MCP: create_task(workplan_id, title, priority="medium", description?, assignee?, due_date?) REST: POST /tasks/ MCP: update_task_status(task_id, status, blocking_reason?, tokens_in?, tokens_out?, workplan_tokens_in?, workplan_tokens_out?, note?, model?, agent?, session_id?) REST: PATCH /tasks/{task_id} ``` +For repository-owned work, write the workplan/task files and sync with Repo +Manager; the create signatures above describe the projection API. Read using +`GET /workplans/{id}` and `GET /tasks/?workplan_id=`. Send +`X-StateHub-Component: ` on direct HTTP requests for caller attribution. +The removed workstream tools and `/workstreams/` routes are not fallbacks. + Canonical task statuses are `wait`, `todo`, `progress`, `done`, and `cancel`. Legacy aliases are accepted during migration, but do not emit new workplan files with old vocabulary. @@ -86,10 +92,10 @@ The endpoint rejects duplicate task ids with `400` and missing task ids with ## Progress and Decisions ```text -MCP: add_progress_event(summary, event_type="note", topic_id?, workstream_id?, task_id?, detail?) +MCP: add_progress_event(summary, event_type="note", topic_id?, workplan_id?, task_id?, detail?) REST: POST /progress/ -MCP: record_decision(title, decision_type="pending", topic_id?, workstream_id?, description?, rationale?, decided_by?, deadline?) +MCP: record_decision(title, decision_type="pending", topic_id?, workplan_id?, description?, rationale?, decided_by?, deadline?) REST: POST /decisions/ ``` diff --git a/workplans/STATE-WP-0079-retirement-strangler.md b/workplans/STATE-WP-0079-retirement-strangler.md index 15ab51b..06b26b5 100644 --- a/workplans/STATE-WP-0079-retirement-strangler.md +++ b/workplans/STATE-WP-0079-retirement-strangler.md @@ -425,6 +425,16 @@ direct route are live owner residual `RAIL-FAB-WP-0028`. Evidence: the non-D2 repo-manager/hub-core/activity-core/ops-hub route receipts and the explicit writer-change authorization already stated above. +**Cutover re-baseline 2026-09-05.** Replaced the obsolete slice-readiness +snapshot in `docs/retirement-cutover-slice-plan.md` with a family-by-family +receiver/caller/parity/rollback checklist. Live Hub Core runtime OpenAPI lacks +`/messages`, `/progress`, `/token-events` and `/interface-changes`; its working +`/api/v2/interaction-events` is not assumed equivalent. Shared library routers +and HUB-WP-0005 completion are insufficient receiving receipts. T08 now records +the concrete first read-contract gap. State Hub-to-Core Hub service probes were +connection-refused although deployments were ready; transport is also part of +that gate. RAIL-FAB-WP-0028 remains proposed and owns hosted Fabric readiness. + ## Retire legacy surfaces ```task @@ -536,6 +546,22 @@ calendar passage alone is not retirement evidence. the existing hold; no additional retirement is justified. T04/T05/T06 remain `wait` with their existing owner and quiet-window dependencies. +**Authorized retirement and caller migration 2026-09-05.** Fresh seven-day +primary evidence made `GET /sbom/report/licences/` and `GET /sbom/snapshots/` +eligible. Verified direct Nexus endpoints (200), then marked both interfaces +retired with T05 provenance. Receipt: +`docs/evidence/STATE-WP-0079-sbom-retirement-20260905.json`. Eight retired, +seven legacy, zero remaining candidates. Compatibility handlers/history remain; +this is meter retirement, not a router deletion or application deployment. + +Session traces identified ad-hoc task-filter callers in fluid-telegram and +ops-warden. Corrected their AGENTS.md canonical-query/component-header guidance, +plus this repository's stale skill and API documentation. Canonical verification +returned 200 with 8/5 tasks. This does not explain every historical call or prove +future silence: unresolved task/workstream-detail/SBOM-index attribution is T07. +Available pod logs begin after the last calls. Evidence and limits: +`docs/evidence/STATE-WP-0079-caller-and-receiver-review-20260905.md`. + ## Stabilization window and archive prep ```task @@ -555,6 +581,47 @@ stop, or repository archive would therefore interrupt normal work and destroy the evidence needed by T05. T06 remains `wait` until T04 and T05 are done and a fresh central meter demonstrates the required zero normal read/write window. +## Attribute and finish migrating remaining legacy readers + +```task +id: STATE-WP-0079-T07 +status: wait +priority: high +``` + +Owner: state-hub. T05 follow-up from the 2026-09-05 scan. Two ad-hoc task-query +callers were identified and their instructions corrected; the full 11 task +calls, seven workstream-detail calls and one SBOM index call remain only +partially attributed. Existing retained logs postdate these calls. Wait for +new attributed observations or recoverable historical request evidence; do not +reset the clocks with test calls. Trace remaining callers, migrate them to the +verified replacements, and attach fresh quiet-window evidence before retirement. +Use `X-StateHub-Component` for direct agent HTTP requests. No inference from +an `unknown` bucket alone is sufficient to assign an owner. + +## Prove the first deployed hub-core message-read contract + +```task +id: STATE-WP-0079-T08 +status: todo +priority: high +``` + +Receiving owner: hub-core; State Hub owns source/caller evidence under T04. +The live receiver's 31-path OpenAPI has no message/progress/token/interface-log +families. Identify or assemble the owner message-read contract, auth/transport, +and historical projection in the receiving runtime. State Hub-to-owner service +probes were connection-refused in this review; diagnose the actual transport +before assuming a usable proxy path. Do not substitute `/api/v2/interaction-events` +without an explicit semantic mapping. + +Then run a bounded read-only inbox parity pilot over one consistent snapshot, +checking message IDs, ordering, read/archive flags and scope filters; record +exact revisions and rollback to the retained State Hub read. Done when receiving +readiness and parity/rollback receipts make one concrete reader switch reviewable. +Production message writers stay unchanged during this proof. Related owners: +HUB-WP-0004/0005; the full B2/B3 route migration is not implied by this task. + ## Acceptance - [x] Freeze policy documented