feat(FN-1808): merge fusion/fn-1808
This commit is contained in:
@@ -49,6 +49,7 @@ export const DEFAULT_PROJECT_SETTINGS = {
|
||||
globalPause: false,
|
||||
enginePaused: false,
|
||||
maxConcurrent: 2,
|
||||
maxTriageConcurrent: 2,
|
||||
globalMaxConcurrent: 4,
|
||||
maxWorktrees: 4,
|
||||
pollIntervalMs: 15000,
|
||||
|
||||
@@ -234,4 +234,94 @@ describe("getTaskCompletionBlocker", () => {
|
||||
}, { resolveTask }))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
// ── in-review as resolved dependency ───────────────────────────────────
|
||||
|
||||
it("returns undefined when a dependency is in-review", async () => {
|
||||
const resolveTask = async (taskId: string) => {
|
||||
if (taskId === "FN-001") {
|
||||
return { id: "FN-001", column: "in-review" as const };
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
await expect(getTaskCompletionBlocker({
|
||||
...baseCompletionTask,
|
||||
dependencies: ["FN-001"],
|
||||
}, { resolveTask }))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns undefined when dependencies are a mix of done and in-review", async () => {
|
||||
const resolveTask = async (taskId: string) => {
|
||||
if (taskId === "FN-001") {
|
||||
return { id: "FN-001", column: "done" as const };
|
||||
}
|
||||
if (taskId === "FN-002") {
|
||||
return { id: "FN-002", column: "in-review" as const };
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
await expect(getTaskCompletionBlocker({
|
||||
...baseCompletionTask,
|
||||
dependencies: ["FN-001", "FN-002"],
|
||||
}, { resolveTask }))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns a reason when a dependency is in-progress", async () => {
|
||||
const resolveTask = async (taskId: string) => {
|
||||
if (taskId === "FN-001") {
|
||||
return { id: "FN-001", column: "in-progress" as const };
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
await expect(getTaskCompletionBlocker({
|
||||
...baseCompletionTask,
|
||||
dependencies: ["FN-001"],
|
||||
}, { resolveTask }))
|
||||
.resolves.toBe("task has unresolved dependencies: FN-001");
|
||||
});
|
||||
|
||||
it("returns a reason when a dependency is in triage", async () => {
|
||||
const resolveTask = async (taskId: string) => {
|
||||
if (taskId === "FN-001") {
|
||||
return { id: "FN-001", column: "triage" as const };
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
await expect(getTaskCompletionBlocker({
|
||||
...baseCompletionTask,
|
||||
dependencies: ["FN-001"],
|
||||
}, { resolveTask }))
|
||||
.resolves.toBe("task has unresolved dependencies: FN-001");
|
||||
});
|
||||
|
||||
it("returns a reason when a dependency is in todo", async () => {
|
||||
const resolveTask = async (taskId: string) => {
|
||||
if (taskId === "FN-001") {
|
||||
return { id: "FN-001", column: "todo" as const };
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
await expect(getTaskCompletionBlocker({
|
||||
...baseCompletionTask,
|
||||
dependencies: ["FN-001"],
|
||||
}, { resolveTask }))
|
||||
.resolves.toBe("task has unresolved dependencies: FN-001");
|
||||
});
|
||||
|
||||
it("returns a reason when a dependency task does not exist", async () => {
|
||||
const resolveTask = async (_taskId: string) => null;
|
||||
|
||||
await expect(getTaskCompletionBlocker({
|
||||
...baseCompletionTask,
|
||||
dependencies: ["FN-999"],
|
||||
}, { resolveTask }))
|
||||
.resolves.toBe("task has unresolved dependencies: FN-999");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -100,7 +100,7 @@ export async function getTaskCompletionBlocker(
|
||||
|
||||
for (const dependencyId of dependencies) {
|
||||
const dependency = await options.resolveTask(dependencyId);
|
||||
if (!dependency || (dependency.column !== "done" && dependency.column !== "archived")) {
|
||||
if (!dependency || (dependency.column !== "done" && dependency.column !== "in-review" && dependency.column !== "archived")) {
|
||||
unresolvedDependencies.push(dependencyId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -982,6 +982,9 @@ export interface ProjectSettings {
|
||||
/** Maximum number of concurrent AI agents across all activity types
|
||||
* (triage specification, task execution, and merge operations). */
|
||||
maxConcurrent: number;
|
||||
/** Maximum number of concurrent triage/specification agents. When undefined,
|
||||
* falls back to maxConcurrent. */
|
||||
maxTriageConcurrent?: number;
|
||||
/** System-wide maximum concurrent agents across ALL projects.
|
||||
* When multiple projects are active, the sum of their in-flight agents
|
||||
* will not exceed this limit. Applies to triage, execution, and merge.
|
||||
|
||||
Reference in New Issue
Block a user