惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
博客园 - 三生石上(FineUI控件)
WordPress大学
WordPress大学
博客园_首页
宝玉的分享
宝玉的分享
S
SegmentFault 最新的问题
Jina AI
Jina AI
Hugging Face - Blog
Hugging Face - Blog
V
Visual Studio Blog
美团技术团队
IT之家
IT之家
罗磊的独立博客
Blog — PlanetScale
Blog — PlanetScale
Google DeepMind News
Google DeepMind News
月光博客
月光博客
Microsoft Azure Blog
Microsoft Azure Blog
H
Help Net Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Last Week in AI
Last Week in AI
博客园 - 叶小钗
M
MIT News - Artificial intelligence
B
Blog RSS Feed
有赞技术团队
有赞技术团队
Y
Y Combinator Blog

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix: tolerate malformed cron schedule reloads · openclaw/...
steipete · 2026-05-02 · via Recent Commits to openclaw:main
Original file line numberDiff line numberDiff line change

@@ -21,6 +21,7 @@ Docs: https://docs.openclaw.ai

2121
2222

### Fixes

2323
24+

- Cron: make scheduler reload schedule comparison tolerate malformed persisted jobs, so one bad cron entry no longer aborts the whole tick. Fixes #75886. Thanks @samfox-ai.

2425

- Control UI/chat: keep live replies visible when a raw session alias such as `main` sends the chat turn but Gateway emits events under the canonical session key for the same run. Fixes #73716. Thanks @teebes.

2526

- CLI/models: reject `--agent` on `openclaw models set` and `set-image` instead of silently writing agent-scoped requests to global model defaults. Fixes #68391. Thanks @derrickabellard.

2627

- CLI: stop treating the legacy singular `openclaw tool ...` token as a plugin id under restrictive `plugins.allow`, so it falls through as a normal unknown/reserved command instead of suggesting a stale allowlist entry. Fixes #64732. Thanks @efe-arv, @SweetSophia, and @hashtag1974.

Original file line numberDiff line numberDiff line change

@@ -61,18 +61,6 @@ function resolveSchedulePayload(

6161

return schedulePayloadFromRecord(job);

6262

}

6363
64-

function cronScheduleIdentity(job: Pick<CronJob, "schedule"> & { enabled?: boolean }): string {

65-

const schedule = resolveSchedulePayload(job as unknown as Record<string, unknown>);

66-

if (!schedule) {

67-

throw new Error("Unsupported cron schedule kind");

68-

}

69-

return JSON.stringify({

70-

version: 1,

71-

enabled: job.enabled ?? true,

72-

schedule,

73-

});

74-

}

75-
7664

export function tryCronScheduleIdentity(

7765

job: { schedule?: unknown; enabled?: unknown } & Record<string, unknown>,

7866

): string | undefined {

@@ -88,8 +76,14 @@ export function tryCronScheduleIdentity(

8876

}

8977
9078

export function cronSchedulingInputsEqual(

91-

previous: Pick<CronJob, "schedule"> & { enabled?: boolean },

92-

next: Pick<CronJob, "schedule"> & { enabled?: boolean },

79+

previous: Pick<CronJob, "schedule"> & { enabled?: unknown },

80+

next: Pick<CronJob, "schedule"> & { enabled?: unknown },

9381

): boolean {

94-

return cronScheduleIdentity(previous) === cronScheduleIdentity(next);

82+

const previousIdentity = tryCronScheduleIdentity(previous as Record<string, unknown>);

83+

const nextIdentity = tryCronScheduleIdentity(next as Record<string, unknown>);

84+

return (

85+

previousIdentity !== undefined &&

86+

nextIdentity !== undefined &&

87+

previousIdentity === nextIdentity

88+

);

9589

}

Original file line numberDiff line numberDiff line change

@@ -279,6 +279,36 @@ describe("cron service store seam coverage", () => {

279279

expect(findJobOrThrow(state, "reload-cron-expr-job").state.nextRunAtMs).toBe(dueNextRunAtMs);

280280

});

281281
282+

it("clears stale nextRunAtMs without throwing when a force-reloaded schedule is malformed", async () => {

283+

const { storePath } = await makeStorePath();

284+

const staleNextRunAtMs = STORE_TEST_NOW + 3_600_000;

285+
286+

await writeSingleJobStore(storePath, {

287+

...createReloadCronJob({

288+

state: { nextRunAtMs: staleNextRunAtMs },

289+

}),

290+

});

291+
292+

const state = createStoreTestState(storePath);

293+

await ensureLoaded(state, { skipRecompute: true });

294+
295+

await writeSingleJobStore(storePath, {

296+

...createReloadCronJob({

297+

updatedAtMs: STORE_TEST_NOW,

298+

state: { nextRunAtMs: staleNextRunAtMs },

299+

}),

300+

schedule: "0 17 * * *",

301+

});

302+
303+

await expect(ensureLoaded(state, { forceReload: true, skipRecompute: true })).resolves.toBe(

304+

undefined,

305+

);

306+
307+

const reloadedJob = findJobOrThrow(state, "reload-cron-expr-job");

308+

expect(reloadedJob.schedule).toBe("0 17 * * *");

309+

expect(reloadedJob.state.nextRunAtMs).toBeUndefined();

310+

});

311+
282312

it("preserves nextRunAtMs after force reload when scheduling inputs are unchanged", async () => {

283313

const { storePath } = await makeStorePath();

284314

const originalNextRunAtMs = STORE_TEST_NOW + 3_600_000;