Proactive Task Scheduling
Daemon background tasks: cron / heartbeat services; runtimes capability ledger; wake queue and storm-prevention guards; no `src/daemon/`.
The historical
daemon::dispatchermodule, thesrc/daemon/directory, and theProposedAction/PolicyEngine/DispatcherModeenums have all been removed. This page describes the real code insrc/tasks/cron/andsrc/tasks/heartbeat/, with collaboration fromsrc/tasks/shared/,src/runtimes/, andsrc/components/.
Proactive background task execution is now served by two orthogonal services:
- Cron (
src/tasks/cron/) — scheduled tasks triggered by cron expressions / intervals / one-shots - Heartbeat (
src/tasks/heartbeat/) — periodic probes + L1 probe + L2 LLM judge
They share src/tasks/shared/ (clock / delivery / schedule / reaper).
Overview
Proactive scheduling enables:
- Cron schedules — cron expressions / intervals / one-shots / chain triggers (
on_success/on_failure) - Heartbeat probes — periodic L1 probes + proactive wake, decided by the LLM judge
- Runtimes capability ledger —
src/runtimes/manages external tool capabilities (python / node / uv / ffmpeg / yt-dlp …) via a three-phase Probe → Bootstrap → Register flow, persisted to~/.aleph/runtimes/ledger.json - Failure alert / delivery pipeline —
DeliveryEngineroutes execution results to webhooks / notifications - Event-bus push —
CronTaskChanged/HeartbeatTaskChangedevents replace panel polling
Cron (src/tasks/cron/)
src/tasks/cron/
├── mod.rs # entry; re-exports CronService / CronJob / ScheduleKind / etc.
├── config.rs # CronJob / CronJobView / ScheduleKind / JobStateV2 / CronConfig
├── store.rs # JSON atomic persistence
├── executor.rs # executes a job (runs the harness)
├── history.rs # run history
├── alert.rs # FailureAlertConfig / FailureAlert delivery
├── chain.rs # on_success / on_failure triggers (with cycle detection)
├── template.rs # Prompt template rendering (variable substitution)
├── stagger.rs # hash-based stagger across many cron jobs
├── carryover.rs # cross-daemon-restart carryover
├── webhook_target.rs # webhook delivery target
└── service/ # ServiceState / ops / timer / concurrency / catchup
├── mod.rs
├── ops.rs
├── timer.rs
├── concurrency.rs
└── catchup.rsScheduleKind supports:
Cron { expression }— 5/6-field cron expressionInterval { every_secs }— periodicAt { timestamp }— one-shot (auto-disabled after firing)
Chain triggers (on_success / on_failure) register follow-up jobs in chain.rs; cycle detection blocks self-loops.
After execution, DeliveryEngine delivers results via DeliveryTarget::Webhook | DeliveryTarget::Notification | ...; FailureAlertConfig controls failure-alert cadence and channel.
Startup wiring
The full RPC surface is wired by src/bin/aleph-server/commands/start/builder/handlers/agents.rs::register_cron_handlers at startup, replacing cron.*_stub:
| RPC | Purpose |
|---|---|
cron.list | List all cron jobs |
cron.get | Fetch a single job |
cron.create | Create (with CronConfig.validate()) |
cron.update | Update |
cron.delete | Delete |
cron.status | Current execution state |
cron.run | Trigger now |
cron.runs | Run history |
cron.toggle | Enable / pause |
Each successful write also publishes GatewayEventFrame::CronJobChanged to gateway::event_bus so the panel updates without polling. carryover::bootstrap_global_carryover_sweeper() spawns the carry-over sweeper exactly once at first construction (guarded by OnceLock).
Heartbeat (src/tasks/heartbeat/)
src/tasks/heartbeat/
├── mod.rs # entry; re-exports HeartbeatService / HeartbeatTask
├── config.rs # HeartbeatConfig / HeartbeatTask / HeartbeatTaskView
├── store.rs # persistence
├── executor.rs # executes a probe
├── probe.rs # L1 probe
├── dedup.rs # deduplication (avoid repeated triggers)
├── wake.rs # WakeQueue
├── history.rs # probe history
└── service/ # HeartbeatServiceState / ops / timer
├── mod.rs
├── ops.rs
└── timer.rsHeartbeatService owns state + wake_queue + optional event_bus. The wake_queue is the external proactive-wake trigger — the daemon detects an event and wants an immediate probe.
The full RPC surface is wired by register_heartbeat_handlers:
| RPC | Purpose |
|---|---|
heartbeat.list | List all probes |
heartbeat.get | Fetch a single probe |
heartbeat.create | Create |
heartbeat.update | Update |
heartbeat.delete | Delete |
heartbeat.toggle | Enable / pause |
heartbeat.wake | Proactive wake trigger (Guardian Judge LLM decides whether to wake — payload masks secrets) |
heartbeat.runs | Probe history |
Writes also publish HeartbeatTaskChanged to gateway::event_bus.
Guardian Judge
The LLM judge for heartbeat probes:
- 26.7.21+: payload masks secrets — sensitive credentials never appear in the judge prompt
- Heartbeat probes refuse dangerous tools and confirmation-gated tools
Runtimes capability ledger (src/runtimes/)
// src/runtimes/mod.rs
pub mod bootstrap;
mod capability;
pub mod ensure;
pub mod ledger;
pub mod os;
pub mod post_install;
pub mod probe;
pub mod specs;
pub use bootstrap::{dependencies, has_spec, install, BootstrapError, BootstrapResult};
pub use capability::{format_entries_for_prompt, RuntimeCapability};
pub use ensure::ensure_capability;
pub use ledger::{CapabilityEntry, CapabilityLedger, CapabilitySource, CapabilityStatus};
pub use os::TargetOs;
pub use post_install::PostInstallError;
pub use probe::ProbeResult;
pub use specs::{
find_spec, select_install, supported_on_current_os,
InstallStrategy, OsInstall, PostInstallAction, RuntimeSpec, SPECS,
};
pub fn get_runtimes_dir() -> Result<PathBuf>; // ~/.aleph/runtimes/CapabilityLedger replaces the legacy RuntimeRegistry and only tracks state of each capability (Missing | Probing | Bootstrapping | Ready | Stale); it never downloads or installs anything — those responsibilities belong to Bootstrapper and Prober separately. Persisted to ~/.aleph/runtimes/ledger.json.
Three-phase flow:
- Probe — detect system PATH + Aleph-managed binaries
- Bootstrap — install missing tools via shell scripts
- Ledger — persist state to
ledger.json
ensure_capability(name, &ledger) -> Result<bin_path> is the single entry the exec layer uses for PATH injection.
Shared domain types (src/components/)
src/components/ is now a thin shell: mod.rs + types/. The legacy EventHandler chain (IntentAnalyzer / TaskPlanner / ToolExecutor / LoopController / SessionRecorder / SessionCompactor) was removed during the Harness migration; only the shared domain types remain and are consumed by the event system.
Shared infrastructure (src/tasks/shared/)
| Module | Responsibility |
|---|---|
clock.rs | Clock trait + SystemClock: injectable clock for tests |
schedule.rs | pure scheduling computation (next_run_at, etc.) |
delivery.rs | DeliveryEngine + DeliveryTarget + DeliveryPayload: cross-service delivery |
reaper.rs | orphan / stuck job reclamation |
Divergence from historical daemon::dispatcher
- No
ProposedAction/PolicyEngine/DispatcherMode— risk evaluation, policy enforcement live in the execution engine'sPermissionLayer+ContextRuleLayer+SmartFilter - No
src/daemon/directory — cron / heartbeat live directly undersrc/tasks/ - No standalone notification queue / scheduling DAG — notifications flow through
DeliveryEngineand the gatewayevent_bus - No
ActionExecutorsub-component — execution reuses the existing harness viaexecution_engine::RunRequest
See Also
- Daemon concept — daemon service management
- Cron Jobs — full cron configuration
- Heartbeat — full heartbeat configuration
- Runtimes — capability ledger and bootstrap
- Event bus —
CronTaskChanged/HeartbeatTaskChangedpush
loop_graph Governance Topology
The loop_graph governance topology layered over the core, and its relationship with the graph_topology prompt layer.
Resilience System
SQLite persistence core, StateDatabase, skeleton/pulse event model, and task/trace types — the storage substrate that backs Shadow Replay and risk-aware recovery.