fix(cron): detect ghost runs on main-session systemEvent jobs#63111
fix(cron): detect ghost runs on main-session systemEvent jobs#63111liaoandi wants to merge 3 commits into
Conversation
When the gateway is unhealthy, main-session cron jobs configured with payload.kind=systemEvent complete in under 50ms and are recorded as ok even though no agent turn was executed. Add a GHOST_RUN_THRESHOLD_MS constant and emit a structured warn log when a run completes suspiciously fast, so operators can identify silent failures. Fixes openclaw#63106
Greptile SummaryThis PR adds ghost-run detection to the
Confidence Score: 4/5Safe to merge after fixing the One P1 finding: the guard condition compares src/gateway/server-cron.ts — line 519, the
|
| typeof evt.durationMs === "number" && | ||
| evt.durationMs < GHOST_RUN_THRESHOLD_MS && | ||
| job && | ||
| job.sessionTarget !== "none" && |
There was a problem hiding this comment.
!== "none" compares against an invalid CronSessionTarget value
CronSessionTarget is "main" | "isolated" | "current" | \session:${string}`—"none"is never a valid member, so this condition is alwaystrue. TypeScript strict comparison checks will flag this as an impossible narrowing. The root cause described in the PR is specifically sessionTarget === "main", which is the only target that can legally carry payload.kind === "systemEvent"(enforced byassertSupportedJobSpec`). Use the positive check to match the documented intent:
| job.sessionTarget !== "none" && | |
| job.sessionTarget === "main" && |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/server-cron.ts
Line: 519
Comment:
**`!== "none"` compares against an invalid `CronSessionTarget` value**
`CronSessionTarget` is `"main" | "isolated" | "current" | \`session:${string}\`` — `"none"` is never a valid member, so this condition is always `true`. TypeScript strict comparison checks will flag this as an impossible narrowing. The root cause described in the PR is specifically `sessionTarget === "main"`, which is the only target that can legally carry `payload.kind === "systemEvent"` (enforced by `assertSupportedJobSpec`). Use the positive check to match the documented intent:
```suggestion
job.sessionTarget === "main" &&
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a47179a47a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| job.sessionTarget !== "none" && | ||
| job.payload.kind === "systemEvent" |
There was a problem hiding this comment.
Exclude busy main-lane fast path from ghost-run warning
This warning path will fire for healthy runs that intentionally return quickly: executeMainSessionCronJob returns status: "ok" immediately for recurring main-session jobs when runHeartbeatOnce reports requests-in-flight (see src/cron/service/timer.ts around the recurring-job branch), so sub-50ms durations are expected under normal load. Because this check keys only on durationMs < 50 plus systemEvent, it produces false “gateway may be unhealthy” alerts during routine contention, which can drown out real outage signals.
Useful? React with 👍 / 👎.
…next-heartbeat - Replace `!== "none"` (invalid type) with `=== "main"` (P1) - Add `wakeMode === "next-heartbeat"` to exclude wakeMode "now" fast path - Add wakeMode to structured warn fields for triage - Document requests-in-flight false-positive tradeoff (P2) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 971215ed8c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| evt.durationMs < GHOST_RUN_THRESHOLD_MS && | ||
| job && | ||
| job.sessionTarget === "main" && | ||
| job.payload.kind === "systemEvent" && | ||
| job.wakeMode === "next-heartbeat" |
There was a problem hiding this comment.
Avoid warning on healthy next-heartbeat jobs
This condition treats durationMs < 50 as evidence of a ghost run, but for sessionTarget: "main" + payload.kind: "systemEvent" + wakeMode: "next-heartbeat" that fast return is the normal path: executeMainSessionCronJob enqueues the event, calls requestHeartbeatNow, and returns without waiting for processing (src/cron/service/timer.ts). As a result, healthy jobs in this mode will repeatedly emit cron: possible ghost run detected, creating noisy false alarms that make real gateway-health warnings harder to trust.
Useful? React with 👍 / 👎.
next-heartbeat mode is fire-and-forget — every healthy invocation completes in <50 ms, so a duration check produces 100% false positives. Recurring now-mode jobs are also excluded because the busy-lane early return (openclaw#58833) is a legitimate fast path, not a ghost run. Addresses review feedback: - P2: exclude busy main-lane fast path - P2: avoid warning on healthy next-heartbeat jobs Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
|
Closing this as not reproducible on current Close as What I checked:
Review notes: reviewed against 245451b6a9c5. |
Summary
sessionTarget: "main"+payload.kind: "systemEvent"complete in < 50 ms and are recorded asstatus: okeven though no agent turn was executedGHOST_RUN_THRESHOLD_MS = 50constant and a structuredwarnlog in theonEventhandler insrc/gateway/server-cron.tswhen a run completes suspiciously fastjobId,durationMs,sessionTarget,payloadKind, andghostRunThresholdMsso operators can identify silent failuresFixes #63106
Root Cause
executeMainSessionCronJobinsrc/cron/service/timer.tscallsenqueueSystemEvent()and returns{ status: "ok" }immediately without waiting for the agent to process the heartbeat. When the gateway is down or the agent session is unhealthy, the event is silently dropped but the run is still logged asok.Changes
src/gateway/server-cron.ts: AddedGHOST_RUN_THRESHOLD_MSconstant and ghost-run detection warning in theonEvent/appendCronRunLogpathTest plan
cron: possible ghost run detectedin the log with the structured fields🤖 Generated with Claude Code