Skip to content
Back to skills

Stuck Detection

ASecurity

Detect agent conversation loops via topic-signature similarity and emit a self-reflection nudge. Use as an orchestrator guard against repetitive responses and token-burning loops. Use when you say "check stuck loop", "detect repetition", "agent looping", or "reset stuck history". Do NOT use for semantic similarity checks or multi-turn intent tracking; use a richer evaluation tool for those.

  • 47 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
data-aipythonbashtestingdebugging

Works with

  • cli

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned September 25, 2026

npx -y skills add rjmurillo/ai-agents --skill stuck-detection --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Stuck Detection?

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

Security grade badge for Stuck Detection
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/rjmurillo-stuck-detection/badge)](https://www.skillsdirectory.com/skills/rjmurillo-stuck-detection)

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: stuck-detection
version: 1.2.0
model: haiku
model-rationale: cost. The 'haiku' rolling alias resolves via the platform model_tiers map to a tier priced below the sonnet-tier harness default; this unit is routing/mechanical work where the cheaper tier suffices (ADR-080 rule 3).
description: >-
  Detect agent conversation loops via topic-signature similarity and emit a
  self-reflection nudge. Use as an orchestrator guard against repetitive
  responses and token-burning loops. Use when you say "check stuck loop",
  "detect repetition", "agent looping", or "reset stuck history". Do NOT use
  for semantic similarity checks or multi-turn intent tracking; use a richer
  evaluation tool for those.
license: MIT
metadata:
  routing:
    role: conditional-adjunct
    invoker: ai-agents-debugging-playbook
    trigger: ai-agents-debugging-playbook triggers stuck-detection after three read-only commands find no cause
    user-facing: false
---

# Stuck Detection Guard

Detect when an agent is repeating the same topics across turns and surface a
self-reflection nudge so the orchestrator can break the loop. Lightweight,
deterministic, no external services.

## Triggers

| Trigger Phrase | Operation |
|----------------|-----------|
| `check stuck loop` | Score current response against recent history |
| `detect repetition` | Flag repeating topic signatures |
| `agent looping` | Emit a loop-breaking nudge |
| `reset stuck history` | Clear history after a confirmed topic change |

## Quick Reference

| Symptom | Cause | Fix |
|---------|-------|-----|
| Agent re-states the same status every turn | Topic signature unchanged across turns | Inject the `nudge` payload before the next turn |
| False positive after a real topic change | Stale history kept after the user redirected | Run `reset` to clear history |
| Stuck never triggers despite obvious repetition | Responses below `MIN_TEXT_LENGTH` (50 chars) | Lower threshold or pass full response text |
| Unrelated turns flagged as stuck | Generic vocabulary inflates Jaccard score | Raise `DEFAULT_SIMILARITY_THRESHOLD` toward 0.75 |

## When to Use

Use this skill when:

- Building an orchestrator that risks repeating tool calls or status updates
- A long-running session shows degraded variety in responses
- You want a cheap pre-flight guard before sending a response

Use a richer evaluation instead when:

- You need semantic similarity (this uses lexical Jaccard only)
- You need multi-turn intent tracking
- You need cross-session coherence checks

## How It Works

1. **Topic signature.** Strip stopwords, take the top N significant words by
   frequency, sort them, and join with commas. This is the response's signature.
2. **History.** Append each signature with a UTC timestamp to a JSON file.
   Keep only the most recent entries.
3. **Jaccard similarity.** Compare the current signature against the most
   recent K entries. Count how many exceed the similarity threshold.
4. **Trigger.** When K consecutive recent entries are similar, emit a nudge
   payload telling the orchestrator to ask the user a question or change topic.

## Process

1. Run `python3 .claude/skills/stuck-detection/stuck_detection.py check "<text>"`
2. If the JSON result has `"stuck": true`, inject the `nudge` field into the
   orchestrator's next system prompt so the model self-corrects on its next
   turn. The `nudge` is internal control text wrapped in `<stuck-detection>`
   tags. Do not render it directly to the end user.
3. After a confirmed topic change, call `reset` to clear stale history.

## Usage

```bash
# Check a response for stuck pattern (text via arg or stdin)
python3 .claude/skills/stuck-detection/stuck_detection.py check "your response text"
echo "your response" | python3 .claude/skills/stuck-detection/stuck_detection.py check

# Show current history state
python3 .claude/skills/stuck-detection/stuck_detection.py status

# Reset history after a topic change
python3 .claude/skills/stuck-detection/stuck_detection.py reset

# Extract signature only (debugging)
python3 .claude/skills/stuck-detection/stuck_detection.py extract "your text"
```

### Output

```json
{
  "stuck": true,
  "signature": "deploy,error,pipeline,retry,timeout",
  "similar_count": 3,
  "nudge": "<stuck-detection>\nSELF-REFLECTION: Loop detected.\n..."
}
```

## Configuration

Defaults are conservative. Override at the function call site or via constants
in `stuck_detection.py`:

| Constant | Default | Purpose |
|----------|---------|---------|
| `DEFAULT_MAX_HISTORY` | 10 | Entries retained on disk |
| `DEFAULT_STUCK_THRESHOLD` | 3 | Consecutive similar turns to trigger |
| `DEFAULT_SIMILARITY_THRESHOLD` | 0.6 | Jaccard cutoff for "similar" |
| `MIN_TEXT_LENGTH` | 50 | Skip short responses |
| `SIGNATURE_SIZE` | 5 | Top words per signature |

### History Path

Resolution order:

1. `--history <path>` CLI flag
2. `STUCK_DETECTION_HISTORY` environment variable (full path)
3. `STUCK_DETECTION_SESSION` environment variable (per-session file under the
   XDG state dir, e.g. `history-<session>.json`); use this when running
   multiple concurrent sessions to prevent cross-session contamination
4. `$XDG_STATE_HOME/claude-stuck-detection/history.json`
5. `~/.local/state/claude-stuck-detection/history.json`

Paths are expanded and resolved before use. Avoid relative `..` segments in
`--history` or `STUCK_DETECTION_HISTORY` if you need the resolved location to
match the input.

## Verification

After integrating, confirm the following before relying on the guard:

- [ ] `python3 .claude/skills/stuck-detection/stuck_detection.py status` returns valid JSON with the configured `history_length`
- [ ] Three calls to `check` with the same long input return `"stuck": true` on the third call
- [ ] A call to `reset` zeroes `history_length` on the next `status` check
- [ ] The history file lives outside the repository working tree (XDG state dir or env-overridden path)
- [ ] Generated `nudge` text is forwarded into the orchestrator system prompt, not rendered to the end user
- [ ] Concurrent `check` calls never produce truncated JSON (atomic write via temp file + replace)
- [ ] A malformed history file (non-list, missing keys, non-string values) is treated as empty and not propagated to callers
- [ ] Tests in `tests/skills/stuck-detection/` pass under `uv run pytest`

## Anti-Patterns

| Avoid | Why | Instead |
|-------|-----|---------|
| Calling `check` on every token | High overhead, noisy signatures | Call once per agent turn |
| Treating `stuck: false` as proof of progress | Lexical similarity misses semantic loops | Pair with task-completion checks |
| Hardcoding the history path in callers | Breaks portability across environments | Use the env var or CLI flag |
| Ignoring `reset` after topic changes | History stays polluted | Reset when the user redirects |

## Integration

Call from a hook or orchestrator wrapper. When `stuck` is true, prepend the
`nudge` to the system prompt for the next turn so the model self-corrects.

## Testing

```bash
uv run pytest tests/skills/stuck-detection/ -v
```

Files in this skill

  • SKILL.md6.3 KB
  • stuck_detection.py10.9 KB

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.