feat(api): retire phase-1/2 /workstreams REST aliases with 410 stranglers
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Has been cancelled
Build and Publish Multi-Context Image / build-and-push (push) Successful in 2m18s

Add retire_legacy_route() helper and convert CRUD, dependency, and
execution intent legacy routes to 410 Gone while preserving final-call
legacy-meter attribution. Update tests, retirement backlog, and close
STATE-WP-0075 with handoff to STATE-WP-0070 T02 and STATE-WP-0073 T04.
This commit is contained in:
tegwick 2026-07-10 13:31:57 +02:00
parent c4ac853e35
commit 7036e2aa6b
11 changed files with 320 additions and 139 deletions

View file

@ -144,7 +144,7 @@ class TestWorkstreams:
topic = await _create_topic(client)
ws = await _create_workstream(client, topic["id"])
r = await client.get(f"/workstreams/?topic_id={topic['id']}")
r = await client.get(f"/workplans/?topic_id={topic['id']}")
assert r.status_code == 200
ids = [w["id"] for w in r.json()]
assert ws["id"] in ids
@ -154,7 +154,7 @@ class TestWorkstreams:
topic = await _create_topic(client)
ws = await _create_workstream(client, topic["id"])
r = await client.patch(f"/workstreams/{ws['id']}", json={"status": "finished"})
r = await client.patch(f"/workplans/{ws['id']}", json={"status": "finished"})
assert r.status_code == 200
assert r.json()["status"] == "finished"
@ -163,7 +163,7 @@ class TestWorkstreams:
topic = await _create_topic(client)
ws = await _create_workstream(client, topic["id"])
r = await client.patch(f"/workstreams/{ws['id']}", json={"status": "completed"})
r = await client.patch(f"/workplans/{ws['id']}", json={"status": "completed"})
assert r.status_code == 200
assert r.json()["status"] == "finished"
@ -171,9 +171,9 @@ class TestWorkstreams:
await _create_domain(client)
topic = await _create_topic(client)
ws = await _create_workstream(client, topic["id"])
await client.patch(f"/workstreams/{ws['id']}", json={"owner": "alice"})
await client.patch(f"/workplans/{ws['id']}", json={"owner": "alice"})
r = await client.get("/workstreams/?owner=alice")
r = await client.get("/workplans/?owner=alice")
assert r.status_code == 200
assert len(r.json()) == 1
assert r.json()[0]["id"] == ws["id"]
@ -183,7 +183,7 @@ class TestWorkstreams:
topic = await _create_topic(client)
ws = await _create_workstream(client, topic["id"], slug="my-special-ws")
r = await client.get("/workstreams/?slug=my-special-ws")
r = await client.get("/workplans/?slug=my-special-ws")
assert r.status_code == 200
assert len(r.json()) == 1
@ -358,7 +358,7 @@ class TestTasks:
r = await client.patch(f"/tasks/{task['id']}", json={"status": "progress"})
assert r.status_code == 200
r = await client.get(f"/workstreams/{ws['id']}")
r = await client.get(f"/workplans/{ws['id']}")
assert r.status_code == 200
assert r.json()["status"] == "active"
@ -371,7 +371,7 @@ class TestTasks:
r = await client.patch(f"/tasks/{task['id']}", json={"status": "progress"})
assert r.status_code == 200
r = await client.get(f"/workstreams/{ws['id']}")
r = await client.get(f"/workplans/{ws['id']}")
assert r.status_code == 200
assert r.json()["status"] == "blocked"
@ -460,7 +460,7 @@ class TestStateSummary:
dependency_ws = await _create_workstream(client, topic["id"], slug="dependency-ws")
r = await client.post(
f"/workstreams/{blocked_ws['id']}/dependencies/",
f"/workplans/{blocked_ws['id']}/dependencies/",
json={
"to_workstream_id": dependency_ws["id"],
"description": "Blocked until dependency completes",
@ -543,7 +543,7 @@ class TestFlowEndpoints:
assert r.status_code == 200
assert r.json()["current_workstation"] == "finished"
r = await client.get(f"/workstreams/{ws['id']}")
r = await client.get(f"/workplans/{ws['id']}")
assert r.json()["status"] == "finished"
async def test_advance_workstream_respects_current_exit_assertions(self, client):
@ -554,7 +554,7 @@ class TestFlowEndpoints:
task = await _create_task(client, ws["id"])
await client.patch(f"/tasks/{task['id']}", json={"status": "done"})
await client.post(
f"/workstreams/{ws['id']}/dependencies/",
f"/workplans/{ws['id']}/dependencies/",
json={
"to_workstream_id": dependency_ws["id"],
"description": "Dependency must finish first",
@ -565,7 +565,7 @@ class TestFlowEndpoints:
assert r.status_code == 409
assert r.json()["detail"]["blocking_assertions"][0]["id"] == "dependencies.all_complete"
r = await client.get(f"/workstreams/{ws['id']}")
r = await client.get(f"/workplans/{ws['id']}")
assert r.json()["status"] == "active"
@ -677,7 +677,7 @@ class TestReconciliationEndpoints:
assert body["workplan_path"] == "workplans/STATE-WP-9999-demo.md"
assert "status: backlog" in wp.read_text(encoding="utf-8")
r = await client.get(f"/workstreams/{ws['id']}")
r = await client.get(f"/workplans/{ws['id']}")
assert r.json()["status"] == "backlog"
async def test_apply_workstream_without_file_does_not_mutate_db(self, client):
@ -700,7 +700,7 @@ class TestReconciliationEndpoints:
assert body["write_through_result"] == "not_applicable"
assert body["reconciliation_record_id"]
r = await client.get(f"/workstreams/{ws['id']}")
r = await client.get(f"/workplans/{ws['id']}")
assert r.json()["status"] == "active"
r = await client.get("/messages/?to_agent=state-hub&unread_only=true")
@ -805,7 +805,7 @@ class TestReconciliationEndpoints:
assert "status: active" in text
assert "status: progress" in text
r = await client.get(f"/workstreams/{ws['id']}")
r = await client.get(f"/workplans/{ws['id']}")
assert r.json()["status"] == "active"
r = await client.get(f"/tasks/{task['id']}")
@ -883,7 +883,7 @@ class TestReconciliationEndpoints:
"---\n",
encoding="utf-8",
)
await client.patch(f"/workstreams/{ws['id']}", json={"status": "ready"})
await client.patch(f"/workplans/{ws['id']}", json={"status": "ready"})
r = await client.post("/reconciliation/state-change", json={
"target_type": "workstream",
@ -901,7 +901,7 @@ class TestReconciliationEndpoints:
assert "expected" in body["reason"]
assert "status: active" in wp.read_text(encoding="utf-8")
r = await client.get(f"/workstreams/{ws['id']}")
r = await client.get(f"/workplans/{ws['id']}")
assert r.json()["status"] == "ready"
async def test_apply_workstream_file_status_drift_creates_conflict_message(self, client, tmp_path):
@ -941,7 +941,7 @@ class TestReconciliationEndpoints:
assert "differs from cached DB status" in body["reason"]
assert "status: ready" in wp.read_text(encoding="utf-8")
r = await client.get(f"/workstreams/{ws['id']}")
r = await client.get(f"/workplans/{ws['id']}")
assert r.json()["status"] == "active"
async def test_apply_task_unavailable_host_path_creates_conflict_message(self, client, tmp_path):
@ -987,7 +987,7 @@ class TestExecutionQueueEndpoints:
topic = await _create_topic(client)
ws = await _create_workstream(client, topic["id"], status="ready")
r = await client.patch(f"/execution/workstreams/{ws['id']}/intent", json={
r = await client.patch(f"/execution/workplans/{ws['id']}/intent", json={
"execution_state": "queued",
"launch_mode": "queued",
"concurrency_mode": "parallel",
@ -1002,7 +1002,7 @@ class TestExecutionQueueEndpoints:
assert body["concurrency_mode"] == "parallel"
assert body["queue_rank"] == 7
r = await client.get(f"/workstreams/{ws['id']}")
r = await client.get(f"/workplans/{ws['id']}")
assert r.json()["status"] == "ready"
assert r.json()["execution_state"] == "queued"
@ -1041,23 +1041,23 @@ class TestExecutionQueueEndpoints:
planning_priority="low",
planning_order=3,
)
await client.patch(f"/execution/workstreams/{queued['id']}/intent", json={
await client.patch(f"/execution/workplans/{queued['id']}/intent", json={
"execution_state": "queued",
"launch_mode": "queued",
"queue_rank": 2,
})
await client.patch(f"/execution/workstreams/{blocked['id']}/intent", json={
await client.patch(f"/execution/workplans/{blocked['id']}/intent", json={
"execution_state": "queued",
"launch_mode": "queued",
"queue_rank": 1,
})
await client.patch(f"/execution/workstreams/{lifecycle_blocked['id']}/intent", json={
await client.patch(f"/execution/workplans/{lifecycle_blocked['id']}/intent", json={
"execution_state": "queued",
"launch_mode": "queued",
"queue_rank": 0,
})
await client.post(
f"/workstreams/{blocked['id']}/dependencies/",
f"/workplans/{blocked['id']}/dependencies/",
json={"to_workstream_id": dependency["id"], "description": "wait"},
)
@ -1096,7 +1096,7 @@ class TestExecutionQueueEndpoints:
assert body["immediate_pickup"] is True
assert body["status"] == "requested"
r = await client.get(f"/workstreams/{ws['id']}")
r = await client.get(f"/workplans/{ws['id']}")
updated = r.json()
assert updated["status"] == "ready"
assert updated["execution_state"] == "launching"
@ -1508,7 +1508,7 @@ class TestFabricGraphReadModel:
after_progress = await client.get("/progress/")
assert len(after_progress.json()) == before_progress_count
r = await client.get(f"/workstreams/{ws['id']}")
r = await client.get(f"/workplans/{ws['id']}")
assert r.json()["status"] == "ready"
r = await client.get(f"/tasks/{task['id']}")
assert r.json()["status"] == "todo"