---
name: heartbeat-pulse
title: Heartbeat Pulse — Adaptive Pacing
description: Engine substrate controlling heartbeat loop cadence based on time-of-day and live workload signals
triggers: []
category: _meta
status: active
priority: 0
author: system
---

# Heartbeat Pulse

This is an **engine substrate** — it's never injected into an agent's prompt.
The heartbeat loop (`automation/heartbeat.py`) reads `config/pacing.yaml` at
runtime to decide how long to sleep before each tick.

## What this skill owns

Every knob that shapes heartbeat cadence lives in `config/pacing.yaml`:

- **Enable flag**: pacing is off by default. When disabled, the heartbeat
  runs on its clock-aligned base interval exactly as before this skill existed.
- **Quiet hours**: a local-time window (e.g. the user's sleep hours) when
  the loop slows down. Multiplier typically `4.0` — a 15-minute heartbeat
  becomes an hourly one.
- **Busy boost**: when pending jobs or active goal pursuits exceed
  thresholds, the loop speeds up. Multiplier typically `0.5` — a 15-minute
  heartbeat becomes a 7-minute one under load.
- **Clamps**: hard min/max so no multiplier combination produces a
  nonsensical interval (e.g. 2-second polling or overnight dead silence
  when something's burning).

## Priority order

The engine checks conditions in this order:

1. **Pacing disabled** → base interval, reason `disabled`.
2. **Busy-boost tripped** (pending jobs or active goals cross threshold)
   → apply busy multiplier, reason `busy_boost`. **Wins over quiet hours**
   on purpose: if there's active work the user asked for, slowing down
   at night is the wrong move.
3. **Quiet hours** → apply quiet multiplier, reason `quiet_hours`.
4. **Neither** → base interval, reason `base`.
5. Clamp to `[min_seconds, max_seconds]`, reason may become `clamp_min`
   or `clamp_max` if the raw computation fell outside.

## What this skill does NOT own

- Goal-pursuit cadence — that's a separate `goal_pursuit_seconds`
  setting on the Heartbeat instance, tunable independently.
- Whether the loop runs at all — that's `heartbeat_seconds` in
  `config.toml`. Setting this to `0` stops the loop; pacing never fires.
- Dispatch decisions — pacing only affects SLEEP duration, never what
  work runs during a pulse.

## Auditability

Every tick's pacing decision is visible in `GET /api/heartbeat/status`
under the `pacing` block: `{enabled, base_seconds, effective_seconds,
reason, multiplier}`. Agent and user can both see why the next tick
is scheduled when it's scheduled.
