From 303873e835c983318c1404cc1cbe0bddaeef87fd Mon Sep 17 00:00:00 2001 From: ly-wang19 Date: Mon, 8 Jun 2026 18:44:40 +0800 Subject: [PATCH] refactor(cron): replace store-load double casts with raw-boundary record types ensureLoaded cast persisted rows through `as unknown as CronJob[]` and then back to `Record` per item, which mislabeled unvalidated data as CronJob. Treat the rows as raw records at the store boundary and apply a single trusted CronJob cast only after getInvalidPersistedCronJobReason passes, preserving the normalize/validate/quarantine flow. Drops two redundant casts and two lines with no behavior change. Fixes #91314 Co-Authored-By: Claude Opus 4.8 (1M context) (cherry picked from commit e9e494c7febb196e95a6b5f931bc9db79cc350d4) --- src/cron/service/store.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/cron/service/store.ts b/src/cron/service/store.ts index 82ea5e0bfa65..58eed3b0c6a8 100644 --- a/src/cron/service/store.ts +++ b/src/cron/service/store.ts @@ -104,13 +104,13 @@ export async function ensureLoaded( previousJobsById.set(job.id, job); } const loaded = await loadCronJobsStoreWithConfigJobs(state.deps.storePath); - const loadedJobs = (loaded.store.jobs ?? []) as unknown as CronJob[]; + // Persisted cron rows are validated lazily, so treat them as raw records at the + // store boundary and only trust the CronJob shape after validation below. + const loadedJobs = (loaded.store.jobs ?? []) as unknown as Record[]; const jobs: CronJob[] = []; const quarantinedConfigJobs: QuarantinedCronConfigJob[] = [...loaded.invalidConfigRows]; - for (const [index, job] of loadedJobs.entries()) { - const decodedRaw = job as unknown as Record; - const rawConfigJob = loaded.configJobs[index] ?? structuredClone(decodedRaw); - const raw = decodedRaw; + for (const [index, raw] of loadedJobs.entries()) { + const rawConfigJob = loaded.configJobs[index] ?? structuredClone(raw); const sourceIndex = loaded.configJobIndexes[index] ?? index; const runtimeEntry = loaded.configJobRuntimeEntries[index]; // Accept old `jobId` rows at the raw boundary only; the in-memory store @@ -129,11 +129,8 @@ export async function ensureLoaded( "cron: job has invalid persisted sessionTarget; run openclaw doctor --fix to repair", ); } - const hydrated = - normalized && typeof normalized === "object" ? (normalized as unknown as CronJob) : job; - const invalidReason = getInvalidPersistedCronJobReason( - hydrated as unknown as Record, - ); + const hydratedRaw = normalized ?? raw; + const invalidReason = getInvalidPersistedCronJobReason(hydratedRaw); if (invalidReason) { const quarantineEntry: QuarantinedCronConfigJob = { sourceIndex, @@ -157,6 +154,8 @@ export async function ensureLoaded( warnInvalidPersistedCronJob({ state, raw, index: sourceIndex, reason: invalidReason }); continue; } + // Validated above, so the raw record is now a trusted CronJob. + const hydrated = hydratedRaw as unknown as CronJob; jobs.push(hydrated); invalidateStaleNextRunOnScheduleChange({ previousJobsById, hydrated }); }