Retire GET /workstreams/workplan-index with 410; railiance01 deploy runbook
Return 410 Gone for legacy workplan-index route (replacement /workplans/index). Add tests, update transition backlog, operator deploy evidence doc, and STATE-WP-0070 progress notes.
This commit is contained in:
parent
7e68f9e8e3
commit
14b7d8418d
7 changed files with 88 additions and 9 deletions
|
|
@ -412,21 +412,26 @@ async def list_workplans(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/workplan-index")
|
@router.get("/workplan-index", status_code=status.HTTP_410_GONE)
|
||||||
async def workplan_index(
|
async def workplan_index(
|
||||||
request: Request,
|
request: Request,
|
||||||
response: Response,
|
response: Response,
|
||||||
refresh: bool = Query(False, description="Force cache invalidation"),
|
refresh: bool = Query(False, description="Force cache invalidation"),
|
||||||
session: AsyncSession = Depends(get_session),
|
session: AsyncSession = Depends(get_session),
|
||||||
) -> dict[str, Any]:
|
) -> None:
|
||||||
|
replacement_ref = "/workplans/index"
|
||||||
await _meter_legacy_route(
|
await _meter_legacy_route(
|
||||||
session=session,
|
session=session,
|
||||||
request=request,
|
request=request,
|
||||||
response=response,
|
response=response,
|
||||||
interface_key=_legacy_key("GET", "/workstreams/workplan-index"),
|
interface_key=_legacy_key("GET", "/workstreams/workplan-index"),
|
||||||
replacement_ref="/workplans/index",
|
replacement_ref=replacement_ref,
|
||||||
|
)
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_410_GONE,
|
||||||
|
detail="Legacy GET /workstreams/workplan-index retired; use GET /workplans/index",
|
||||||
|
headers=legacy_response_headers(replacement_ref),
|
||||||
)
|
)
|
||||||
return await _workplan_index(refresh=refresh, session=session)
|
|
||||||
|
|
||||||
|
|
||||||
@workplan_router.get("/index")
|
@workplan_router.get("/index")
|
||||||
|
|
|
||||||
51
docs/evidence/railiance01-state-hub-deploy-20260709.md
Normal file
51
docs/evidence/railiance01-state-hub-deploy-20260709.md
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
# railiance01 State Hub deploy — workstream caller migration
|
||||||
|
|
||||||
|
Date: 2026-07-09
|
||||||
|
Workplan: STATE-WP-0070 T01
|
||||||
|
Blocked: `git pull` on railiance01 failed with `.git/FETCH_HEAD` permission denied.
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
Legacy-meter shows ~150k weekly `GET /workstreams/*` calls. Local migrations
|
||||||
|
(≥ `b05ca2c`) moved fix-consistency and activity-core to `/workplans/`. The
|
||||||
|
railiance01 sweep host was still at `050cbbc` when checked via SSH.
|
||||||
|
|
||||||
|
## Target revisions
|
||||||
|
|
||||||
|
| Repo | Minimum commit | Notes |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `state-hub` | `7e68f9e` | Caller migrations + identity headers + workplan-index 410 |
|
||||||
|
| `activity-core` | `5a92923` | `/workplans` resolver + progress `workplan_id` only |
|
||||||
|
|
||||||
|
## Steps (operator on railiance01)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Fix checkout permissions if needed (example — adjust user/group)
|
||||||
|
sudo chown -R "$(whoami):$(id -gn)" ~/state-hub/.git ~/activity-core/.git
|
||||||
|
|
||||||
|
# 2. Pull both repos
|
||||||
|
cd ~/state-hub && git fetch origin main && git pull --ff-only origin main
|
||||||
|
git log -1 --oneline # expect 7e68f9e or newer
|
||||||
|
|
||||||
|
cd ~/activity-core && git fetch origin main && git pull --ff-only origin main
|
||||||
|
git log -1 --oneline # expect 5a92923 or newer
|
||||||
|
|
||||||
|
# 3. Optional: one-shot consistency sweep against workstation hub via tunnel
|
||||||
|
# (API must reach the hub — typically http://127.0.0.1:8000 through ops-bridge)
|
||||||
|
statehub fix-consistency --all --remote --api-base http://127.0.0.1:8000
|
||||||
|
|
||||||
|
# 4. Verify legacy-meter attribution (from any host with hub API access)
|
||||||
|
python ~/state-hub/scripts/capture_legacy_meter_evidence.py --days 7
|
||||||
|
# Expect component bucket state-hub.fix-consistency; declining GET /workstreams/*
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verification checklist
|
||||||
|
|
||||||
|
- [ ] `state-hub` and `activity-core` at target commits on railiance01
|
||||||
|
- [ ] Consistency sweep completes without C-00 automation errors
|
||||||
|
- [ ] Weekly legacy-meter review shows `state-hub.fix-consistency` (not only `unknown`)
|
||||||
|
- [ ] `GET /workstreams/*` 7-day window counts trend down week-over-week
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
`git checkout <previous-sha>` in each repo; no schema migration involved.
|
||||||
|
|
@ -47,7 +47,7 @@ Risk order: REST > MCP > events > dashboard prose > internal identifiers.
|
||||||
| 1 | `rest_api:GET /workstreams/{workstream_id}` | `GET /workplans/{workplan_id}` | REST | T04 |
|
| 1 | `rest_api:GET /workstreams/{workstream_id}` | `GET /workplans/{workplan_id}` | REST | T04 |
|
||||||
| 1 | `rest_api:PATCH /workstreams/{workstream_id}` | `PATCH /workplans/{workplan_id}` | REST | T04 |
|
| 1 | `rest_api:PATCH /workstreams/{workstream_id}` | `PATCH /workplans/{workplan_id}` | REST | T04 |
|
||||||
| 1 | ~~`rest_api:DELETE /workstreams/{workstream_id}`~~ **retired** (410) | `DELETE /workplans/{workplan_id}` | REST | T04 ✓ |
|
| 1 | ~~`rest_api:DELETE /workstreams/{workstream_id}`~~ **retired** (410) | `DELETE /workplans/{workplan_id}` | REST | T04 ✓ |
|
||||||
| 1 | `rest_api:GET /workstreams/workplan-index` | `GET /workplans/index` | REST | T04 |
|
| 1 | ~~`rest_api:GET /workstreams/workplan-index`~~ **retired** (410) | `GET /workplans/index` | REST | T04 ✓ |
|
||||||
| 2 | `rest_api:GET /workstreams/{workstream_id}/dependencies/` | `GET /workplans/{workplan_id}/dependencies/` | REST | T04 |
|
| 2 | `rest_api:GET /workstreams/{workstream_id}/dependencies/` | `GET /workplans/{workplan_id}/dependencies/` | REST | T04 |
|
||||||
| 2 | `rest_api:POST /workstreams/{workstream_id}/dependencies/` | `POST /workplans/{workplan_id}/dependencies/` | REST | T04 |
|
| 2 | `rest_api:POST /workstreams/{workstream_id}/dependencies/` | `POST /workplans/{workplan_id}/dependencies/` | REST | T04 |
|
||||||
| 2 | `rest_api:DELETE /workstreams/{workstream_id}/dependencies/{dep_id}` | `DELETE /workplans/{workplan_id}/dependencies/{dep_id}` | REST | T04 |
|
| 2 | `rest_api:DELETE /workstreams/{workstream_id}/dependencies/{dep_id}` | `DELETE /workplans/{workplan_id}/dependencies/{dep_id}` | REST | T04 |
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ Physical database renames are intentionally out of scope for this workplan.
|
||||||
| Read workplan | `GET /workplans/{workplan_id}` | `GET /workstreams/{workstream_id}` | `rest_api:GET /workstreams/{workstream_id}` |
|
| Read workplan | `GET /workplans/{workplan_id}` | `GET /workstreams/{workstream_id}` | `rest_api:GET /workstreams/{workstream_id}` |
|
||||||
| Update workplan | `PATCH /workplans/{workplan_id}` | `PATCH /workstreams/{workstream_id}` | `rest_api:PATCH /workstreams/{workstream_id}` |
|
| Update workplan | `PATCH /workplans/{workplan_id}` | `PATCH /workstreams/{workstream_id}` | `rest_api:PATCH /workstreams/{workstream_id}` |
|
||||||
| Archive workplan | `DELETE /workplans/{workplan_id}` | ~~`DELETE /workstreams/{workstream_id}`~~ **410 Gone** (2026-07-08) | `rest_api:DELETE /workstreams/{workstream_id}` (retired) |
|
| Archive workplan | `DELETE /workplans/{workplan_id}` | ~~`DELETE /workstreams/{workstream_id}`~~ **410 Gone** (2026-07-08) | `rest_api:DELETE /workstreams/{workstream_id}` (retired) |
|
||||||
| Workplan index | `GET /workplans/index` | `GET /workstreams/workplan-index` | `rest_api:GET /workstreams/workplan-index` |
|
| Workplan index | `GET /workplans/index` | ~~`GET /workstreams/workplan-index`~~ **410 Gone** (2026-07-09) | `rest_api:GET /workstreams/workplan-index` (retired) |
|
||||||
| Workplan dependencies | `GET/POST /workplans/{workplan_id}/dependencies/` | `GET/POST /workstreams/{workstream_id}/dependencies/` | matching `rest_api:* /workstreams/...` keys |
|
| Workplan dependencies | `GET/POST /workplans/{workplan_id}/dependencies/` | `GET/POST /workstreams/{workstream_id}/dependencies/` | matching `rest_api:* /workstreams/...` keys |
|
||||||
| Delete dependency | `DELETE /workplans/{workplan_id}/dependencies/{dep_id}` | `DELETE /workstreams/{workstream_id}/dependencies/{dep_id}` | `rest_api:DELETE /workstreams/{workstream_id}/dependencies/{dep_id}` |
|
| Delete dependency | `DELETE /workplans/{workplan_id}/dependencies/{dep_id}` | `DELETE /workstreams/{workstream_id}/dependencies/{dep_id}` | `rest_api:DELETE /workstreams/{workstream_id}/dependencies/{dep_id}` |
|
||||||
| Execution intent | `PATCH /execution/workplans/{workplan_id}/intent` | `PATCH /execution/workstreams/{workstream_id}/intent` | `rest_api:PATCH /execution/workstreams/{workstream_id}/intent` |
|
| Execution intent | `PATCH /execution/workplans/{workplan_id}/intent` | `PATCH /execution/workstreams/{workstream_id}/intent` | `rest_api:PATCH /execution/workstreams/{workstream_id}/intent` |
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,20 @@ class TestWorkplanAliasesAndLegacyMeter:
|
||||||
}
|
}
|
||||||
assert items.get("event_subject:org.statehub.workstream.completed") is None
|
assert items.get("event_subject:org.statehub.workstream.completed") is None
|
||||||
|
|
||||||
|
async def test_legacy_workplan_index_returns_410(self, client):
|
||||||
|
r = await client.get(
|
||||||
|
"/workstreams/workplan-index",
|
||||||
|
headers={"X-StateHub-Component": "old-index-client"},
|
||||||
|
)
|
||||||
|
assert r.status_code == 410
|
||||||
|
assert "retired" in r.json()["detail"]
|
||||||
|
assert r.headers["X-StateHub-Replacement"] == "/workplans/index"
|
||||||
|
|
||||||
|
summary = (await client.get("/legacy-meter/summary")).json()
|
||||||
|
item = _summary_by_key(summary)["rest_api:GET /workstreams/workplan-index"]
|
||||||
|
assert item["window"]["calls"] == 1
|
||||||
|
assert item["window"]["components"] == {"old-index-client": 1}
|
||||||
|
|
||||||
async def test_legacy_delete_workstream_returns_410(self, client):
|
async def test_legacy_delete_workstream_returns_410(self, client):
|
||||||
await _create_domain(client)
|
await _create_domain(client)
|
||||||
topic = await _create_topic(client)
|
topic = await _create_topic(client)
|
||||||
|
|
|
||||||
|
|
@ -189,8 +189,12 @@ class TestWorkstreams:
|
||||||
|
|
||||||
async def test_workplan_index_route(self, client):
|
async def test_workplan_index_route(self, client):
|
||||||
r = await client.get("/workstreams/workplan-index")
|
r = await client.get("/workstreams/workplan-index")
|
||||||
|
assert r.status_code == 410
|
||||||
|
assert r.headers["X-StateHub-Replacement"] == "/workplans/index"
|
||||||
|
|
||||||
|
r = await client.get("/workplans/index")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert "workstreams" in r.json()
|
assert "workplans" in r.json()
|
||||||
|
|
||||||
async def test_workplan_bindings_sync_populates_index(self, client, tmp_path):
|
async def test_workplan_bindings_sync_populates_index(self, client, tmp_path):
|
||||||
await _create_domain(client)
|
await _create_domain(client)
|
||||||
|
|
|
||||||
|
|
@ -57,13 +57,14 @@ Progress 2026-07-09: identity headers landed on fix-consistency, custodian_cli,
|
||||||
dashboard loaders, cleanup_stale_tasks, validate_repo_adr. Local sweep attributes
|
dashboard loaders, cleanup_stale_tasks, validate_repo_adr. Local sweep attributes
|
||||||
`state-hub.fix-consistency` on task-create metering. railiance01 still at
|
`state-hub.fix-consistency` on task-create metering. railiance01 still at
|
||||||
`050cbbc`; `git pull` blocked (`.git/FETCH_HEAD` permission denied) — needs
|
`050cbbc`; `git pull` blocked (`.git/FETCH_HEAD` permission denied) — needs
|
||||||
operator deploy.
|
operator deploy. Hub message posted to `ops-warden` (2026-07-09); runbook at
|
||||||
|
`docs/evidence/railiance01-state-hub-deploy-20260709.md`.
|
||||||
|
|
||||||
## Task: Retire remaining REST `/workstreams` routes per key
|
## Task: Retire remaining REST `/workstreams` routes per key
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: STATE-WP-0070-T02
|
id: STATE-WP-0070-T02
|
||||||
status: todo
|
status: progress
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "13122195-3b7e-4e22-a5e8-4ebe7e9a093f"
|
state_hub_task_id: "13122195-3b7e-4e22-a5e8-4ebe7e9a093f"
|
||||||
```
|
```
|
||||||
|
|
@ -77,6 +78,10 @@ still showing usage:
|
||||||
Return **410 Gone** (matching DELETE pattern) when legacy-meter shows seven
|
Return **410 Gone** (matching DELETE pattern) when legacy-meter shows seven
|
||||||
consecutive zero-usage windows. Update tests and capture evidence per retirement.
|
consecutive zero-usage windows. Update tests and capture evidence per retirement.
|
||||||
|
|
||||||
|
Progress 2026-07-09: `GET /workstreams/workplan-index` → **410 Gone** (zero-usage
|
||||||
|
candidate in 1-day capture). `DELETE /workstreams/{id}` already 410 from STATE-WP-0069.
|
||||||
|
Next candidates when meter clears: `POST .../dependencies/` (0 calls in 1-day window).
|
||||||
|
|
||||||
## Task: MCP alias removal (Phase 2)
|
## Task: MCP alias removal (Phase 2)
|
||||||
|
|
||||||
```task
|
```task
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue