Skip to content

fix(cron): resolve lastRunStatus in cron list/show human output#93245

Merged
vincentkoc merged 1 commit into
openclaw:mainfrom
ly-wang19:fix/cron-status-resolver-dedup
Jun 15, 2026
Merged

fix(cron): resolve lastRunStatus in cron list/show human output#93245
vincentkoc merged 1 commit into
openclaw:mainfrom
ly-wang19:fix/cron-status-resolver-dedup

Conversation

@ly-wang19

Copy link
Copy Markdown
Contributor

Summary

openclaw cron list and openclaw cron show print idle for a job whose real status is ok / error / skipped whenever the job's state carries only lastRunStatus (the primary field) and not the deprecated lastStatus alias.

The human-render helper formatStatus (src/cli/cron-cli/shared.ts) resolved the status as state.lastStatus ?? "idle", omitting lastRunStatus — while the sibling computeStatus (used for the --json status field) and every other status resolver in the codebase use state.lastRunStatus ?? state.lastStatus ?? "idle". computeStatus's own JSDoc claims it "mirrors the human-readable status shown by cron list / cron show" — but formatStatus had diverged from it. lastRunStatus is the canonical field; lastStatus is @deprecated (src/cron/types.ts).

Root cause + fix

formatStatus was a near-duplicate of computeStatus that was never updated when lastRunStatus became the primary field. This change deletes formatStatus and routes both human-render call sites (the cron list Status column and the cron show status: line) through the single canonical computeStatus — one resolver, honoring the documented "mirror" contract. Net −8 LOC in source.

Change Type

  • Bug fix (also removes duplicated logic)

Linked Issue

Source-discovered (no existing issue/PR — verified). The human cron list/cron show status now matches the --json status field and the rest of the status-resolution code.

Evidence

  • Failing before / passing after. New test renders lastRunStatus (matching the --json status), not idle, when lastStatus is unset builds a job with state: { lastRunStatus: "ok" } (no lastStatus) and asserts cron show prints status: ok (not status: idle), the cron list Status column shows ok, and enrichCronJsonWithStatus agrees (status: "ok"). On main the human render returns idle, so the test fails without this patch. Full file: 29/29 tests pass (Node 22.22.1). oxlint + oxfmt clean; tsgo:core clean.

Real behavior proof

Behavior addressed: cron list/cron show human output now shows a job's real run status (ok/error/skipped) when only lastRunStatus is set, instead of idle, matching the --json status field.

Real environment tested: local OpenClaw dev gateway (gateway run --dev --auth none) on Node 22.22.1 (macOS), isolated OPENCLAW_STATE_DIR/OPENCLAW_HOME. A real cron job was created, then given a lastRunStatus-only state via a cron.update state patch (allowed by CronJobStatePatchSchema), then rendered by cron show/cron list on main vs this patch.

Exact steps or command run after this patch:

$ openclaw cron add --every 1h --session isolated --message hi --name statusbug-proof
# inject lastRunStatus-only state (cron.update patch.state = { lastRunStatus: "ok" }) -> job state becomes {"nextRunAtMs":..., "lastRunStatus":"ok"}  (no lastStatus)
$ openclaw cron show <job-id>
$ openclaw cron list --json

Evidence after fix: live gateway terminal output for the same lastRunStatus-only job, on main vs this patch:

# BEFORE (origin/main) — human render diverges from --json:
$ openclaw cron show <job-id>      ->  status: idle
$ openclaw cron list --json        ->  status: ok        (human "idle" vs json "ok" — DIVERGENT)

# AFTER (this patch) — human render matches --json:
$ openclaw cron show <job-id>      ->  status: ok
$ openclaw cron list --json        ->  status: ok        (consistent)

Observed result after fix: cron show/cron list report the real status (ok) for the lastRunStatus-only job; before the patch they reported idle while --json reported ok. Normal jobs (which dual-write both fields) are unaffected.

What was not tested: a job reaching the lastRunStatus-only state purely through the live scheduler (the runtime dual-writes both lastRunStatus and lastStatus, so the divergence was reproduced by writing the canonical field alone via the supported cron.update state patch — exactly the field shape the deprecated-alias migration moves toward).

Security Impact

None — display/formatting consolidation only. No new permissions, secrets, network calls, or tool-exec surface.

Compatibility / Migration

Backward compatible. Output is unchanged for every job that has lastStatus set (all normally-run jobs); only the previously-wrong idle rendering for lastRunStatus-only state changes, to the correct status.

AI assistance

AI-assisted (Claude Code). Verified locally: 29/29 unit tests, oxlint clean, oxfmt clean, tsgo:core clean, plus the live before/after gateway proof above. Author understands the change.

cron list/show printed "idle" for a job whose status is ok/error/skipped
when only lastRunStatus (the primary field) was set: formatStatus used
`lastStatus ?? "idle"` and omitted lastRunStatus, diverging from computeStatus
(the --json status resolver) whose JSDoc says it mirrors the human output.
Delete the duplicate formatStatus and render via the canonical computeStatus.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@openclaw-barnacle openclaw-barnacle Bot added cli CLI command changes size: S proof: supplied External PR includes structured after-fix real behavior proof. labels Jun 15, 2026
@clawsweeper

clawsweeper Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 15, 2026, 6:17 AM ET / 10:17 UTC.

Summary
The PR routes cron list and cron show human status output through the existing JSON status resolver and adds a regression test for lastRunStatus-only state.

PR surface: Source -8, Tests +35. Total +27 across 2 files.

Reproducibility: yes. from source inspection: a job with state.lastRunStatus set and no state.lastStatus gets the correct JSON status but current human output can render idle because the human helper ignores lastRunStatus. I did not execute the CLI locally because this review was required to keep the checkout read-only.

Review metrics: none identified.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Next step before merge

  • No ClawSweeper repair lane is needed because the branch is reviewable as-is and no actionable patch finding was found.

Security
Cleared: The diff only changes cron CLI rendering and a colocated test; it adds no dependencies, permissions, secret handling, network calls, workflow changes, or code-execution surface.

Review details

Best possible solution:

Merge the narrow resolver consolidation after normal maintainer and CI gates so cron human and JSON status output share one canonical implementation.

Do we have a high-confidence way to reproduce the issue?

Yes from source inspection: a job with state.lastRunStatus set and no state.lastStatus gets the correct JSON status but current human output can render idle because the human helper ignores lastRunStatus. I did not execute the CLI locally because this review was required to keep the checkout read-only.

Is this the best way to solve the issue?

Yes. Reusing computeStatus is the best narrow fix because it deletes duplicate resolver logic and makes the documented human/JSON mirror contract a single code path.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 0314819f918a.

Label changes

Label changes:

  • add P2: This fixes a bounded CLI status-display bug with limited blast radius and clear regression coverage.
  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body provides structured after-fix terminal proof from a local OpenClaw dev gateway showing cron show changing from idle to ok for a lastRunStatus-only job, with tests covering the shared cron list renderer.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🦞 diamond lobster.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body provides structured after-fix terminal proof from a local OpenClaw dev gateway showing cron show changing from idle to ok for a lastRunStatus-only job, with tests covering the shared cron list renderer.

Label justifications:

  • P2: This fixes a bounded CLI status-display bug with limited blast radius and clear regression coverage.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body provides structured after-fix terminal proof from a local OpenClaw dev gateway showing cron show changing from idle to ok for a lastRunStatus-only job, with tests covering the shared cron list renderer.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body provides structured after-fix terminal proof from a local OpenClaw dev gateway showing cron show changing from idle to ok for a lastRunStatus-only job, with tests covering the shared cron list renderer.
Evidence reviewed

PR surface:

Source -8, Tests +35. Total +27 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 8 16 -8
Tests 1 35 0 +35
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 43 16 +27

What I checked:

  • Current-main divergence: computeStatus resolves disabled/running state and then state.lastRunStatus ?? state.lastStatus ?? "idle", while the human helper below still falls back to only state.lastStatus ?? "idle". (src/cli/cron-cli/shared.ts:157, 0314819f918a)
  • Human output call sites: printCronList and printCronShow on current main call formatStatus, so a state containing only lastRunStatus can render as idle in human output. (src/cli/cron-cli/shared.ts:453, 0314819f918a)
  • State contract: CronJobState names lastRunStatus as the preferred execution outcome field and marks lastStatus deprecated. (src/cron/types.ts:287, 0314819f918a)
  • Protocol accepts primary-field patches: CronJobStatePatchSchema allows lastRunStatus, matching the PR body's real repro path that patches only the canonical status field. (packages/gateway-protocol/src/schema/cron.ts:407, 0314819f918a)
  • Sibling resolvers use the same precedence: Cron store and service resolvers already prefer lastRunStatus over lastStatus, so the PR aligns the CLI human surface with sibling cron status handling. (src/cron/store/state-codec.ts:34, 0314819f918a)
  • Documented mirror contract: The cron docs state that JSON status is computed from enabled, state.runningAtMs, and state.lastRunStatus, and mirrors the human-readable status column. Public docs: docs/cli/cron.md. (docs/cli/cron.md:316, 0314819f918a)

Likely related people:

  • forgedthought: Authored the commit in merged pull request feat(cron): add computed status field to --json output #78701 that added enrichCronJsonWithStatus and computeStatus to the cron CLI shared module. (role: introduced JSON status resolver; confidence: high; commits: 01441ab90512; files: src/cli/cron-cli/shared.ts, src/cli/cron-cli/register.cron-add.ts, src/cli/cron-cli/register.cron-simple.ts)
  • aweiker: Opened merged pull request feat(cron): add computed status field to --json output #78701 for the computed cron JSON status feature whose mirror contract this PR repairs. (role: feature owner; confidence: medium; commits: 01441ab90512, 36f7b7e20df0, 86c6907551e0; files: src/cli/cron-cli/shared.ts)
  • vincentkoc: Merged the computed cron JSON status pull request that established the current computeStatus path and documented mirror behavior. (role: merger; confidence: medium; commits: 01441ab90512; files: src/cli/cron-cli/shared.ts)
  • Alix-007: Authored recent merged cron filter/status work that also resolves lastRunStatus ?? lastStatus, making them relevant for sibling status behavior. (role: recent adjacent contributor; confidence: medium; commits: 2a9173bb9b6e; files: src/cron/service/ops.ts, ui/src/ui/controllers/cron.ts, ui/src/ui/views/cron.ts)
  • steipete: Contributed to and merged recent cron jobs filter/status work in fix(cron): complete jobs filters #87620, which shares the lastRunStatus precedence invariant. (role: adjacent contributor and merger; confidence: medium; commits: 6711e4ef9db6, 579663c5a438, 033dd486ea05; files: src/cron/service/ops.ts, ui/src/ui/controllers/cron.ts, ui/src/ui/views/cron.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal backlog priority with limited blast radius. labels Jun 15, 2026
@vincentkoc vincentkoc self-assigned this Jun 15, 2026
@vincentkoc
vincentkoc merged commit 53541b2 into openclaw:main Jun 15, 2026
216 of 225 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 16, 2026
…claw#93245)

cron list/show printed "idle" for a job whose status is ok/error/skipped
when only lastRunStatus (the primary field) was set: formatStatus used
`lastStatus ?? "idle"` and omitted lastRunStatus, diverging from computeStatus
(the --json status resolver) whose JSDoc says it mirrors the human output.
Delete the duplicate formatStatus and render via the canonical computeStatus.

Co-authored-by: ly-wang19 <[email protected]>
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
crh-code pushed a commit to crh-code/openclaw that referenced this pull request Jun 18, 2026
…claw#93245)

cron list/show printed "idle" for a job whose status is ok/error/skipped
when only lastRunStatus (the primary field) was set: formatStatus used
`lastStatus ?? "idle"` and omitted lastRunStatus, diverging from computeStatus
(the --json status resolver) whose JSDoc says it mirrors the human output.
Delete the duplicate formatStatus and render via the canonical computeStatus.

Co-authored-by: ly-wang19 <[email protected]>
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
badgerbees pushed a commit to badgerbees/openclaw that referenced this pull request Jul 8, 2026
…claw#93245)

cron list/show printed "idle" for a job whose status is ok/error/skipped
when only lastRunStatus (the primary field) was set: formatStatus used
`lastStatus ?? "idle"` and omitted lastRunStatus, diverging from computeStatus
(the --json status resolver) whose JSDoc says it mirrors the human output.
Delete the duplicate formatStatus and render via the canonical computeStatus.

Co-authored-by: ly-wang19 <[email protected]>
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli CLI command changes P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants