feat(FN-2987): merge fusion/fn-2987
- **Archived task read-only enforcement** — `packages/core/src/store.ts` adds guards that prevent log entries and documents from being written to archived tasks; `packages/engine/src/agent-tools.ts` surfaces meaningful errors instead of silent failures when tools are called on archived tasks - **Type safety for archived guards** — resolved typecheck issues around the archived-task write guards in core - **Tests for archived behavior** — added regression coverage in `packages/core/src/__tests__/store.test.ts` and `packages/core/src/__tests__/task-documents.test.ts`; also added `packages/engine/src/__tests__/agent-tools.test.ts` to cover archived-task error paths - **Documentation** — updated `docs/task-management.md` to document the archived read-only behavior - **Also merged** `fusion/fn-2959-2` (CustomProviderForm component, SettingsModal routing, custom provider API routes, and associated tests) Commits merged: - feat(FN-2987): complete Step 6 — document archived read-only behavior - fix(FN-2987): complete Step 5 — resolve typecheck for archived guards - test(FN-2987): complete Step 4 — add archived-task regression coverage - feat(FN-2987): complete Step 3 — handle archived log tool errors - feat(FN-2987): complete Step 2 — enforce archived document write guard - feat(FN-2987): complete Step 1 — harden archived logEntry checks - feat(FN-2959): merge fusion/fn-2959-2 Files changed: .changeset/custom-openai-anthropic-providers.md | 5 + docs/task-management.md | 4 + packages/core/src/__tests__/store.test.ts | 38 +++ packages/core/src/__tests__/task-documents.test.ts | 32 +++ packages/core/src/store.ts | 34 ++- packages/dashboard/app/api/legacy.ts | 41 ++++ .../app/components/CustomProviderForm.css | 45 ++++ .../app/components/CustomProviderForm.tsx | 203 ++++++++++++++++ .../app/components/ModelOnboardingModal.css | 14 ++ .../app/components/ModelOnboardingModal.tsx | 62 ++++- .../dashboard/app/components/SettingsModal.css | 38 +++ .../dashboard/app/components/SettingsModal.tsx | 91 +++++++- .../__tests__/CustomProviderForm.test.tsx | 60 +++++ .../__tests__/ModelOnboardingModal.test.tsx | 23 ++ .../components/__tests__/SettingsModal.test.tsx | 13 ++ .../__tests__/SettingsModalNodeRouting.test.tsx | 4 + .../components/__tests__/settings-mobile.test.tsx | 4 + packages/dashboard/src/auth-paths.ts | 4 + packages/dashboard/src/routes.ts | 2 + .../__tests__/custom-provider-routes.test.ts | 118 ++++++++++ .../src/routes/register-custom-provider-routes.ts | 254 +++++++++++++++++++++ .../src/__tests__/agent-document-tools.test.ts | 15 ++ packages/engine/src/__tests__/agent-tools.test.ts | 43 ++++ packages/engine/src/agent-tools.ts | 28 ++- 24 files changed, 1166 insertions(+), 9 deletions(-) Fusion-Task-Id: FN-2987
This commit is contained in:
@@ -6903,6 +6903,44 @@ Task with acceptance criteria
|
||||
});
|
||||
});
|
||||
|
||||
describe("logEntry on archived tasks", () => {
|
||||
it("rejects logEntry on cleanup-archived task with archived error", async () => {
|
||||
const task = await store.createTask({ description: "Cleanup archive log test" });
|
||||
await store.moveTask(task.id, "todo");
|
||||
await store.moveTask(task.id, "in-progress");
|
||||
await store.moveTask(task.id, "in-review");
|
||||
await store.moveTask(task.id, "done");
|
||||
await store.archiveTask(task.id, true);
|
||||
|
||||
await expect(store.logEntry(task.id, "should fail")).rejects.toThrow(/archived/i);
|
||||
await expect(store.logEntry(task.id, "should fail")).rejects.not.toThrow(/not found/i);
|
||||
});
|
||||
|
||||
it("rejects logEntry on non-cleanup archived task with archived error", async () => {
|
||||
const task = await store.createTask({ description: "Non-cleanup archive log test" });
|
||||
await store.moveTask(task.id, "todo");
|
||||
await store.moveTask(task.id, "in-progress");
|
||||
await store.moveTask(task.id, "in-review");
|
||||
await store.moveTask(task.id, "done");
|
||||
await store.archiveTask(task.id, false);
|
||||
|
||||
await expect(store.logEntry(task.id, "should fail")).rejects.toThrow(/archived/i);
|
||||
});
|
||||
|
||||
it("rejects logEntry with runContext on cleanup-archived task", async () => {
|
||||
const task = await store.createTask({ description: "Cleanup archive runContext log test" });
|
||||
await store.moveTask(task.id, "todo");
|
||||
await store.moveTask(task.id, "in-progress");
|
||||
await store.moveTask(task.id, "in-review");
|
||||
await store.moveTask(task.id, "done");
|
||||
await store.archiveTask(task.id, true);
|
||||
|
||||
await expect(
|
||||
store.logEntry(task.id, "should fail", "outcome", { runId: "run-1", agentId: "agent-1" }),
|
||||
).rejects.toThrow(/archived/i);
|
||||
});
|
||||
});
|
||||
|
||||
describe("unarchiveTask", () => {
|
||||
it("unarchives an archived task (moves archived → done)", async () => {
|
||||
const task = await store.createTask({ description: "Test task" });
|
||||
|
||||
@@ -112,6 +112,38 @@ describe("TaskStore task documents", () => {
|
||||
).rejects.toThrow("Task KB-DOES-NOT-EXIST not found");
|
||||
});
|
||||
|
||||
it("rejects upsertTaskDocument on cleanup-archived task", async () => {
|
||||
const task = await store.createTask({ description: "Cleanup archived docs test" });
|
||||
await store.moveTask(task.id, "todo");
|
||||
await store.moveTask(task.id, "in-progress");
|
||||
await store.moveTask(task.id, "in-review");
|
||||
await store.moveTask(task.id, "done");
|
||||
await store.archiveTask(task.id, true);
|
||||
|
||||
await expect(
|
||||
store.upsertTaskDocument(task.id, {
|
||||
key: "plan",
|
||||
content: "should fail",
|
||||
}),
|
||||
).rejects.toThrow(/archived/i);
|
||||
});
|
||||
|
||||
it("rejects upsertTaskDocument on non-cleanup archived task", async () => {
|
||||
const task = await store.createTask({ description: "Non-cleanup archived docs test" });
|
||||
await store.moveTask(task.id, "todo");
|
||||
await store.moveTask(task.id, "in-progress");
|
||||
await store.moveTask(task.id, "in-review");
|
||||
await store.moveTask(task.id, "done");
|
||||
await store.archiveTask(task.id, false);
|
||||
|
||||
await expect(
|
||||
store.upsertTaskDocument(task.id, {
|
||||
key: "plan",
|
||||
content: "should fail",
|
||||
}),
|
||||
).rejects.toThrow(/archived/i);
|
||||
});
|
||||
|
||||
it("updates a document, increments revision, and archives previous content", async () => {
|
||||
const task = await store.createTask({ description: "Update task" });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user