6370134bfe
Follow-ups on the #87259 salvage: - cron/scheduler.py: the ledger-terminal reconciliation now requires the terminal execution row's claimed_at to be >= the in-memory claim's registration time (_running_since). Without this, the latest terminal row for a recurring job is usually the PREVIOUS run's outcome — a fresh claim in the try_register_running_job -> create_execution window (or a finished run whose worker finally block hasn't released yet) would be force-released and the job double-dispatched. Unparseable/missing claimed_at fails closed to the age-based bound. - cron/scheduler.py: take the _running_job_ids snapshot for the ledger query under _running_lock — list() over a set concurrently mutated by try_register/release_running_job can raise RuntimeError. - tests: existing reconciliation tests updated to the claimed_at contract; two new race-guard tests (previous-run terminal row never releases a fresh claim; missing claimed_at fails closed). Mutation-verified: removing the ownership guard fails both.