generated from coulomb/repo-seed
feat(classification-spine): implement STATE-WP-0065 repo-anchored model
Replace the ad-hoc coordination-domain spine with the Repo Classification Standard: 14 market domains, classification columns on managed_repos, and workplans anchored by repo_id (topic_id optional). - Add Alembic migration d8e9f0a1b2c3 with data backfill and workstream→workplan rename - Add api/classification.py validation and register-from-classification tooling - Expose workplan-first REST/MCP surface with legacy workstream aliases - Add C-24 consistency rule and legacy domain frontmatter mapping - Update dashboard repos page with category/capability/stake filters - Update orientation docs; mark STATE-WP-0065 finished
This commit is contained in:
@@ -19,15 +19,16 @@ async def _call_tool(name: str, arguments: dict[str, Any]) -> dict[str, Any]:
|
||||
|
||||
|
||||
class TestMCPWriteTools:
|
||||
async def test_create_workstream_returns_rest_shape_and_emits_progress(self, monkeypatch):
|
||||
async def test_create_workplan_returns_rest_shape_and_emits_progress(self, monkeypatch):
|
||||
calls: list[tuple[str, dict[str, Any]]] = []
|
||||
|
||||
def fake_post(path: str, body: dict[str, Any]) -> dict[str, Any]:
|
||||
calls.append((path, body))
|
||||
if path == "/workstreams":
|
||||
if path == "/workplans":
|
||||
return {
|
||||
"id": "ws-1",
|
||||
"topic_id": body["topic_id"],
|
||||
"id": "wp-1",
|
||||
"repo_id": body["repo_id"],
|
||||
"topic_id": body.get("topic_id"),
|
||||
"title": body["title"],
|
||||
"slug": body["slug"],
|
||||
"status": body["status"],
|
||||
@@ -39,20 +40,42 @@ class TestMCPWriteTools:
|
||||
monkeypatch.setattr(server, "_post", fake_post)
|
||||
|
||||
body = await _call_tool(
|
||||
"create_workstream",
|
||||
{"topic_id": "topic-1", "title": "MCP Reliable Write"},
|
||||
"create_workplan",
|
||||
{"repo_id": "repo-1", "topic_id": "topic-1", "title": "MCP Reliable Write"},
|
||||
)
|
||||
|
||||
assert body == {
|
||||
"id": "ws-1",
|
||||
"id": "wp-1",
|
||||
"repo_id": "repo-1",
|
||||
"topic_id": "topic-1",
|
||||
"title": "MCP Reliable Write",
|
||||
"slug": "mcp-reliable-write",
|
||||
"status": "active",
|
||||
}
|
||||
assert [path for path, _ in calls] == ["/workstreams", "/progress"]
|
||||
assert calls[1][1]["workstream_id"] == "ws-1"
|
||||
assert calls[1][1]["event_type"] == "workstream_created"
|
||||
assert [path for path, _ in calls] == ["/workplans", "/progress"]
|
||||
assert calls[1][1]["workplan_id"] == "wp-1"
|
||||
assert calls[1][1]["event_type"] == "workplan_created"
|
||||
|
||||
async def test_create_workstream_legacy_alias_uses_workplans_endpoint(self, monkeypatch):
|
||||
calls: list[tuple[str, dict[str, Any]]] = []
|
||||
|
||||
def fake_post(path: str, body: dict[str, Any]) -> dict[str, Any]:
|
||||
calls.append((path, body))
|
||||
if path == "/workplans":
|
||||
return {"id": "wp-1", "repo_id": body["repo_id"], "title": body["title"], "slug": body["slug"], "status": "active"}
|
||||
if path == "/progress":
|
||||
return {"id": "event-1", **body}
|
||||
raise AssertionError(f"unexpected POST {path}")
|
||||
|
||||
monkeypatch.setattr(server, "_post", fake_post)
|
||||
|
||||
body = await _call_tool(
|
||||
"create_workstream",
|
||||
{"repo_id": "repo-1", "title": "Legacy alias"},
|
||||
)
|
||||
|
||||
assert body["id"] == "wp-1"
|
||||
assert [path for path, _ in calls] == ["/workplans", "/progress"]
|
||||
|
||||
async def test_create_task_returns_rest_shape_and_emits_progress(self, monkeypatch):
|
||||
calls: list[tuple[str, dict[str, Any]]] = []
|
||||
@@ -62,7 +85,8 @@ class TestMCPWriteTools:
|
||||
if path == "/tasks":
|
||||
return {
|
||||
"id": "task-1",
|
||||
"workstream_id": body["workstream_id"],
|
||||
"workplan_id": body.get("workplan_id") or body.get("workstream_id"),
|
||||
"workstream_id": body.get("workplan_id") or body.get("workstream_id"),
|
||||
"title": body["title"],
|
||||
"priority": body["priority"],
|
||||
"status": "todo",
|
||||
@@ -80,6 +104,7 @@ class TestMCPWriteTools:
|
||||
|
||||
assert body == {
|
||||
"id": "task-1",
|
||||
"workplan_id": "ws-1",
|
||||
"workstream_id": "ws-1",
|
||||
"title": "MCP task",
|
||||
"priority": "high",
|
||||
@@ -266,18 +291,18 @@ class TestMCPWriteTools:
|
||||
|
||||
def fake_post(path: str, body: dict[str, Any]) -> dict[str, Any]:
|
||||
calls.append((path, body))
|
||||
return {"error": "API 422: invalid topic"}
|
||||
return {"error": "API 422: invalid repo"}
|
||||
|
||||
monkeypatch.setattr(server, "_post", fake_post)
|
||||
|
||||
body = await _call_tool(
|
||||
"create_workstream",
|
||||
{"topic_id": "bad-topic", "title": "No progress on failure"},
|
||||
{"repo_id": "bad-repo", "title": "No progress on failure"},
|
||||
)
|
||||
|
||||
assert body["tool"] == "create_workstream"
|
||||
assert body["error"] == "API 422: invalid topic"
|
||||
assert [path for path, _ in calls] == ["/workstreams"]
|
||||
assert body["error"] == "API 422: invalid repo"
|
||||
assert [path for path, _ in calls] == ["/workplans"]
|
||||
|
||||
async def test_record_decision_missing_id_is_clear_and_skips_progress(self, monkeypatch):
|
||||
calls: list[tuple[str, dict[str, Any]]] = []
|
||||
|
||||
Reference in New Issue
Block a user