feat(capability-requests): add routing dispute & reroute workflow (CUST-WP-0027)

Adds a structured dispute mechanism when capability request routing is wrong:
- New `routing_disputed` status with four DB columns (dispute_reason, disputed_by,
  dispute_suggested_domain, disputed_at) via Alembic migration m0h1i2j3k4l5
- POST /capability-requests/{id}/dispute — any party can flag misrouting with a reason
  and optional suggested domain; notifies custodian + current fulfilling domain
- POST /capability-requests/{id}/reroute — custodian re-routes to correct domain via
  catalog_entry_id or direct slug; appends audit trail to routing_note; resets to requested
- Two new MCP tools: dispute_capability_routing and reroute_capability_request
- Dashboard: amber disputed-banner at top of Summary, routing_disputed Kanban column,
  dispute details (reason, suggested domain, raised-by) shown on disputed cards

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 23:58:52 +01:00
parent b6103d1f9f
commit b3a44fb4f3
6 changed files with 277 additions and 16 deletions

View File

@@ -1951,6 +1951,53 @@ def get_capability_request(request_id: str) -> str:
return json.dumps(_get(f"/capability-requests/{request_id}"), indent=2)
@mcp.tool()
def dispute_capability_routing(
request_id: str,
reason: str,
disputed_by: str,
suggested_domain: Optional[str] = None,
) -> str:
"""Flag a capability request routing as incorrect. Transitions to routing_disputed.
Args:
request_id: UUID of the capability request
reason: Why the routing is wrong
disputed_by: Agent raising the dispute (e.g. 'netkingdom-worker')
suggested_domain: The domain slug this should be routed to (optional)
"""
return json.dumps(_post(f"/capability-requests/{request_id}/dispute", {
"reason": reason,
"disputed_by": disputed_by,
"suggested_domain": suggested_domain,
}), indent=2)
@mcp.tool()
def reroute_capability_request(
request_id: str,
note: str,
rerouted_by: str,
domain: Optional[str] = None,
catalog_entry_id: Optional[str] = None,
) -> str:
"""Re-route a disputed capability request to a new domain. Resets to requested.
Args:
request_id: UUID of the capability request (must be routing_disputed)
note: Reason for the re-routing decision
rerouted_by: Agent performing the re-route (e.g. 'custodian')
domain: Target domain slug (used if catalog_entry_id not provided)
catalog_entry_id: Preferred — UUID of catalog entry; re-derives domain automatically
"""
return json.dumps(_post(f"/capability-requests/{request_id}/reroute", {
"note": note,
"rerouted_by": rerouted_by,
"domain": domain,
"catalog_entry_id": catalog_entry_id,
}), indent=2)
# ---------------------------------------------------------------------------
# Third-Party Services Catalog (TPSC)
# ---------------------------------------------------------------------------