feat(FN-2724): add interactive task routing controls

- Replace the static routing panel with a dedicated Routing tab that shows effective node, source, and policy details
- Add per-task node override selection with optimistic save/rollback, clear override action, and in-progress lock messaging
- Tighten PATCH /tasks/:id nodeId validation (empty/non-string rejection) while preserving node override conflict enforcement
- Update dashboard route and modal tests, including new RoutingTab coverage and tab-order assertions
This commit is contained in:
Fusion
2026-04-28 10:02:35 -07:00
committed by gsxdsm
parent d52add6f8c
commit f3b098cc15
8 changed files with 556 additions and 70 deletions

View File

@@ -3127,6 +3127,26 @@ describe("PATCH /tasks/:id", () => {
expect(store.updateTask).not.toHaveBeenCalled();
});
it("returns 400 for empty nodeId string", async () => {
const res = await REQUEST(buildApp(), "PATCH", "/api/tasks/KB-001", JSON.stringify({ nodeId: "" }), {
"Content-Type": "application/json",
});
expect(res.status).toBe(400);
expect(res.body.error).toContain("nodeId must be a non-empty string");
expect(store.updateTask).not.toHaveBeenCalled();
});
it("returns 400 for non-string nodeId", async () => {
const res = await REQUEST(buildApp(), "PATCH", "/api/tasks/KB-001", JSON.stringify({ nodeId: 123 }), {
"Content-Type": "application/json",
});
expect(res.status).toBe(400);
expect(res.body.error).toContain("nodeId must be a string or null");
expect(store.updateTask).not.toHaveBeenCalled();
});
it("forwards sourceIssue object updates to store.updateTask", async () => {
const sourceIssue = {
provider: "github",