stallguard
Your coding agent says "done". Did it do anything? stallguard is a drop-in wrapper that streams agent steps, kills stalls and retry-loops, and flags fake-GOAL finishes — with a shareable trace for every run.
pipx install stallguard
stallguard run -- npx claude-code "fix the login bug"
# [stallguard] status=ok exit=0 duration=42.1s trace=traces/stallguard-....html
No code changes. Just prefix your agent command with stallguard run --.
The problem
AI coding agents fail in three boring, expensive ways:
- Stalls — no output for 20 minutes while your CI minute-burn ticks.
- Loops — the same tool call retried 500 times (
retrying with same args...). - Fake-GOALs — prints
DONE ✅whilegit diffis empty.
Logs scroll by, nobody watches, green checkmarks lie. stallguard turns each run
into a verdict: ok, error, stalled, looped, or fake-goal — enforced
by exit code, so CI can act on it.
30-second quickstart
# any agent, any command — just prefix it
stallguard run -- python -u your_agent.py
stallguard run --stall-timeout 90 --require-diff -- ./run.sh
# inspect a run later
stallguard view traces/stallguard-*.jsonl
# open traces/stallguard-*.html in a browser for the full step-by-step trace
What it catches
| flag | default | catches | exit |
|---|---|---|---|
--stall-timeout N |
120 | no output for N seconds → SIGKILL |
124 |
--repeat-limit K |
15 | K identical lines in a row → SIGKILL |
125 |
--require-diff |
off | exit 0 + "done" text but empty git diff |
3 |
--expect-file PATH |
— | required file missing after run (repeatable) | 3 |
--trace-dir DIR |
traces |
JSONL + summary JSON + self-contained HTML per run | — |
0 = ok, 1 = agent errored, 3 = fake-goal, 124 = stalled, 125 = looped.
Why not just timeout?
timeout 300 ./agent kills a hung run but tells you nothing: was it a stall, a
loop, or nearly done? And it can't catch the worst case — the agent that
finishes fast, prints success, and changed zero files. stallguard records
timestamped evidence for every run and verifies the goal actually happened.
CI: fail on fake success
- name: Run agent under stallguard
run: stallguard run --stall-timeout 300 --require-diff -- ./run-agent.sh
- name: Upload traces
uses: actions/upload-artifact@v4
with:
name: stallguard-traces
path: traces/
Or use the bundled composite action (see action.yml). Every failed run links its HTML trace — debugging starts from evidence, not from re-running and hoping.
Works with any agent
stallguard sees stdout, not APIs, so it works with everything:
Claude Code, Codex CLI, OpenHands, AutoGPT, your own Python script — if it
prints steps, stallguard can supervise it. Zero dependencies, stdlib only.
Dev
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
python -m stallguard run -- python -u examples/ok.py
python -m stallguard run --stall-timeout 1 -- python -u examples/hang.py; echo $?
python -m stallguard run --repeat-limit 3 -- python -u examples/loop.py; echo $?
python -m unittest discover -s tests
Roadmap
- Token/step budgets (
--max-lines,--max-minutes) - Semantic loop detection (same tool call, different formatting)
- Live web dashboard for parallel agent fleets
- PyPI release + Homebrew formula
PRs welcome — especially real traces of agents failing in new and creative ways.
MIT. If stallguard caught your agent slacking, leave a ⭐.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stallguard-0.1.0.tar.gz.
File metadata
- Download URL: stallguard-0.1.0.tar.gz
- Upload date:
- Size: 11.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d54f5a7f7c41d6b4b8f4ebbb123d215868532bfbd47661cfc72e86ec222d1e2f
|
|
| MD5 |
3863fdf1dee4e62eaaed61faa0af6737
|
|
| BLAKE2b-256 |
232812b01fc2385bf74a11806409bb35f7e04a72d6fd81a36d3ef6059de2f53d
|
Provenance
The following attestation bundles were made for stallguard-0.1.0.tar.gz:
Publisher:
publish.yml on gpt4omni/stallguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stallguard-0.1.0.tar.gz -
Subject digest:
d54f5a7f7c41d6b4b8f4ebbb123d215868532bfbd47661cfc72e86ec222d1e2f - Sigstore transparency entry: 2690593176
- Sigstore integration time:
-
Permalink:
gpt4omni/stallguard@e2b4a81cbce2fca8df031cb783059afe0a55a055 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/gpt4omni
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e2b4a81cbce2fca8df031cb783059afe0a55a055 -
Trigger Event:
push
-
Statement type:
File details
Details for the file stallguard-0.1.0-py3-none-any.whl.
File metadata
- Download URL: stallguard-0.1.0-py3-none-any.whl
- Upload date:
- Size: 10.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cbb9d49f3dd0d9f60548bdea048ac006bc8ada7adffbe50986bd8936d4f4af08
|
|
| MD5 |
6c4715842592d51fd8421b8048ec512e
|
|
| BLAKE2b-256 |
52b96cdf79785a05f76a2061f3a5d48d101d879b1ad24d65cf2347da809790ae
|
Provenance
The following attestation bundles were made for stallguard-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on gpt4omni/stallguard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stallguard-0.1.0-py3-none-any.whl -
Subject digest:
cbb9d49f3dd0d9f60548bdea048ac006bc8ada7adffbe50986bd8936d4f4af08 - Sigstore transparency entry: 2690593343
- Sigstore integration time:
-
Permalink:
gpt4omni/stallguard@e2b4a81cbce2fca8df031cb783059afe0a55a055 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/gpt4omni
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e2b4a81cbce2fca8df031cb783059afe0a55a055 -
Trigger Event:
push
-
Statement type: