Skip to main content

fleet-guard

Machine-resource guard layer for fleets of coding-agent sessions (Claude Code and friends).

When you run many agent sessions in parallel, the failure mode is rarely a bad prompt — it's the machine falling over: background processes leak and pile up, a hung run burns quota for hours while doing nothing, a session ends and leaves orphaned children behind, a scratch dir quietly eats the disk overnight. fleet-guard is a small stdlib-only Python package — three Claude Code hooks, a stall watchdog daemon, a disk guard, and an append-only JSONL ledger — that keeps the fleet from taking down the host.

What it does

  1. Cross-session background-process quota — a PreToolUse hook counts agent-attributed background processes machine-wide. Over the cap (default: half your CPU cores), the new background run is blocked with a message naming what's already running.
  2. Stall watchdog — register long-running processes with fleet-guard watch add. If a watched process is alive but its log goes quiet for N seconds (default 600), the watchdog captures a diagnostic snapshot and kills the whole process tree — children first, SIGTERM, then SIGKILL for survivors.
  3. SessionEnd reaping — a SessionEnd hook kills leaked child process trees the session left behind.
  4. Disk guard — alerts (and optionally quarantines) when agent scratch dirs grow past a threshold, or their filesystem runs low on free space.
  5. Append-only ledger — every guard action lands in ~/.fleet-guard/ledger.jsonl.

Fail-open by design. Every hook and daemon pass is wrapped so that a guard that crashes exits 0 and lets the agent continue. A guard must never take down the agent it's protecting.

How this differs

  • vs loop-guard (my semantic layer): loop-guard detects semantic loops — an agent stuck in churn/test/burn review cycles, repeating itself without progress. fleet-guard is the machine-resource layer underneath it: processes, stalls, leaked children, disk. An agent can be semantically fine and still wedge the machine (a hung sleep-forever burns no tokens but holds a slot; a runaway log fills the disk). The two are complementary — run both.
  • vs manual shell workarounds (session-tree.sh scripts, hand-rolled cgroups, pkill cron jobs): those work, but they're glue you maintain per machine. fleet-guard is one installable package — hooks, daemon, ledger — with a single config surface and tests.

Motivating case class

The design is informed by Ramiz Raja's production checklist for running multiple Claude Code agents (codebyramiz.com/blog/running-multiple-claude-code-agents, Oct 2026): cap background processes at half the cores, alert when too many are busy, reap leaked processes on session end, and run a watchdog that snapshots and kills a process tree after 600s of log silence. His watchdog once caught a run hung for 7h22m while using only 258ms of CPU — invisible to any CPU-based monitor, obvious to a log-liveness check. His disk policy came from an overnight 56G → 1G incident. fleet-guard packages exactly that operational pattern so you don't have to re-derive it the hard way.

Install

pip install fleet-guard

Python 3.9+, stdlib only — no dependencies. Linux is the primary target (/proc-based inspection); macOS works via a ps fallback (environment attribution is Linux-only).

Hook setup (Claude Code)

Add to your Claude Code hooks config:

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Bash",
        "hooks": [{ "type": "command", "command": "fleet-guard hook pretooluse" }] }
    ],
    "SessionEnd": [
      { "hooks": [{ "type": "command", "command": "fleet-guard hook sessionend" }] }
    ],
    "Stop": [
      { "hooks": [{ "type": "command", "command": "fleet-guard hook stop" }] }
    ]
  }
}

Attribute background runs to a session so the quota and reaper can see them:

fleet-guard run --session "$CLAUDE_SESSION_ID" -- ./long-build.sh > build.log 2>&1 &
fleet-guard watch add --pid $! --log build.log --timeout 600 --label "build"

Usage

# watch a process's log for stalls (log mtime = liveness signal)
fleet-guard watch add --pid 1234 --log ./build.log --timeout 600 --session abc
fleet-guard watch list
fleet-guard watch rm --pid 1234

# run the watchdog daemon (or tick `watchdog-once` from cron)
fleet-guard daemon --interval 30
fleet-guard watchdog-once

# check agent scratch dirs against disk limits
fleet-guard disk-check

# inspect what the guard did
fleet-guard ledger -n 20

Configuration

Defaults live in code; override with ~/.fleet-guard/config.json or FLEET_GUARD_* environment variables:

Key Default Env
bg_quota half CPU cores FLEET_GUARD_BG_QUOTA
busy_alert_threshold 4 FLEET_GUARD_BUSY_ALERT
stall_timeout_s 600 FLEET_GUARD_STALL_TIMEOUT
watch_interval_s 30 FLEET_GUARD_WATCH_INTERVAL
kill_grace_s 5 FLEET_GUARD_KILL_GRACE
scratch_dirs [] FLEET_GUARD_SCRATCH_DIRS (pathsep-joined)
disk_max_bytes 10 GB FLEET_GUARD_DISK_MAX_BYTES
disk_min_free_bytes 5 GB FLEET_GUARD_DISK_MIN_FREE
disk_quarantine false FLEET_GUARD_DISK_QUARANTINE=1
state_dir ~/.fleet-guard FLEET_GUARD_STATE_DIR

Ledger format

~/.fleet-guard/ledger.jsonl, one JSON object per line:

{"ts": "2026-10-21T19:00:00+00:00", "event": "watchdog.stall-kill",
 "session_id": "abc", "details": {"pid": 1234, "idle_s": 623.4,
 "dump": "/home/you/.fleet-guard/dumps/stall-1234-1729530000.json", "sigkilled": []}}

Events: hook.pretooluse-block, hook.stop-busy-alert, sessionend.reap, watch.add, watch.exit, watchdog.stall-kill, disk.alert.

A note on "thread dumps"

The stall snapshot captures everything reachable from outside the target process with the stdlib on Linux: cmdline, /proc/<pid>/status, wait channel, kernel stack (/proc/<pid>/stack), IO counters, open FD count, exe/cwd. That is usually enough to tell "deadlocked on a futex" from "waiting on network IO". A true in-process Python thread dump (every thread's Python-level stack) needs cooperation from inside the target — tools like py-spy or gdb — which is deliberately out of scope for a zero-dependency guard. If you need that, point the dump path at your py-spy wrapper.

Development

python -m unittest discover -s tests -v   # 11 tests, no network

License

MIT — see LICENSE.

Metadata

Release files for fleet-guard 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fleet-guard 0.1.0
File Size Uploaded Provenance Actions
fleet_guard-0.1.0.tar.gz 19.7 kB Without provenance Details for fleet_guard-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for fleet-guard 0.1.0
File Interpreter ABI Platform Actions
fleet_guard-0.1.0-py3-none-any.whl Python 3 none any Details for fleet_guard-0.1.0-py3-none-any.whl

Total release size: 37.6 kB

Release files / fleet_guard-0.1.0.tar.gz

Download URL fleet_guard-0.1.0.tar.gz
Size 19.7 kB
Tags Source
SHA-256 checksum
How to use checksums
de5bf4f7da9efb5db7fb39215f534403cc5ae77f245fe80d72d25b107fa4b003
BLAKE2b-256 checksum
How to use checksums
1ae32688f2393027cd24e66d62d5ba5577bbdfed97daf517bf0c663344f6f68c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / fleet_guard-0.1.0-py3-none-any.whl

Download URL fleet_guard-0.1.0-py3-none-any.whl
Size 17.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c91131f6cb19b2dba5690b6f794e8ac0eb811fae54a6400229b5235eb0481cbb
BLAKE2b-256 checksum
How to use checksums
77e7a3debf1ef125e0e37efd83a45f873f25b88394cc962595843cbdc0ee0488
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Security for fleet-guard 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page