You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Addresses teknium1's review on NousResearch#75833 (targets the fix/cron-runtime-ownership branch so it folds into that PR).
The runtime-ownership work retains a repeat-exhausted job as a declaration but hides it behind a runtime_tombstone, and get_job / resolve_job_ref / list_jobs filtered those records unconditionally — so a completed declaration could not be listed, removed, edited, or revived through any supported surface. This PR adds explicit terminal-declaration management pathways, fixes the revival semantics so a revived job actually fires, and documents the definition/runtime split and paired-backup behavior.
Related Issue
Fixes the two review findings on NousResearch#75833 (hidden runtime tombstones; missing documentation for the definition/runtime split).
Type of Change
🐛 Bug fix (non-breaking change that fixes an issue)
📝 Documentation update
✅ Tests (adding or improving test coverage)
Changes Made
cron/jobs.py
get_job, resolve_job_ref, list_jobs gain a keyword-only include_completed opt-in; tombstoned records surface with state: "completed" via _normalize_job_record.
remove_job resolves completed declarations; resume_job / trigger_job revive them — clearing the tombstone and resetting the repeat budget (_revive_updates) so the dispatch-limit guard can't immediately re-tombstone the fire. resume_job raises an actionable error for a completed past one-shot.
_reconcile_runtime_state: a definition-edit revival now also resets repeat.completed and clears claims — previously a prompt-only edit revived the job with an exhausted budget, and the next due scan re-tombstoned it (stale_dispatch_limit) without ever firing.
update_job re-reads the record after save so callers see reconciled state instead of a stale tombstone or repeat count.
The shared resolve now reaches terminal declarations; pause returns an actionable error for them; run revives before executing.
Post-run readbacks in _execute_job_now / the run action use include_completed=True — a finite one-shot tombstones itself inside mark_job_run, and the filtered readback misreported a successful final fire as success: false with an empty job record.
hermes_cli/cron.py: hermes cron list --all includes completed jobs (the [completed] render branch was previously unreachable); hermes cron edit resolves them.
Docs: website/docs/developer-guide/cron-internals.md (definition/runtime split, tombstone lifecycle and management pathways, paired backup/restore section), architecture.md (cron flow), user-guide/features/cron.md (job storage split, completed-jobs section, back-up-both-files warning), guides/cron-troubleshooting.md (runtime.db in the permissions check).
Tests: new tests/cron/test_tombstone_management.py — 10 end-to-end regressions covering list/get/resolve opt-ins, removal (definition + runtime row), revival via resume/trigger/edit (including the insta-retombstone trap and re-completion after revival), the no-op-update-keeps-completed contract, and the full tool-level list → pause-guard → resume → remove pathway.
Manual: create a one-shot (repeat 1), let it fire; hermes cron list hides it, hermes cron list --all shows it as [completed]; hermes cron resume <id> / run <id> revives it (repeat back to 0/1); hermes cron remove <id> deletes the declaration.
ruff check, compileall, and scripts/check-windows-footguns.py pass on the changed files.
CI note: the Check contributors / check-attribution failure is pre-existing and not caused by this diff.
The check computes git merge-base origin/main HEAD, and this fork's main is at 49c3c2e — far behind the upstream main that fix/cron-runtime-ownership branched from. The scan therefore covers ~19.6k upstream commits (2,429 distinct author emails), and the 21 unmapped emails it reports (jquesnelle, nousbot, etc.) are all already present on the base branch — the identical scan over fix/cron-runtime-ownership alone yields the same set. The one email this PR's commit adds (noreply@anthropic.com) is in the check's ignore list (scripts/release.py).
Fix options outside this PR's scope: fast-forward the fork's main to upstream (the merge-base then collapses to the real fork point), or add the 21 mapping files via scripts/add_contributor.py. The check will pass upstream, where main is current.
Closing as superseded: the base branch (fix/cron-runtime-ownership) now carries an equivalent fix for the same sweeper finding — commits 7034a85 (terminal-management path + docs) and dd709c5 (review-driven coherence hardening, now f7dce23 after rebase) — developed independently and already posted to NousResearch#75833.
Harvested from this PR into the branch (thanks — these were real catches the other pass missed):
website/docs/developer-guide/architecture.md cron-flow diagram and website/docs/guides/cron-troubleshooting.md permissions section (both still described the single-file store)
isinstance(tombstone, dict) guards on tombstone reads
listing a completed job on the completed opt-in regardless of the disabled filter
Deliberate divergences from this PR's approach, for the record:
Revive semantics: this PR kept "any operator edit revives" and reset the repeat budget alongside the tombstone pop. The branch instead requires a cadence edit (schedule/repeat/enabled) to revive — a prompt-typo fix on a completed job should not silently re-arm unattended runs. Non-cadence edits persist but leave the job completed, and the response says so.
run on a completed job: this PR treats it as an explicit revive-and-fire; the branch returns an explicit terminal error pointing at update/remove, for the same reason — accidental resurrection of a finished job is the costlier failure mode for an unattended scheduler.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Addresses teknium1's review on NousResearch#75833 (targets the
fix/cron-runtime-ownershipbranch so it folds into that PR).The runtime-ownership work retains a repeat-exhausted job as a declaration but hides it behind a
runtime_tombstone, andget_job/resolve_job_ref/list_jobsfiltered those records unconditionally — so a completed declaration could not be listed, removed, edited, or revived through any supported surface. This PR adds explicit terminal-declaration management pathways, fixes the revival semantics so a revived job actually fires, and documents the definition/runtime split and paired-backup behavior.Related Issue
Fixes the two review findings on NousResearch#75833 (hidden runtime tombstones; missing documentation for the definition/runtime split).
Type of Change
Changes Made
cron/jobs.pyget_job,resolve_job_ref,list_jobsgain a keyword-onlyinclude_completedopt-in; tombstoned records surface withstate: "completed"via_normalize_job_record.remove_jobresolves completed declarations;resume_job/trigger_jobrevive them — clearing the tombstone and resetting the repeat budget (_revive_updates) so the dispatch-limit guard can't immediately re-tombstone the fire.resume_jobraises an actionable error for a completed past one-shot._reconcile_runtime_state: a definition-edit revival now also resetsrepeat.completedand clears claims — previously a prompt-only edit revived the job with an exhausted budget, and the next due scan re-tombstoned it (stale_dispatch_limit) without ever firing.update_jobre-reads the record after save so callers see reconciled state instead of a stale tombstone or repeat count.tools/cronjob_tools.pylistgainsinclude_completed(schema + handler);_format_jobexposescompleted_at/completed_reason.pausereturns an actionable error for them;runrevives before executing._execute_job_now/ therunaction useinclude_completed=True— a finite one-shot tombstones itself insidemark_job_run, and the filtered readback misreported a successful final fire assuccess: falsewith an empty job record.hermes_cli/cron.py:hermes cron list --allincludes completed jobs (the[completed]render branch was previously unreachable);hermes cron editresolves them.website/docs/developer-guide/cron-internals.md(definition/runtime split, tombstone lifecycle and management pathways, paired backup/restore section),architecture.md(cron flow),user-guide/features/cron.md(job storage split, completed-jobs section, back-up-both-files warning),guides/cron-troubleshooting.md(runtime.db in the permissions check).tests/cron/test_tombstone_management.py— 10 end-to-end regressions covering list/get/resolve opt-ins, removal (definition + runtime row), revival via resume/trigger/edit (including the insta-retombstone trap and re-completion after revival), the no-op-update-keeps-completed contract, and the full tool-level list → pause-guard → resume → remove pathway.How to Test
scripts/run_tests.sh tests/cron tests/tools/test_cronjob_run_immediate.py tests/tools/test_cronjob_schema.py tests/hermes_cli/test_backup.py tests/hermes_cli/test_cron.py tests/hermes_cli/test_web_server_cron_profiles.py tests/gateway/test_api_server_jobs.py -q→ 506 passed, 0 failed.repeat1), let it fire;hermes cron listhides it,hermes cron list --allshows it as[completed];hermes cron resume <id>/run <id>revives it (repeat back to0/1);hermes cron remove <id>deletes the declaration.ruff check,compileall, andscripts/check-windows-footguns.pypass on the changed files.Checklist
Code
fix(scope):,feat(scope):, etc.)Documentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — N/A (no config keys changed)CONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — N/Acronjobschema documentsinclude_completedand completed-job managementScreenshots / Logs
🤖 Generated with Claude Code
https://claude.ai/code/session_01UQr9bocSz9QnBak8rfh5Ng
Generated by Claude Code