STATE-WP-0070 T04 (partial): drop open_workstreams from /state/summary
Removes the redundant open_workstreams mirror field from StateSummary (schema,
router, MCP get_domain_summary reader, dashboard consumers + empty-state stub,
and tests). Consumers already preferred open_workplans, so this is the
low-risk half of T04.
Deferred (still have live callers — not yet retirement-ready):
- workstream_id query/body field alias on preferred routes — external
session-close curls/scripts fleet-wide still send it.
- flows/workstream.yaml — /flows/workstream/{id} routes are still served and
exercised by tests; retire only once no callers remain.
Staged on branch state-wp-0070-legacy-retirement — do not merge until the 7th
documented zero-usage window is captured.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ef62cb3872
commit
6d3de59436
8 changed files with 9 additions and 16 deletions
|
|
@ -199,7 +199,7 @@ async def build_state_summary(session: AsyncSession) -> StateSummary:
|
||||||
)
|
)
|
||||||
open_ws = list(open_ws_rows.scalars().all())
|
open_ws = list(open_ws_rows.scalars().all())
|
||||||
|
|
||||||
# Task counts per workplan (used to enrich open_workplans / open_workstreams)
|
# Task counts per workplan (used to enrich open_workplans)
|
||||||
task_per_ws: dict = {}
|
task_per_ws: dict = {}
|
||||||
task_statuses_per_ws: dict = {}
|
task_statuses_per_ws: dict = {}
|
||||||
for ws_id, tstat, cnt in await session.execute(
|
for ws_id, tstat, cnt in await session.execute(
|
||||||
|
|
@ -430,7 +430,6 @@ async def build_state_summary(session: AsyncSession) -> StateSummary:
|
||||||
open_capability_requests=open_cap_req_count,
|
open_capability_requests=open_cap_req_count,
|
||||||
ranked_suggestions=ranked_suggestions,
|
ranked_suggestions=ranked_suggestions,
|
||||||
open_workplans=open_workplan_rows,
|
open_workplans=open_workplan_rows,
|
||||||
open_workstreams=open_workplan_rows,
|
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
@ -793,7 +792,7 @@ async def _build_domain_summaries(session: AsyncSession) -> list[DomainSummary]:
|
||||||
async def get_deps(session: AsyncSession = Depends(get_session)) -> list[WorkstreamWithDeps]:
|
async def get_deps(session: AsyncSession = Depends(get_session)) -> list[WorkstreamWithDeps]:
|
||||||
"""Lightweight dep-graph endpoint: open workstreams with their dependency edges only.
|
"""Lightweight dep-graph endpoint: open workstreams with their dependency edges only.
|
||||||
|
|
||||||
Returns the same structure as open_workstreams in /state/summary but skips
|
Returns the same structure as open_workplans in /state/summary but skips
|
||||||
the 10-table full-summary computation. Task counts are omitted (all zero).
|
the 10-table full-summary computation. Task counts are omitted (all zero).
|
||||||
Used by workstreams.md and dependencies.md which only need dep edges.
|
Used by workstreams.md and dependencies.md which only need dep edges.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,6 @@ class StateSummary(BaseModel):
|
||||||
waiting_tasks: list[TaskRead]
|
waiting_tasks: list[TaskRead]
|
||||||
blocked_tasks: list[TaskRead] = []
|
blocked_tasks: list[TaskRead] = []
|
||||||
recent_progress: list[ProgressEventRead]
|
recent_progress: list[ProgressEventRead]
|
||||||
open_workstreams: list[WorkstreamWithDeps]
|
|
||||||
open_workplans: list[WorkstreamWithDeps] = []
|
open_workplans: list[WorkstreamWithDeps] = []
|
||||||
next_steps: list[NextStep] = []
|
next_steps: list[NextStep] = []
|
||||||
domains: list[DomainSummary] = []
|
domains: list[DomainSummary] = []
|
||||||
|
|
|
||||||
|
|
@ -38,5 +38,4 @@ except urllib.error.URLError as e:
|
||||||
"blocked_tasks": [],
|
"blocked_tasks": [],
|
||||||
"recent_progress": [],
|
"recent_progress": [],
|
||||||
"open_workplans": [],
|
"open_workplans": [],
|
||||||
"open_workstreams": [],
|
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -437,7 +437,7 @@ display(html`<div class="grid grid-cols-3" style="gap:1rem;margin-bottom:1.5rem"
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const waitingTasks = summary.waiting_tasks ?? summary.blocked_tasks ?? [];
|
const waitingTasks = summary.waiting_tasks ?? summary.blocked_tasks ?? [];
|
||||||
const wsById = Object.fromEntries((summary.open_workplans ?? summary.open_workstreams ?? []).map(w => [w.id, w]));
|
const wsById = Object.fromEntries((summary.open_workplans ?? []).map(w => [w.id, w]));
|
||||||
const todayCount = (summary.recent_progress ?? []).filter(e =>
|
const todayCount = (summary.recent_progress ?? []).filter(e =>
|
||||||
e.created_at?.startsWith(new Date().toISOString().slice(0, 10))).length;
|
e.created_at?.startsWith(new Date().toISOString().slice(0, 10))).length;
|
||||||
const decCount = (decisions.open ?? 0) + (decisions.escalated ?? 0);
|
const decCount = (decisions.open ?? 0) + (decisions.escalated ?? 0);
|
||||||
|
|
|
||||||
|
|
@ -263,9 +263,7 @@ def get_domain_summary(domain_slug: str) -> str:
|
||||||
topic_id = topic["id"]
|
topic_id = topic["id"]
|
||||||
|
|
||||||
state_summary = _get("/state/summary")
|
state_summary = _get("/state/summary")
|
||||||
open_workplans = state_summary.get("open_workplans") or state_summary.get(
|
open_workplans = state_summary.get("open_workplans", [])
|
||||||
"open_workstreams", []
|
|
||||||
)
|
|
||||||
workstreams = [ws for ws in open_workplans if ws.get("topic_id") == topic_id]
|
workstreams = [ws for ws in open_workplans if ws.get("topic_id") == topic_id]
|
||||||
blocking = _get("/decisions", {"decision_type": "pending", "topic_id": topic_id})
|
blocking = _get("/decisions", {"decision_type": "pending", "topic_id": topic_id})
|
||||||
recent = _get("/progress", {"topic_id": topic_id, "limit": 5})
|
recent = _get("/progress", {"topic_id": topic_id, "limit": 5})
|
||||||
|
|
|
||||||
|
|
@ -48,14 +48,14 @@ class TestGetStateSummary:
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
body = r.json()
|
body = r.json()
|
||||||
# Required top-level fields
|
# Required top-level fields
|
||||||
for key in ("open_workstreams", "blocking_decisions", "blocked_tasks",
|
for key in ("open_workplans", "blocking_decisions", "blocked_tasks",
|
||||||
"domains", "contribution_counts", "licence_risk_count"):
|
"domains", "contribution_counts", "licence_risk_count"):
|
||||||
assert key in body, f"missing key: {key}"
|
assert key in body, f"missing key: {key}"
|
||||||
|
|
||||||
async def test_empty_db_returns_zero_counts(self, client):
|
async def test_empty_db_returns_zero_counts(self, client):
|
||||||
r = await client.get("/state/summary")
|
r = await client.get("/state/summary")
|
||||||
body = r.json()
|
body = r.json()
|
||||||
assert body["open_workstreams"] == []
|
assert body["open_workplans"] == []
|
||||||
assert body["blocking_decisions"] == []
|
assert body["blocking_decisions"] == []
|
||||||
assert body["blocked_tasks"] == []
|
assert body["blocked_tasks"] == []
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -433,9 +433,7 @@ class TestStateSummary:
|
||||||
r = await client.get("/state/summary")
|
r = await client.get("/state/summary")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
body = r.json()
|
body = r.json()
|
||||||
assert "open_workstreams" in body
|
|
||||||
assert "open_workplans" in body
|
assert "open_workplans" in body
|
||||||
assert body["open_workplans"] == body["open_workstreams"]
|
|
||||||
assert "blocking_decisions" in body
|
assert "blocking_decisions" in body
|
||||||
assert "blocked_tasks" in body
|
assert "blocked_tasks" in body
|
||||||
assert "domains" in body
|
assert "domains" in body
|
||||||
|
|
@ -471,7 +469,7 @@ class TestStateSummary:
|
||||||
r = await client.get("/state/summary")
|
r = await client.get("/state/summary")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
body = r.json()
|
body = r.json()
|
||||||
summaries = {item["id"]: item for item in body["open_workstreams"]}
|
summaries = {item["id"]: item for item in body["open_workplans"]}
|
||||||
|
|
||||||
assert summaries[blocked_ws["id"]]["status"] == "blocked"
|
assert summaries[blocked_ws["id"]]["status"] == "blocked"
|
||||||
assert summaries[blocked_ws["id"]]["blocked_reasons"][0]["id"] == "dependencies.all_complete"
|
assert summaries[blocked_ws["id"]]["blocked_reasons"][0]["id"] == "dependencies.all_complete"
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ def test_summary_cache_unit_progress_section():
|
||||||
blocking_decisions=[],
|
blocking_decisions=[],
|
||||||
waiting_tasks=[],
|
waiting_tasks=[],
|
||||||
recent_progress=[],
|
recent_progress=[],
|
||||||
open_workstreams=[],
|
open_workplans=[],
|
||||||
)
|
)
|
||||||
cache.store(summary, rev)
|
cache.store(summary, rev)
|
||||||
|
|
||||||
|
|
@ -181,7 +181,7 @@ def test_invalidate_summary_cache_scopes():
|
||||||
blocking_decisions=[],
|
blocking_decisions=[],
|
||||||
waiting_tasks=[],
|
waiting_tasks=[],
|
||||||
recent_progress=[],
|
recent_progress=[],
|
||||||
open_workstreams=[],
|
open_workplans=[],
|
||||||
)
|
)
|
||||||
cache.store(summary, rev)
|
cache.store(summary, rev)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue