Files
fusion/packages/dashboard/app/utils/__tests__/taskTiming.test.ts
gsxdsm 4dab2b6986 FN-7011: Exclude engine downtime from task timing
Exclude proven engine-process downtime from active task duration stats and badges.

- Persist a throttled engineLastActiveAt heartbeat while the scheduler is running and unpaused.
- Reconcile in-progress execution segment anchors on startup when downtime exceeds the poll threshold.
- Emit run-audit events for shifted and no-action downtime reconciliation outcomes.
- Cover core timing, dashboard badge behavior, scheduler heartbeat throttling, and self-healing audit metadata.
- Add a patch changeset for the published CLI package.

Files changed:
 .changeset/fn-7011-engine-downtime-timing.md       |  7 +++
 AGENTS.md                                          |  1 +
 .../src/__tests__/store-execution-timing.test.ts   | 51 ++++++++++++++++++++++
 packages/core/src/settings-schema.ts               |  1 +
 packages/core/src/store.ts                         | 28 ++++++++++++
 packages/core/src/types.ts                         |  5 +++
 .../app/utils/__tests__/taskTiming.test.ts         | 16 +++++++
 .../__tests__/scheduler-workflow-cutover.test.ts   | 29 +++++++++++-
 packages/engine/src/__tests__/self-healing.test.ts | 23 ++++++++++
 packages/engine/src/run-audit.ts                   |  4 ++
 packages/engine/src/scheduler.ts                   | 11 +++++
 packages/engine/src/self-healing.ts                | 21 +++++++++
 12 files changed, 196 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-7011

Fusion-Task-Lineage: 44225b0a-0fa1-40be-812d-33fd5e446944
2026-06-25 23:35:31 -07:00

60 lines
1.8 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { getActiveRuntimeMs, getWallClockSinceFirstExecutionMs } from "../taskTiming";
describe("taskTiming helpers", () => {
it("returns persisted plus live segment for in-progress tasks", () => {
const nowMs = Date.parse("2026-05-15T13:16:00.000Z");
const runtime = getActiveRuntimeMs(
{
column: "in-progress",
cumulativeActiveMs: 240_000,
executionStartedAt: "2026-05-15T13:15:00.000Z",
columnMovedAt: "2026-05-15T13:15:00.000Z",
},
nowMs,
);
expect(runtime).toBe(300_000);
});
it("returns null when there is no active-runtime signal", () => {
const runtime = getActiveRuntimeMs(
{
column: "todo",
cumulativeActiveMs: undefined,
executionStartedAt: undefined,
columnMovedAt: undefined,
},
Date.now(),
);
expect(runtime).toBeNull();
});
it("uses shifted executionStartedAt so the active badge excludes engine-down time", () => {
const t0 = Date.parse("2026-06-25T00:00:00.000Z");
const runtime = getActiveRuntimeMs(
{
column: "in-progress",
cumulativeActiveMs: undefined,
executionStartedAt: new Date(t0 + 60 * 60_000).toISOString(),
columnMovedAt: new Date(t0).toISOString(),
},
t0 + 65 * 60_000,
);
expect(runtime).toBe(5 * 60_000);
expect(getActiveRuntimeMs({ column: "in-progress", cumulativeActiveMs: undefined, executionStartedAt: undefined, columnMovedAt: undefined }, t0)).toBeNull();
});
it("returns wall-clock runtime since first execution", () => {
const wallClock = getWallClockSinceFirstExecutionMs(
"2026-05-15T08:42:00.000Z",
"2026-05-15T13:17:00.000Z",
Date.parse("2026-05-15T13:20:00.000Z"),
);
expect(wallClock).toBe(16_500_000);
});
});