critic (all 3 models — HIGH):
- ExtendOnce was a single global one-shot shared across every run a System
monitors, so only the FIRST run to stall got its extension and all others
were killed by the backstop. Key the fired-state per run (RunInfo.RunID).
- Kill is now sticky: a `killed` flag short-circuits later ticks so a wavering
Escalator returning ExtendBy after a Kill can't un-collapse the deadline; a
Kill paired with Nudge/ExtendBy ignores the latter.
- watch() recovers panics from a misbehaving Escalator (logs; the run falls
back to its existing deadline) instead of silently killing the watch goroutine.
checkpoint (deepseek — HIGH): handle.Save advanced the throttle clock BEFORE
the store write, so a failed save was silently throttled away (caller believes
it persisted). Advance lastSave only after a successful persist.
schedule (all 3): compute Next BEFORE Run — a permanently-unparseable cron now
skips the job entirely instead of re-running it every tick forever; nil required
callbacks return a validate() error instead of a first-tick nil panic; Loop
recovers tick panics; the Mark-failure => possible-re-run trade-off is documented
(Run must be idempotent). + tests for each.
Triaged-but-kept: critic backstopMul<=1 floor (it's a total-runtime multiple, so
a floor >1 is intentional, not the reported footgun); checkpoint Load (nil,nil)
on miss (documented convention).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A host-agnostic ticker (Tick = one pass; Loop = run on an interval until ctx
done) that fires due jobs. Every dependency is wired by the host:
- Due lists due jobs (skill.ListDueScheduled / persona.ListScheduledAgents),
- Run executes one (run.Executor),
- Mark stamps the next fire (store.MarkScheduledRun),
- Next computes the cron next-fire (a cron lib / skill's parser).
The battery owns NO cron grammar, so it never duplicates the parser. A job whose
Run or Next errors is logged and left un-stamped (stays due, retries next tick)
— one bad job can't stall the others; only a failing Due lister is pass-fatal.
Tests: due jobs run + stamped, bad-cron job runs but isn't stamped, a failing
Run doesn't stamp or stall siblings, Due error surfaces. Core imports ZERO.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>