d1afa16053
mark_job_run popped a finite one-shot from jobs.json the moment its repeat limit was reached and returned early — discarding the last_status / last_error / last_delivery_error it had just written. Every finished one-shot vanished from `cronjob action=list` with no inspectable record, and a delivery failure (agent succeeded, platform send failed) was silently thrown away with it. Changes: - mark_job_run now retires a limit-reached one-shot as a terminal record (state="completed", enabled=False, next_run_at=None) — mirroring the existing next_run_at-is-None terminal branch — so the final status and any delivery error persist and surface in the cronjob tool's list output (which already emits last_delivery_error and defaults to include_disabled=True). - claim_dispatch's stale-job cleanup marks already-ran jobs completed instead of popping them; genuinely wedged claims (last_run_at never written) are still removed with the operator-visible diagnostic. - Retention sweep in the due scan prunes completed one-shot records older than cron.completed_retention_days (default 7; non-positive disables) so jobs.json cannot grow unboundedly. Recurring jobs and non-terminal one-shots are never candidates. Tests: completion retains record + delivery error, list surfaces it, completed jobs never re-dispatch, sweep prunes old / keeps recent / ignores recurring / honors the disable knob; recurring lifecycle unchanged.