feat(FN-4053): unify task ID allocation with store-owned distributed alloca

Unifies task ID allocation under a single store-owned distributed allocator, removing redundant ID-generation logic from dashboard route handlers and simplifying the overall flow; Step 1 merges the allocation authority into the store, Step 3 removes the now-unnecessary mixed-path routing, and tests

Fusion-Task-Id: FN-4053
This commit is contained in:
Fusion
2026-05-11 21:38:25 -07:00
committed by gsxdsm
parent ce0e5f4ce6
commit 4404c6103e
12 changed files with 244 additions and 359 deletions

View File

@@ -4,11 +4,9 @@ import {
COLUMNS,
TASK_PRIORITIES,
VALID_TRANSITIONS,
buildMeshReplicatedTaskCreatePayload,
isTaskPriority,
REPO_OVERRIDE_RE,
resolveTitleSummarizerSettingsModel,
toReplicatedCreateInput,
validateNodeOverrideChange,
canAgentTakeImplementationTaskForExplicitRouting,
formatRoleMismatchReason,
@@ -19,7 +17,6 @@ import { maybeCreateTrackingIssue } from "../github-tracking.js";
import { parseGitHubBadgeUrl } from "./register-git-github.js";
import { planTaskWorktreePath } from "@fusion/engine";
import { ApiError, badRequest, conflict, notFound } from "../api-error.js";
import { fetchFromRemoteNode } from "./register-settings-sync-helpers.js";
import type { ApiRoutesContext } from "./types.js";
import { resolveBranchSelection } from "./branch-selection.js";
@@ -350,91 +347,13 @@ export function registerTaskWorkflowRoutes(ctx: ApiRoutesContext, deps: TaskWork
...(validatedGithubTracking ? { githubTracking: validatedGithubTracking } : {}),
};
if (typeof scopedStore.createTaskWithReservedId !== "function") {
const task = await scopedStore.createTask(
createInput,
{ onSummarize, settings: { autoSummarizeTitles: settings.autoSummarizeTitles } },
);
await maybeCreateTaskTrackingIssue(scopedStore, task, options?.githubToken);
res.status(201).json(task);
return;
}
const allocator = scopedStore.getDistributedTaskIdAllocator();
const { CentralCore } = await import("@fusion/core");
const central = new CentralCore();
await central.init();
const nodes = await central.listNodes();
const localNode = nodes.find((node) => node.type === "local");
const remoteNodes = nodes.filter((node) => node.type === "remote" && node.url && node.apiKey);
await central.close();
const nodeIdForReservation = localNode?.id ?? "local";
const isOverlapClassFailure = (err: unknown): boolean => {
const message = err instanceof Error ? err.message : String(err);
return (
message.includes("Task ID already exists:") ||
message.includes("Replicated task payload collision for existing task")
);
};
const maxCreateAttempts = 3;
for (let attempt = 1; attempt <= maxCreateAttempts; attempt += 1) {
const reservation = await allocator.reserveDistributedTaskId({
prefix: "FN",
nodeId: nodeIdForReservation,
});
let createdTask: Task | null = null;
try {
createdTask = await scopedStore.createTaskWithReservedId(createInput, {
taskId: reservation.taskId,
});
await maybeCreateTaskTrackingIssue(scopedStore, createdTask, options?.githubToken);
const replicatedPayload = buildMeshReplicatedTaskCreatePayload({
taskId: createdTask.id,
reservationId: reservation.reservationId,
sourceNodeId: nodeIdForReservation,
createdAt: createdTask.createdAt,
updatedAt: createdTask.updatedAt,
prompt: (await scopedStore.getTask(createdTask.id)).prompt,
createInput: toReplicatedCreateInput(createdTask),
});
for (const peer of remoteNodes) {
await fetchFromRemoteNode(peer, "/api/mesh/tasks/create", {
method: "POST",
body: replicatedPayload,
});
}
await allocator.commitDistributedTaskIdReservation({
reservationId: reservation.reservationId,
nodeId: nodeIdForReservation,
});
res.status(201).json(createdTask);
return;
} catch (err: unknown) {
await allocator.abortDistributedTaskIdReservation({
reservationId: reservation.reservationId,
nodeId: nodeIdForReservation,
reason: "failed-create",
}).catch(() => undefined);
if (createdTask) {
await scopedStore.deleteTask(createdTask.id).catch(() => undefined);
}
if (attempt < maxCreateAttempts && isOverlapClassFailure(err)) {
continue;
}
const message = err instanceof Error ? err.message : String(err);
throw new ApiError(503, `Cluster task create failed: ${message}`);
}
}
const task = await scopedStore.createTask(
createInput,
{ onSummarize, settings: { autoSummarizeTitles: settings.autoSummarizeTitles } },
);
await maybeCreateTaskTrackingIssue(scopedStore, task, options?.githubToken);
res.status(201).json(task);
return;
} catch (err: unknown) {
if (err instanceof ApiError) {
throw err;