Fixes
- Skills/crons (writeback): new
auditsubcommand — a mechanical CronList-vs-registry diff that refresheslastSeenAlive, relinks reminders that survived under a new task id, flags ambiguous live duplicates as blocked instead of guessing, and persists the result for every other surface to read. Closes #32: a partially-executed reconcile could leave reminders "registered" but never firing, with no alert anywhere. - Hooks/reconcile-crons: the SessionStart envelope now verifies itself — audit first, recreate only what is actually missing, re-audit, retry once, and report the mechanical count to the user, with explicit "single unit of work" wording so an interleaved chat reply can't strand the reconcile halfway.
- Skills/heartbeat: always-on cron-health step (runs even outside active hours) — prunes fired one-shots, audits, and re-creates orphans every 30 minutes, so a long-running service session no longer waits days for the next restart to heal. Notifications are throttled.
- Skills/crons (writeback):
set-aliveno longer resurrects a stale.reconcilingmarker — the CronCreate bypass window only slides while a reconcile is making real progress. - Lib/doctor:
/agent:doctorreads the persisted audit and warns when reminders are registered but not firing (or blocked by ambiguous duplicates), with the exact keys and the fix; the doctor skill refreshes the audit before printing the card so the two can never contradict each other. - Docs/crons: corrected the two-sessions claim — the registry lock serializes writes; it never made a second session skip its reconcile.
Changes
- CI: new GitHub Actions workflow validates dependency install, jq availability, and shell-entrypoint parsing on ubuntu + macos for every push/PR.
Thanks @wrahman0 for the detailed #32 field report — the audit and orphan-count design follow those suggestions closely.
Full changelog: CHANGELOG.md.