feat(legacy-meter): meter MCP aliases and fix brief workstream_id label (STATE-WP-0070 T03/T04)
T03: MCP alias retirement gate was unstartable — deprecation notices existed
but no mcp:* interfaces were ever registered or metered. Add
_meter_legacy_mcp() posting /legacy-meter/usage (kind mcp_tool, component
state-hub.mcp) from create_workstream, list_workstreams, update_workstream,
update_workstream_status, and state://workstreams/{topic_slug}; register all
five phase-3 backlog keys so zero-window streaks accrue from today.
T04: trace residual POST /progress/ workstream_id (3 calls/8h, unknown) to
.custodian-brief.md labelling workplans "workstream_id:"; brief generator now
prints "workplan_id:". Briefs refresh on each repo's next fix-consistency run.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2943251c03
commit
b2640a1591
4 changed files with 58 additions and 1 deletions
|
|
@ -57,6 +57,10 @@ Risk order: REST > MCP > events > dashboard prose > internal identifiers.
|
|||
| 3 | `mcp:update_workstream_status` | `update_workplan_status` | MCP | T03 |
|
||||
| 3 | `mcp:list_workstreams` | `list_workplans` | MCP | T03 |
|
||||
| 3 | `state://workstreams/{topic_slug}` | `state://workplans/{topic_slug}` (proposed) | MCP resource | T03 |
|
||||
|
||||
Phase-3 MCP keys metered + registered 2026-07-10 (STATE-WP-0070 T03):
|
||||
`_meter_legacy_mcp()` records usage from the alias tools/resource; zero-window
|
||||
streaks accrue from registration.
|
||||
| 4 | ~~`event_subject:org.statehub.workstream.completed`~~ **retired** | `org.statehub.workplan.completed` | Event | T05 ✓ |
|
||||
| 5 | Dashboard nav label `Workstreams` | `Workplans` (URL compat retained) | Prose | T02 |
|
||||
| 5 | `dashboard/src/index.md` user-facing copy | workplan-first strings | Prose | T02 |
|
||||
|
|
|
|||
|
|
@ -219,6 +219,38 @@ def _legacy_mcp_deprecation(*, tool: str | None = None, resource: str | None = N
|
|||
raise ValueError("tool or resource required")
|
||||
|
||||
|
||||
def _meter_legacy_mcp(*, tool: str | None = None, resource: str | None = None) -> None:
|
||||
"""Record legacy MCP alias usage on the legacy meter (best-effort).
|
||||
|
||||
Keys match docs/workplan-terminology-legacy-retirement-backlog.md phase 3
|
||||
(`mcp:<tool_name>` / resource URI). Failures never surface to the caller —
|
||||
the meter gates retirement, it must not break the aliased call itself.
|
||||
"""
|
||||
if tool:
|
||||
key = f"mcp:{tool}"
|
||||
replacement = _LEGACY_MCP_TOOL_REPLACEMENTS[tool]
|
||||
elif resource:
|
||||
key = resource
|
||||
replacement = _LEGACY_MCP_RESOURCE_REPLACEMENTS[resource]
|
||||
else:
|
||||
raise ValueError("tool or resource required")
|
||||
try:
|
||||
with _client() as c:
|
||||
c.post(
|
||||
"/legacy-meter/usage",
|
||||
json={
|
||||
"interface_key": key,
|
||||
"interface_kind": "mcp_tool",
|
||||
"replacement_ref": replacement,
|
||||
"owner_component": "state-hub.mcp",
|
||||
"replacement_verified": True,
|
||||
"component_key": "state-hub.mcp",
|
||||
},
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _attach_legacy_deprecation(tool_name: str, result_json: str) -> str:
|
||||
if tool_name not in _LEGACY_MCP_TOOL_REPLACEMENTS:
|
||||
return result_json
|
||||
|
|
@ -272,6 +304,7 @@ def resource_workplans(topic_slug: str) -> str:
|
|||
@mcp.resource("state://workstreams/{topic_slug}")
|
||||
def resource_workstreams(topic_slug: str) -> str:
|
||||
"""Legacy resource alias — prefer state://workplans/{topic_slug}."""
|
||||
_meter_legacy_mcp(resource="state://workstreams/{topic_slug}")
|
||||
topics = _get("/topics", {"status": "active"})
|
||||
match = next((t for t in topics if t["slug"] == topic_slug), None)
|
||||
if not match:
|
||||
|
|
@ -802,6 +835,7 @@ def create_workstream(
|
|||
planning_order: int | None = None,
|
||||
) -> str:
|
||||
"""DEPRECATED legacy alias — use create_workplan(repo_id=...) instead."""
|
||||
_meter_legacy_mcp(tool="create_workstream")
|
||||
if not repo_id:
|
||||
return _json_result(_mcp_error("create_workstream", "repo_id is required"))
|
||||
return _create_workplan_impl(
|
||||
|
|
@ -1219,6 +1253,7 @@ def list_workstreams(
|
|||
slug: str | None = None,
|
||||
) -> str:
|
||||
"""DEPRECATED legacy alias — use list_workplans instead."""
|
||||
_meter_legacy_mcp(tool="list_workstreams")
|
||||
return _attach_legacy_deprecation(
|
||||
"list_workstreams",
|
||||
list_workplans(
|
||||
|
|
@ -1249,6 +1284,7 @@ def update_workstream_status(
|
|||
workstream_id: str | None = None,
|
||||
) -> str:
|
||||
"""DEPRECATED legacy alias — use update_workplan_status instead."""
|
||||
_meter_legacy_mcp(tool="update_workstream_status")
|
||||
parent_id = workplan_id or workstream_id
|
||||
if not parent_id:
|
||||
return _json_result(_mcp_error("update_workstream_status", "workplan_id is required"))
|
||||
|
|
@ -1289,6 +1325,7 @@ def update_workstream(
|
|||
status: str | None = None,
|
||||
) -> str:
|
||||
"""DEPRECATED legacy alias — use update_workplan instead."""
|
||||
_meter_legacy_mcp(tool="update_workstream")
|
||||
parent_id = workplan_id or workstream_id
|
||||
if not parent_id:
|
||||
return _json_result(_mcp_error("update_workstream", "workplan_id is required"))
|
||||
|
|
|
|||
|
|
@ -2099,7 +2099,7 @@ def _write_custodian_brief(api_base: str, repo_slug: str, repo_path: str) -> boo
|
|||
lines += [
|
||||
"",
|
||||
f"### {ws_title}",
|
||||
f"Progress: {pct} done | workstream_id: `{ws_id}`",
|
||||
f"Progress: {pct} done | workplan_id: `{ws_id}`",
|
||||
]
|
||||
|
||||
if open_tasks:
|
||||
|
|
|
|||
|
|
@ -110,6 +110,15 @@ Remove `create_workstream`, `update_workstream`, `list_workstreams`,
|
|||
`update_workstream_status`, and `state://workstreams/{topic_slug}` when MCP
|
||||
procedure keys hit zero usage for seven windows. Keep `TOOLS.md` migration notes.
|
||||
|
||||
Progress 2026-07-10: gate was unstartable — MCP alias usage was never metered
|
||||
(deprecation notices only, no `mcp:*` interfaces registered). Fixed:
|
||||
`_meter_legacy_mcp()` in `mcp_server/server.py` posts `/legacy-meter/usage`
|
||||
(kind `mcp_tool`, component `state-hub.mcp`) from all four legacy tools and the
|
||||
legacy resource; all five backlog phase-3 keys registered on the meter, so
|
||||
zero-window streaks accrue from 2026-07-10. Note: `mcp:list_workstreams` shows
|
||||
1 verification call in the 2026-07-10 window — streak counts from the next
|
||||
window. Removal remains gated on 7 consecutive zero windows.
|
||||
|
||||
## Task: Internal dual-key and param alias cleanup
|
||||
|
||||
```task
|
||||
|
|
@ -123,6 +132,13 @@ state_hub_task_id: "18af98bf-c74e-47c8-948a-e713cff88bb8"
|
|||
- Remove `workstream_id` query/body aliases on preferred routes.
|
||||
- Retire `flows/workstream.yaml` when no callers remain.
|
||||
|
||||
Progress 2026-07-10: residual `POST /progress/ workstream_id` (3 calls/8h,
|
||||
`unknown`) traced to `.custodian-brief.md` files labelling workplans
|
||||
`workstream_id:` — agents copy the field name into session-close curl posts.
|
||||
Brief generator fixed (`consistency_check.py` → `workplan_id:`); briefs
|
||||
regenerate on each repo's next fix-consistency sweep. Expect this key to decay
|
||||
to zero once briefs refresh fleet-wide.
|
||||
|
||||
## Task: Closeout and fleet gate
|
||||
|
||||
```task
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue