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(
|
||||
request: Request,
|
||||
response: Response,
|
||||
refresh: bool = Query(False, description="Force cache invalidation"),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
) -> dict[str, Any]:
|
||||
) -> None:
|
||||
replacement_ref = "/workplans/index"
|
||||
await _meter_legacy_route(
|
||||
session=session,
|
||||
request=request,
|
||||
response=response,
|
||||
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")
|
||||
|
|
|
|||
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: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: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: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 |
|
||||
|
|
|
|||
|
|
@ -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}` |
|
||||
| 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) |
|
||||
| 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 |
|
||||
| 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` |
|
||||
|
|
|
|||
|
|
@ -165,6 +165,20 @@ class TestWorkplanAliasesAndLegacyMeter:
|
|||
}
|
||||
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):
|
||||
await _create_domain(client)
|
||||
topic = await _create_topic(client)
|
||||
|
|
|
|||
|
|
@ -189,8 +189,12 @@ class TestWorkstreams:
|
|||
|
||||
async def test_workplan_index_route(self, client):
|
||||
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 "workstreams" in r.json()
|
||||
assert "workplans" in r.json()
|
||||
|
||||
async def test_workplan_bindings_sync_populates_index(self, client, tmp_path):
|
||||
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
|
||||
`state-hub.fix-consistency` on task-create metering. railiance01 still at
|
||||
`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
|
||||
id: STATE-WP-0070-T02
|
||||
status: todo
|
||||
status: progress
|
||||
priority: high
|
||||
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
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue