Aleph
Architecture

Proactive Task Scheduling

Daemon background tasks: cron / heartbeat services; runtimes capability ledger; wake queue and storm-prevention guards; no `src/daemon/`.

The historical daemon::dispatcher module, the src/daemon/ directory, and the ProposedAction / PolicyEngine / DispatcherMode enums have all been removed. This page describes the real code in src/tasks/cron/ and src/tasks/heartbeat/, with collaboration from src/tasks/shared/, src/runtimes/, and src/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 ledgersrc/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 pipelineDeliveryEngine routes execution results to webhooks / notifications
  • Event-bus pushCronTaskChanged / HeartbeatTaskChanged events 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.rs

ScheduleKind supports:

  • Cron { expression } — 5/6-field cron expression
  • Interval { every_secs } — periodic
  • At { 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:

RPCPurpose
cron.listList all cron jobs
cron.getFetch a single job
cron.createCreate (with CronConfig.validate())
cron.updateUpdate
cron.deleteDelete
cron.statusCurrent execution state
cron.runTrigger now
cron.runsRun history
cron.toggleEnable / 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.rs

HeartbeatService 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:

RPCPurpose
heartbeat.listList all probes
heartbeat.getFetch a single probe
heartbeat.createCreate
heartbeat.updateUpdate
heartbeat.deleteDelete
heartbeat.toggleEnable / pause
heartbeat.wakeProactive wake trigger (Guardian Judge LLM decides whether to wake — payload masks secrets)
heartbeat.runsProbe 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:

  1. Probe — detect system PATH + Aleph-managed binaries
  2. Bootstrap — install missing tools via shell scripts
  3. 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/)

ModuleResponsibility
clock.rsClock trait + SystemClock: injectable clock for tests
schedule.rspure scheduling computation (next_run_at, etc.)
delivery.rsDeliveryEngine + DeliveryTarget + DeliveryPayload: cross-service delivery
reaper.rsorphan / stuck job reclamation

Divergence from historical daemon::dispatcher

  • No ProposedAction / PolicyEngine / DispatcherMode — risk evaluation, policy enforcement live in the execution engine's PermissionLayer + ContextRuleLayer + SmartFilter
  • No src/daemon/ directory — cron / heartbeat live directly under src/tasks/
  • No standalone notification queue / scheduling DAG — notifications flow through DeliveryEngine and the gateway event_bus
  • No ActionExecutor sub-component — execution reuses the existing harness via execution_engine::RunRequest

See Also

On this page