Skip to content
Back to skills

Heartbeat

ASecurity

Run one Nazgul automation-heartbeat tick — triages the work inbox and auto-starts the next objective if idle. Opt-in and default-off; fired by an optional Claude Code native scheduled agent (routine) or run by hand. Use when asked to "run a heartbeat tick", "check the inbox", or to test/debug the heartbeat.

  • 7 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 5, 2026
ai-agentsbashtestingsecuritydocumentation

Works with

  • claude code

Security analysis

A100/100

Scanned September 5, 2026

npx -y skills add OrodruinLabs/nazgul --skill heartbeat --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Heartbeat?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Heartbeat
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/orodruinlabs-heartbeat/badge)](https://www.skillsdirectory.com/skills/orodruinlabs-heartbeat)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: nazgul:heartbeat
description: Run one Nazgul automation-heartbeat tick — triages the work inbox and auto-starts the next objective if idle. Opt-in and default-off; fired by an optional Claude Code native scheduled agent (routine) or run by hand. Use when asked to "run a heartbeat tick", "check the inbox", or to test/debug the heartbeat.
context: fork
allowed-tools: Read, Bash
metadata:
  author: Jose Mejia
---

# Nazgul Heartbeat

## Examples
- `/nazgul:heartbeat` — Run one heartbeat tick by hand (identical to a scheduled routine firing)

## Current State
- Heartbeat enabled: !`jq -r '.automation.heartbeat.enabled // false' nazgul/config.json 2>/dev/null || echo "false"`
- Inbox dir: !`jq -r '.automation.heartbeat.inbox.dir // "nazgul/inbox"' nazgul/config.json 2>/dev/null || echo "nazgul/inbox"`
- Inbox candidates: !`ls -1 nazgul/inbox/*.md nazgul/inbox/*.json 2>/dev/null | wc -l | tr -d ' '`
- Latest heartbeat log: !`ls -1t nazgul/logs/heartbeat-*.jsonl 2>/dev/null | head -1 || echo "none yet"`

## Instructions

Run exactly one heartbeat tick and report the outcome. This skill is trigger-agnostic: run it by
hand for testing, or configure a Claude Code native scheduled agent (routine) to fire it on an
interval (see below).

### Step 1: Check Initialization

If `nazgul/config.json` does not exist:
- Output: "Nazgul not initialized. Run `/nazgul:init` first."
- Stop here.

### Step 2: Run One Tick

Invoke the tick engine directly — it is the entire implementation; this skill only wraps it:

```bash
NAZGUL_SESSION_ID="$(cat nazgul/.session_id 2>/dev/null)" bash scripts/heartbeat.sh
```

`NAZGUL_SESSION_ID` tells the tick which `nazgul/sessions/*.lock` is its OWN, so the stacking
pre-steps' serialization guard counts only OTHER sessions. `nazgul/.session_id` is the right source
and the env vars are not: `CLAUDE_SESSION_ID` is unset in a skill's bash environment, and
`CLAUDE_CODE_SESSION_ID` holds a subagent's own id rather than the registered one — whereas
`scripts/session-context.sh` persists the exact id it then registers the lock under. The variable is
optional (the engine falls back to the same file, then to a loud
`stack_skipped_session_ambiguity` decision record); it is passed here so the sanctioned path is
explicit rather than inferred.

`scripts/heartbeat.sh` is self-contained and trigger-agnostic: it gates on
`automation.heartbeat.enabled`, enforces the two unconditional hard stops (BLOCKED task, security
rejection) regardless of that flag, triages the inbox, and — only when idle and clear — archives the
picked candidate and auto-starts it. It always exits 0 and appends one decision record to
`nazgul/logs/heartbeat-<date>.jsonl`.

### Step 3: Report the Tick

Read the last line of the latest `nazgul/logs/heartbeat-<date>.jsonl` (`tail -1`) and report the
`decision` field with its context:

| `decision` | Report |
|---|---|
| `disabled` | Heartbeat is disabled (`automation.heartbeat.enabled: false`) — tick was a no-op |
| `hard_stop` | Halted — `reason` (`blocked_task` and/or `security_rejection`) |
| `nothing_actionable` | Saw N inbox candidates, none actionable |
| `skipped` | Saw N, picked `<picked>`, skipped (`reason`) |
| `started` | Saw N, picked `<picked>` → started `<objective>` (archived to `<archived_to>`) |

Use `/nazgul:log` for the full historical heartbeat timeline across ticks.

## Enabling the Interval Trigger (opt-in, default off)

`automation.heartbeat.enabled` defaults to `false` — an unconfigured project never ticks on its own.
To enable unattended, periodic ticking:

1. Set `automation.heartbeat.enabled: true` in `nazgul/config.json` (review
   `automation.heartbeat.inbox.dir` and `automation.heartbeat.auto_start` for your setup).
2. Configure a Claude Code **native scheduled agent (routine)** to fire `/nazgul:heartbeat` on your
   chosen interval (`automation.heartbeat.interval`, e.g. `30m`). Routines are a Claude Code platform
   primitive independent of this plugin — set one up per the Claude Code scheduled-agent
   documentation, pointing it at this skill.
3. Each fired tick is identical to running `/nazgul:heartbeat` by hand: same gates, same hard stops,
   same decision record.

The core (`scripts/heartbeat.sh`) does not know or care what fired it — a routine, a human, or a test
harness all invoke the same tick. This skill wires no OS-level cron/launchd job and no `claude -p`
invocation; that remains deferred (FEAT-009).

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.