Add persistent task recommendations that agents can create, resolve, and display in task details. - Persist recommendation state and expose task recommendation API routes. - Generate recommendations from executor task completions with duplicate suppression. - Add localized dashboard recommendation tab and settings control with coverage. Files changed: .changeset/fn-8829-recommendations.md | 7 + docs/dashboard-guide.md | 1 + docs/settings-reference.md | 1 + .../postgres/settings-persistence.pg.test.ts | 10 + .../postgres/task-recommendations.pg.test.ts | 191 +++++++++ .../core/src/__tests__/settings-parity.test.ts | 2 + packages/core/src/config/settings-schema.ts | 2 + packages/core/src/index.ts | 2 +- .../0047_fn_8829_task_recommendations.sql | 3 + packages/core/src/postgres/schema-applier.ts | 32 +- packages/core/src/postgres/schema/project.ts | 2 + packages/core/src/store.ts | 12 +- packages/core/src/task-store/persistence.ts | 4 +- packages/core/src/task-store/serialization.ts | 1 + packages/core/src/task-store/settings-ops.ts | 22 + packages/core/src/task-store/task-mutation-ops.ts | 78 +++- packages/core/src/task-store/task-row-mappers.ts | 2 +- packages/core/src/task-store/task-update.ts | 51 ++- packages/core/src/types.ts | 4 + packages/core/src/types/settings/settings-scope.ts | 6 + packages/core/src/types/task/task-core.ts | 18 + .../__tests__/App.openTasksInRightSidebar.test.ts | 3 +- packages/dashboard/app/__tests__/api-tasks.test.ts | 31 ++ packages/dashboard/app/api/legacy.ts | 1 + packages/dashboard/app/api/tasks/tasks.ts | 24 ++ .../dashboard/app/components/TaskDetailModal.tsx | 43 +- .../app/components/TaskRecommendationsTab.css | 71 ++++ .../app/components/TaskRecommendationsTab.tsx | 127 ++++++ .../__tests__/SettingsModal.general.test.tsx | 11 + .../TaskDetailModal.recommendations.test.tsx | 111 +++++ .../app/components/settings/section-keys.ts | 1 + .../settings/sections/GeneralSection.tsx | 14 + .../settings-default-descriptions.test.tsx | 1 + packages/dashboard/app/hooks/useModalManager.ts | 6 + packages/dashboard/app/plugins/types.ts | 7 +- .../__tests__/task-recommendation-routes.test.ts | 472 +++++++++++++++++++++ .../src/routes/register-task-workflow-routes.ts | 263 +++++++++++- .../executor-task-recommendations.test.ts | 128 ++++++ packages/engine/src/executor.ts | 66 ++- packages/i18n/locales/en/app.json | 15 +- packages/i18n/locales/es/app.json | 16 +- packages/i18n/locales/fr/app.json | 16 +- packages/i18n/locales/ko/app.json | 16 +- packages/i18n/locales/zh-CN/app.json | 16 +- packages/i18n/locales/zh-TW/app.json | 16 +- packages/i18n/src/resources.d.ts | 12 + 46 files changed, 1908 insertions(+), 30 deletions(-) Fusion-Task-Id: FN-8829 Fusion-Task-Lineage: 5f60a1fb-9cf8-4577-9bfd-c20a2d402333 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
92 lines
3.1 KiB
TypeScript
92 lines
3.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { getBoardTaskOpenRoute, shouldOpenBoardTaskInDock } from "../App";
|
|
|
|
describe("board task detail routing", () => {
|
|
it("opens board card clicks in the dock only when the setting and dock surface are both active", () => {
|
|
expect(shouldOpenBoardTaskInDock(true, true)).toBe(true);
|
|
expect(shouldOpenBoardTaskInDock(false, true)).toBe(false);
|
|
expect(shouldOpenBoardTaskInDock(true, false)).toBe(false);
|
|
});
|
|
|
|
it("keeps deep-tab opens on the existing main-panel path", () => {
|
|
expect(shouldOpenBoardTaskInDock(true, true, "changes")).toBe(false);
|
|
expect(shouldOpenBoardTaskInDock(true, true, "recommendations")).toBe(false);
|
|
expect(shouldOpenBoardTaskInDock(true, true, "retries")).toBe(false);
|
|
expect(shouldOpenBoardTaskInDock(true, true, "workflow")).toBe(false);
|
|
});
|
|
|
|
it("routes ordinary board card clicks to the popup on mobile, tablet, and desktop when enabled", () => {
|
|
for (const isMobile of [true, false]) {
|
|
expect(getBoardTaskOpenRoute({
|
|
isMobile,
|
|
openMobileTasksInPopup: true,
|
|
openTasksInRightSidebar: false,
|
|
rightDockActive: false,
|
|
})).toBe("popup");
|
|
}
|
|
});
|
|
|
|
it("keeps setting-off and undefined values on the existing fallback route", () => {
|
|
expect(getBoardTaskOpenRoute({
|
|
isMobile: true,
|
|
openMobileTasksInPopup: false,
|
|
openTasksInRightSidebar: false,
|
|
rightDockActive: false,
|
|
})).toBe("main-panel");
|
|
|
|
expect(getBoardTaskOpenRoute({
|
|
isMobile: false,
|
|
openMobileTasksInPopup: undefined as unknown as boolean,
|
|
openTasksInRightSidebar: false,
|
|
rightDockActive: false,
|
|
})).toBe("main-panel");
|
|
});
|
|
|
|
it("gives the popup setting deterministic precedence over desktop and tablet right-dock routing", () => {
|
|
expect(getBoardTaskOpenRoute({
|
|
isMobile: false,
|
|
openMobileTasksInPopup: true,
|
|
openTasksInRightSidebar: true,
|
|
rightDockActive: true,
|
|
})).toBe("popup");
|
|
|
|
expect(getBoardTaskOpenRoute({
|
|
isMobile: false,
|
|
openMobileTasksInPopup: false,
|
|
openTasksInRightSidebar: true,
|
|
rightDockActive: true,
|
|
})).toBe("dock");
|
|
|
|
expect(getBoardTaskOpenRoute({
|
|
isMobile: false,
|
|
openMobileTasksInPopup: true,
|
|
openTasksInRightSidebar: false,
|
|
rightDockActive: true,
|
|
})).toBe("popup");
|
|
});
|
|
|
|
it("routes every board-card deep tab to the popup when enabled", () => {
|
|
for (const initialTab of ["changes", "recommendations", "retries", "workflow"] as const) {
|
|
for (const isMobile of [true, false]) {
|
|
expect(getBoardTaskOpenRoute({
|
|
isMobile,
|
|
openMobileTasksInPopup: true,
|
|
openTasksInRightSidebar: true,
|
|
rightDockActive: true,
|
|
initialTab,
|
|
})).toBe("popup");
|
|
}
|
|
}
|
|
});
|
|
|
|
it("keeps a popup-disabled changes deep link in the main panel", () => {
|
|
expect(getBoardTaskOpenRoute({
|
|
isMobile: false,
|
|
openMobileTasksInPopup: false,
|
|
openTasksInRightSidebar: true,
|
|
rightDockActive: true,
|
|
initialTab: "changes",
|
|
})).toBe("main-panel");
|
|
});
|
|
});
|