fix(cron): report success for one-shot jobs removed after completion (#71760) - #71768
fix(cron): report success for one-shot jobs removed after completion (#71760)#71768JonthanaHanh wants to merge 1 commit into
Conversation
For finite one-shot jobs (repeat.times=1), mark_job_run removes the job from the store when completed >= times. The subsequent get_job() in _execute_job_now returns None, causing execution_success=False even though the job ran end-to-end and delivered its output. Handle the None case: if run_one_job returned True and the job no longer exists in the store, treat it as successful completion rather than failure. Fixes NousResearch#71760
yinkev
left a comment
There was a problem hiding this comment.
Blocking correctness issue: refreshed is None does not prove the run succeeded.
cron.scheduler.run_one_job() explicitly returns True when the job was processed even if the job itself failed; only an exception makes it return False. Separately, cron.jobs.mark_job_run() removes a finite one-shot once completed >= times regardless of the success argument. Therefore this sequence is valid:
run_job -> success=False
mark_job_run(job_id, False, "provider 500") -> one-shot removed
run_one_job(...) -> True # processed
get_job(job_id) -> None
With this PR, that failed run is reported as success=True and loses its error.
I reproduced the regression on head f472490ca94f with a focused test: claim_job_for_fire=True, run_one_job=True, get_job=None, expected failure. The branch returns True (assert True is False).
The fix needs a terminal outcome source independent of the deleted job record. The existing execution ledger is the clean seam: create/pass an explicit execution_id into run_one_job, then read that exact execution's completed/failed status and error after it returns. Alternatively, extend the shared run API to return a terminal outcome without changing its existing processed/not-processed boolean contract used by the scheduler. Please add both removed-success and removed-failure one-shot regressions.
|
I prepared the narrow correction on top of this PR so you can cherry-pick it rather than re-derive the ledger path: git cherry-pick 8879aac9bf4711de71053d62272fabe9f8bad9dfCommit: yinkev@8879aac It keeps the successful self-deleting one-shot behavior, but when the job record is gone it reads the immutable Focused result on your head plus this commit: 9 passed, Ruff/ |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for isolating the post-removal status path. The false Ran now: failed premise remains present on current main at tools/cronjob_tools.py:605-611.
Problems
- The changed
success=bool(processed)line is not a terminal-success signal.cron/scheduler.py:3890-3891specifies thatrun_one_job()returnsTrueeven when a job failed, whilecron/jobs.py:1739-1743removes finite jobs for either terminal outcome. A removed failed one-shot would be reported as successful and lose its error. tests/tools/test_cronjob_run_immediate.pycovers only the successful removed case, so it does not exercise that failure path.
Suggested changes
- Use
cron.executions.latest_execution(job_id)when the mutable job record is gone; its terminalcompleted/failedstate and error are available atcron/executions.py:260-262. - Add the symmetric removed-failure regression. The supplied follow-up in the discussion implements this approach.
Automated hermes-sweeper review.
| return { | ||
| "claimed": True, | ||
| "success": bool(processed), | ||
| "error": None, |
There was a problem hiding this comment.
processed is not a success result: run_one_job() returns True for a processed failed job too (cron/scheduler.py:3890-3891), and finite jobs are removed for either terminal outcome (cron/jobs.py:1739-1743). Read the execution ledger here so a removed failed one-shot remains failed and retains its error.
SummaryFour PRs address the same post-run status race: finite jobs delete their mutable job row before Related pull requests
Duplicates#63570, #71768, and #71981 implement the same missing-row-as-success approach; #71981 is an exact practical duplicate of #71768, while #76731 is the ledger-based correction rather than an equivalent duplicate. Suggested consolidationKeep #76731 open with a salvage path: add the symmetric removed-failure ledger regression and verify that its terminal error is preserved. Author action on #71768: replace the Complex graphflowchart LR
classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
classDef best stroke-width:3px,stroke:#b45309
classDef target stroke-width:3px,stroke:#4338ca
I71760(["issue #71760 (open)"])
subgraph Dup63570 ["PRs duplicating each other"]
P63570["PR #63570 (closed)"]
P71768["PR #71768 (open)"]
P71981["PR #71981 (open)"]
end
P71768 -->|best fix| I71760
class I71760 open
class P63570 closed
class P71768 open
class P71981 open
class P71768 best
class P71768 target
click I71760 "https://github.com/NousResearch/hermes-agent/issues/71760"
click P63570 "https://github.com/NousResearch/hermes-agent/pull/63570"
click P71768 "https://github.com/NousResearch/hermes-agent/pull/71768"
click P71981 "https://github.com/NousResearch/hermes-agent/pull/71981"
Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label). Cross-PR triage: Reviewed 4 pull requests and 1 issue in this complex. Each diff was read against this issue; Assessment working set: 14 kB of PR diffs, 8 kB of issue/PR text, 6 kB of discussion (9 comments), 6 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch. |
Summary
For finite one-shot jobs (
repeat.times=1),mark_job_runremoves the job from the store whencompleted >= times. The subsequentget_job()in_execute_job_nowreturnsNone, causingexecution_success=Falseeven though the job ran end-to-end and delivered its output.This produced a false
Ran now: failed.message when the operator manually triggered a one-shot job viahermes cron run.Root Cause
In
tools/cronjob_tools.py::_execute_job_now(line 642-643):When
mark_job_rundeletes the completed one-shot job,get_jobreturnsNone, sorefreshedbecomes{}, andlast_statusis never"ok". The fix handles theNonecase by treating job removal (afterrun_one_jobreturnedTrue) as implicit success.Changes
tools/cronjob_tools.py: Handleget_jobreturningNonefor completed one-shot jobs in_execute_job_nowtests/tools/test_cronjob_run_immediate.py: Add testtest_execute_job_now_handles_oneshot_removalverifying the fixTest Plan
test_execute_job_now_handles_oneshot_removalpassesFixes #71760