Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions packages/dashboard/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1595,6 +1595,8 @@ export function createServer(store: TaskStore, options?: ServerOptions): ReturnT
});

app.get("/api/health/reliability", async (req, res) => {
const projectId = getProjectIdFromRequest(req);
const scopedStore = projectId ? await getScopedStore(projectId) : store;
Comment on lines 1597 to +1599

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unhandled getScopedStore rejection in GET handler

Both the GET and POST endpoints call await getScopedStore(projectId) without wrapping it in try/catch. If store resolution fails for an unknown or misconfigured projectId (e.g. resolveScopedStore throws), Express 4 won't automatically propagate the async rejection to the error handler — the request will hang without a response. Compare with /api/engine/start (lines 1578–1594) and the terminal WebSocket handler (lines 2059–2077), both of which wrap getScopedStore/resolveScopedStore in try/catch and return a structured error response. The same guard is needed here so callers receive a 500 JSON response rather than a stalled connection.

Comment on lines 1597 to +1599

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing FNXC change comment

The project's coding convention (AGENTS.md) requires a FNXC:Area-of-product yyyy-MM-dd-hh:mm comment whenever new behavior is introduced or requirements change. The two modified blocks add multi-project scoping to the reliability endpoints but carry no FNXC annotation describing the FUX-042 requirement and the date of the change. This pattern appears consistently across the file for similar routing additions.

Context Used: AGENTS.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

const rawWindowDays = req.query.windowDays;
const parsedWindowDays = rawWindowDays === undefined ? 7 : Number.parseInt(String(rawWindowDays), 10);

Expand All @@ -1606,7 +1608,7 @@ export function createServer(store: TaskStore, options?: ServerOptions): ReturnT
return;
}

const settings = await store.getSettings();
const settings = await scopedStore.getSettings();
const resetAt = typeof settings.reliabilityStatsResetAt === "string" ? settings.reliabilityStatsResetAt : null;

const nowMs = Date.now();
Expand All @@ -1617,11 +1619,11 @@ export function createServer(store: TaskStore, options?: ServerOptions): ReturnT
const endIso = new Date(nowMs).toISOString();

const [runAuditEvents, enteredByDay, bouncedByDay, durationEvents, mergedTaskIds] = await Promise.all([
Promise.resolve(store.getRunAuditEvents({ startTime: startIso, endTime: endIso, limit: 50_000 })),
store.getTaskMovedCountsByDay({ since: startIso, until: endIso, toColumn: "in-review" }),
store.getTaskMovedCountsByDay({ since: startIso, until: endIso, fromColumn: "in-review", toColumn: "in-progress" }),
store.getInReviewDurationEvents({ since: startIso, until: endIso }),
store.getTaskMergedTaskIds({ since: startIso, until: endIso }),
Promise.resolve(scopedStore.getRunAuditEvents({ startTime: startIso, endTime: endIso, limit: 50_000 })),
scopedStore.getTaskMovedCountsByDay({ since: startIso, until: endIso, toColumn: "in-review" }),
scopedStore.getTaskMovedCountsByDay({ since: startIso, until: endIso, fromColumn: "in-review", toColumn: "in-progress" }),
scopedStore.getInReviewDurationEvents({ since: startIso, until: endIso }),
scopedStore.getTaskMergedTaskIds({ since: startIso, until: endIso }),
]);

const postMergeByDay = postMergeAuditFailuresPerDay(runAuditEvents, effectiveStartMs, nowMs);
Expand Down Expand Up @@ -1698,9 +1700,11 @@ export function createServer(store: TaskStore, options?: ServerOptions): ReturnT
});
});

app.post("/api/health/reliability/reset", async (_req, res) => {
app.post("/api/health/reliability/reset", async (req, res) => {
const projectId = getProjectIdFromRequest(req);
const scopedStore = projectId ? await getScopedStore(projectId) : store;
Comment on lines +1703 to +1705

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Same missing error guard in POST reset handler

The reset endpoint has the identical problem: await getScopedStore(projectId) can throw if the projectId is unknown or the underlying store resolution fails, and there is no try/catch to translate that into a structured HTTP error. A caller posting a reset for a non-existent project will get a hanging request instead of a 500 or 404 response.

const resetAt = new Date().toISOString();
await store.updateSettings({ reliabilityStatsResetAt: resetAt });
await scopedStore.updateSettings({ reliabilityStatsResetAt: resetAt });
res.json({ resetAt });
});
Comment on lines +1703 to 1709

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing changeset for published package

AGENTS.md requires a changeset under .changeset/<name>.md with "@runfusion/fusion": patch for any bug fix that changes observable behavior in the published CLI. This PR fixes incorrect aggregate reliability stats in multi-project setups (FUX-042), which is a user-visible behaviour change for Fusion operators — a patch changeset with a fix category is expected.

Context Used: AGENTS.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!


Expand Down
Loading