fix(FN-3716): stabilize verification and role policy checks

- Add shared agent role policy helpers for implementation task detection and executor-role enforcement
- Provide a standardized role-mismatch error formatter for delegation and assignment paths
- Update test isolation leak detection to ignore ephemeral fusion-test-home-root temp directories while preserving baseline checks

Fusion-Task-Id: FN-3716
This commit is contained in:
Fusion
2026-05-08 02:00:04 -07:00
committed by gsxdsm
parent 8e839fc950
commit f8a0903538
18 changed files with 365 additions and 15 deletions

View File

@@ -2054,6 +2054,7 @@ describe("PATCH /tasks/:id/assign and GET /agents/:id/tasks", () => {
let tempDir: string;
let fusionDir: string;
let agentId: string;
let reviewerAgentId: string;
let store: TaskStore;
// Agent store init + createAgent is ~50ms per call; hoisted to beforeAll
@@ -2070,13 +2071,19 @@ describe("PATCH /tasks/:id/assign and GET /agents/:id/tasks", () => {
name: "Assignment test agent",
role: "executor",
});
const reviewer = await agentStore.createAgent({
name: "Assignment reviewer agent",
role: "reviewer",
});
agentId = agent.id;
reviewerAgentId = reviewer.id;
}, 30_000);
beforeEach(() => {
store = createMockStore({
getFusionDir: vi.fn().mockReturnValue(fusionDir),
updateTask: vi.fn(),
getTask: vi.fn().mockResolvedValue({ ...FAKE_TASK_DETAIL, id: "FN-200", column: "todo" }),
listTasks: vi.fn().mockResolvedValue([]),
selectNextTaskForAgent: vi.fn().mockResolvedValue(null),
} as any);
@@ -2109,6 +2116,39 @@ describe("PATCH /tasks/:id/assign and GET /agents/:id/tasks", () => {
expect(res.body.assignedAgentId).toBe(agentId);
}, 20000);
it("returns 409 when assigning implementation task to non-executor without override", async () => {
const res = await REQUEST(
buildApp(),
"PATCH",
"/api/tasks/FN-200/assign",
JSON.stringify({ agentId: reviewerAgentId }),
{ "Content-Type": "application/json" },
);
expect(res.status).toBe(409);
expect(res.body.error).toContain("requires an \"executor\"-role agent");
expect(store.updateTask).not.toHaveBeenCalled();
}, 20000);
it("allows non-executor assignment when override is true", async () => {
(store.updateTask as ReturnType<typeof vi.fn>).mockResolvedValue({
...FAKE_TASK_DETAIL,
id: "FN-200",
assignedAgentId: reviewerAgentId,
});
const res = await REQUEST(
buildApp(),
"PATCH",
"/api/tasks/FN-200/assign",
JSON.stringify({ agentId: reviewerAgentId, override: true }),
{ "Content-Type": "application/json" },
);
expect(res.status).toBe(200);
expect(store.updateTask).toHaveBeenCalledWith("FN-200", { assignedAgentId: reviewerAgentId });
}, 20000);
it("returns 404 when assigning to a non-existent agent", async () => {
const res = await REQUEST(
buildApp(),