mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 05:16:23 -06:00
fix(ui): stop flagging running automations as overdue and show their state (#123745)
* fix(ui): stop flagging running automations as overdue and show their state The gateway intentionally leaves nextRunAtMs past-due while a run executes (it advances only on the outcome), and agentTurn runs may take up to an hour — 12x the sidebar's 5-minute overdue grace. The overdue chip and the cron table therefore reported healthy in-flight jobs as 'overdue' / 'next run: X minutes ago'. runningAtMs is the recorded fact for an in-flight run (already public and used by the CLI's status), but no UI surface read it. Add the shared isCronJobRunning helper, exclude running jobs from the overdue filter, and render 'Running' in the next-run cell. * test(cron): move running-state row test beside the capped view suite
This commit is contained in:
committed by
GitHub
parent
7fb47729c3
commit
f9d9b9da2b
@@ -5,7 +5,7 @@ import type { CronJob, ModelAuthStatusResult } from "../api/types.ts";
|
||||
import type { NavigationRouteId } from "../app-navigation.ts";
|
||||
import type { ExecApprovalRequest } from "../app/exec-approval.ts";
|
||||
import { t } from "../i18n/index.ts";
|
||||
import { isCronJobActiveFailure } from "../lib/cron-status.ts";
|
||||
import { isCronJobActiveFailure, isCronJobRunning } from "../lib/cron-status.ts";
|
||||
import { clampText } from "../lib/format.ts";
|
||||
import { isMonitoredAuthProvider } from "../lib/model-auth.ts";
|
||||
import type { IconName } from "./icons.ts";
|
||||
@@ -89,6 +89,7 @@ export function buildSidebarAttentionItems(params: {
|
||||
const overdueCron = params.cronJobs.filter(
|
||||
(job) =>
|
||||
job.enabled &&
|
||||
!isCronJobRunning(job) &&
|
||||
job.state?.nextRunAtMs != null &&
|
||||
params.now - job.state.nextRunAtMs > CRON_OVERDUE_GRACE_MS,
|
||||
);
|
||||
|
||||
@@ -153,6 +153,22 @@ describe("cron attention details", () => {
|
||||
|
||||
expect(overdue?.detail).toBe("Nightly backup\nunnamed-id");
|
||||
});
|
||||
|
||||
it("does not flag an actively running job as overdue", () => {
|
||||
// The gateway leaves nextRunAtMs past-due during execution; runningAtMs is
|
||||
// the recorded fact that a run is in flight (agentTurn runs may take up to
|
||||
// an hour, far beyond the 5-minute overdue grace).
|
||||
const running = cronJob("running-id");
|
||||
running.state = { lastRunStatus: "ok", nextRunAtMs: 1, runningAtMs: 2 };
|
||||
const stalled = cronJob("stalled-id");
|
||||
stalled.state = { lastRunStatus: "ok", nextRunAtMs: 2 };
|
||||
|
||||
const overdue = cronItems([running, stalled], 300_003).find(
|
||||
(item) => item.kind === "cronOverdue",
|
||||
);
|
||||
|
||||
expect(overdue?.detail).toBe("stalled-id");
|
||||
});
|
||||
});
|
||||
|
||||
describe("pending approval attention", () => {
|
||||
|
||||
@@ -5839,6 +5839,7 @@ export const en: TranslationMap = {
|
||||
runStatusError: "Error",
|
||||
runStatusSkipped: "Skipped",
|
||||
runStatusUnknown: "Unknown",
|
||||
runStatusRunning: "Running",
|
||||
deliveryDelivered: "Delivered",
|
||||
deliveryNotDelivered: "Not delivered",
|
||||
deliveryUnknown: "Unknown",
|
||||
|
||||
@@ -7,6 +7,14 @@ export function resolveCronJobLastRunStatus(job: CronJob): CronJobLastRunStatus
|
||||
return job.state?.lastRunStatus ?? job.state?.lastStatus ?? "unknown";
|
||||
}
|
||||
|
||||
// The gateway intentionally leaves nextRunAtMs past-due while a run executes
|
||||
// (it only advances on the outcome), so "overdue" surfaces must not flag a
|
||||
// job that is actively running. runningAtMs is the recorded fact for that.
|
||||
export function isCronJobRunning(job: CronJob): boolean {
|
||||
const runningAtMs = job.state?.runningAtMs;
|
||||
return typeof runningAtMs === "number" && Number.isFinite(runningAtMs);
|
||||
}
|
||||
|
||||
// "Failed cron" surfaces (cron page, sidebar attention chips) track current
|
||||
// actionability, so a failure only counts while the job is still enabled.
|
||||
// Disabled jobs keep their historical `lastRunStatus: "error"` for detail
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Running-state rendering for the Automations (cron) table.
|
||||
// Lives beside view.test.ts, which sits at the max-lines cap.
|
||||
import { expect, it } from "vitest";
|
||||
import {
|
||||
createCronViewJob as createJob,
|
||||
renderCronView as renderView,
|
||||
} from "./view.test-support.ts";
|
||||
|
||||
it("shows Running instead of a past-due next-run time while a run executes", () => {
|
||||
const running = createJob("job-running", {
|
||||
state: { nextRunAtMs: Date.now() - 600_000, runningAtMs: Date.now() - 60_000 },
|
||||
});
|
||||
const container = renderView({ jobs: [running] });
|
||||
const row = container.querySelector(".cron-table__row");
|
||||
expect(row?.querySelector(".cron-table__running")?.textContent).toBe("Running");
|
||||
expect(row?.textContent).not.toContain("ago");
|
||||
});
|
||||
@@ -36,7 +36,11 @@ import {
|
||||
renderSettingsToggleRow,
|
||||
} from "../../components/settings-ui.ts";
|
||||
import { t } from "../../i18n/index.ts";
|
||||
import { isCronJobActiveFailure, resolveCronJobLastRunStatus } from "../../lib/cron-status.ts";
|
||||
import {
|
||||
isCronJobActiveFailure,
|
||||
isCronJobRunning,
|
||||
resolveCronJobLastRunStatus,
|
||||
} from "../../lib/cron-status.ts";
|
||||
import { parseCronEveryMs } from "../../lib/cron/decimal.ts";
|
||||
import type {
|
||||
CronFieldErrors,
|
||||
@@ -755,7 +759,11 @@ function renderJobRow(job: CronJob, props: CronProps) {
|
||||
</span>
|
||||
<span class="cron-table__cell">${formatCronSchedule(job)}</span>
|
||||
<span class="cron-table__cell">
|
||||
${hasNextRun ? formatRelativeTimestamp(nextRunAtMs) : t("common.na")}
|
||||
${isCronJobRunning(job)
|
||||
? html`<span class="cron-table__running">${t("cron.runs.runStatusRunning")}</span>`
|
||||
: hasNextRun
|
||||
? formatRelativeTimestamp(nextRunAtMs)
|
||||
: t("common.na")}
|
||||
</span>
|
||||
<span class="cron-table__cell cron-table__last">${renderLastRunCell(job)}</span>
|
||||
<span
|
||||
|
||||
@@ -358,6 +358,10 @@
|
||||
font-size: var(--control-ui-text-xs);
|
||||
}
|
||||
|
||||
.cron-table__running {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.cron-table__cell {
|
||||
color: var(--text);
|
||||
font-size: 12.5px;
|
||||
|
||||
Reference in New Issue
Block a user