feat(FN-1259): add review handoff mechanism for user assignment

- Add assigneeUserId field to Task type and SQLite schema for human assignment
- Add reviewHandoffPolicy setting to control automatic handoff behavior
- Implement handoff detection in executor: detect user assignment during review and auto-transition task
- Add dashboard API routes for user assignment, handoff queries, and completion
- Add frontend API functions: getHandoffTask, assignTaskToUser, completeHandoff
- Add comprehensive tests for store methods, API routes, and executor handoff logic
- Update memory documentation with review handoff pattern
This commit is contained in:
gsxdsm
2026-04-09 21:06:19 -07:00
parent cc4382835d
commit e404444439
16 changed files with 476 additions and 19 deletions

View File

@@ -1197,6 +1197,30 @@ describe("TaskStore", () => {
});
});
describe("updateTask — assigneeUserId", () => {
it("sets assigneeUserId via updateTask", async () => {
const task = await store.createTask({ title: "User task", description: "A task" });
const updated = await store.updateTask(task.id, { assigneeUserId: "requesting-user" });
expect(updated.assigneeUserId).toBe("requesting-user");
});
it("clears assigneeUserId when set to null", async () => {
const task = await store.createTask({ title: "User task", description: "A task" });
await store.updateTask(task.id, { assigneeUserId: "requesting-user" });
const updated = await store.updateTask(task.id, { assigneeUserId: null });
expect(updated.assigneeUserId).toBeUndefined();
});
it("sets and clears status: awaiting-user-review", async () => {
const task = await store.createTask({ title: "Review task", description: "A task" });
const updated = await store.updateTask(task.id, { status: "awaiting-user-review" });
expect(updated.status).toBe("awaiting-user-review");
const cleared = await store.updateTask(task.id, { status: null });
expect(cleared.status).toBeUndefined();
});
});
// ── Task prefix tests ──────────────────────────────────────────
describe("taskPrefix setting", () => {
@@ -1576,6 +1600,21 @@ describe("TaskStore", () => {
});
});
describe("createTask — assigneeUserId", () => {
it("persists assigneeUserId on creation", async () => {
const created = await store.createTask({
title: "Task with user assignment",
description: "A task assigned to a user",
assigneeUserId: "requesting-user",
});
expect(created.assigneeUserId).toBe("requesting-user");
const persisted = await store.getTask(created.id);
expect(persisted.assigneeUserId).toBe("requesting-user");
});
});
describe("updateTask — model overrides", () => {
it("sets executor model provider and id via updateTask", async () => {
const task = await createTestTask();