.claude/skills/behavioral-loop-detection/SKILL.md
Detect when agents are stuck in repetitive action loops using a rolling 20-action window with escalating nudges — replan after 3 similar actions, explore after 5, force-done after 8.
npx skillsauth add oimiragieo/agent-studio behavioral-loop-detectionInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Detects when an agent is stuck in a repetitive action loop by maintaining a rolling 20-action history window. Compares recent actions using normalized similarity scoring and applies escalating interventions before the agent wastes tokens or blocks progress.
Designed for any agentic context — browser automation, file editing, API calls, or multi-step orchestration — not tied to any specific tool type.
Invoke this skill when:
TaskUpdate(completed) after a long sequenceSkill({ skill: 'behavioral-loop-detection' });
SAME ACTION × 3 = REPLAN
SAME ACTION × 5 = EXPLORE
SAME ACTION × 8 = FORCE-DONE
Never let an agent silently loop forever. Each repetition consumes tokens and produces no value.
{ toolName, normalizedArgs, timestamp, stepIndex }Two actions are similar when:
toolName is identical, ANDNormalization rules (apply in order):
/a/b/file.ts → file.ts)Similarity scoring:
function jaccardSimilarity(a, b) {
const setA = new Set(a.split(/\s+/));
const setB = new Set(b.split(/\s+/));
const intersection = new Set([...setA].filter(x => setB.has(x)));
const union = new Set([...setA, ...setB]);
return intersection.size / union.size;
}
| Threshold | Trigger Condition | Intervention | Message to Agent | | --------- | -------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------- | | Level 1 | 3 similar actions in a row | REPLAN nudge | "You have repeated a similar action 3 times. Stop and produce a revised plan before continuing." | | Level 2 | 5 similar actions in a row | EXPLORE nudge | "You have repeated a similar action 5 times. The current approach is failing. Try a completely different tool or method." | | Level 3 | 8 similar actions in a row | FORCE-DONE signal | "Loop limit reached (8 repetitions). Mark this task complete with partial results and explain what was not accomplished." |
TaskUpdate({ status: 'completed', metadata: { partial: true, loopDetected: true } })At the start of task execution or when this skill is loaded, create the buffer:
Command:
const actionBuffer = {
window: [], // Array of last 20 action entries
maxSize: 20,
similarRunLength: 0, // Current streak of similar actions
lastNormalized: null, // Normalized args of last action
};
Expected output: In-memory buffer ready; no file I/O required unless persisting across sessions.
Verify: actionBuffer.window.length === 0 on initialization.
Before executing any tool call, append to the buffer:
Command:
function recordAction(buffer, toolName, rawArgs) {
const normalized = normalizeArgs(toolName, rawArgs);
const entry = {
toolName,
normalizedArgs: normalized,
timestamp: Date.now(),
stepIndex: buffer.window.length,
};
if (buffer.window.length >= buffer.maxSize) {
buffer.window.shift(); // drop oldest
}
buffer.window.push(entry);
return entry;
}
Expected output: Buffer contains the new entry; length never exceeds 20.
Verify: buffer.window.length <= 20 after each call.
After recording, compute similarity and update the run-length counter:
Command:
function checkSimilarity(buffer, currentEntry) {
if (!buffer.lastNormalized) {
buffer.lastNormalized = currentEntry.normalizedArgs;
buffer.similarRunLength = 1;
return { similar: false, runLength: 1 };
}
const score = jaccardSimilarity(buffer.lastNormalized, currentEntry.normalizedArgs);
if (score >= 0.75) {
buffer.similarRunLength += 1;
} else {
buffer.similarRunLength = 1;
buffer.lastNormalized = currentEntry.normalizedArgs;
}
return { similar: score >= 0.75, runLength: buffer.similarRunLength, score };
}
Expected output: { similar: boolean, runLength: number, score: number }
Verify: runLength increments only when score >= 0.75.
Evaluate the run-length and emit the appropriate intervention:
Command:
function applyEscalation(runLength, taskId) {
if (runLength >= 8) {
console.error(`[loop-detection] FORCE-DONE: ${runLength} similar actions. Task ${taskId}`);
return {
level: 3,
action: 'force-done',
message:
'Loop limit reached (8 repetitions). Mark this task complete with partial results and explain what was not accomplished.',
};
}
if (runLength >= 5) {
console.error(`[loop-detection] EXPLORE: ${runLength} similar actions. Task ${taskId}`);
return {
level: 2,
action: 'explore',
message:
'You have repeated a similar action 5 times. The current approach is failing. Try a completely different tool or method.',
};
}
if (runLength >= 3) {
console.error(`[loop-detection] REPLAN: ${runLength} similar actions. Task ${taskId}`);
return {
level: 1,
action: 'replan',
message:
'You have repeated a similar action 3 times. Stop and produce a revised plan before continuing.',
};
}
return { level: 0, action: 'continue', message: null };
}
Expected output: { level: 0|1|2|3, action: string, message: string|null }
Verify: level === 3 triggers TaskUpdate({ status: 'completed', metadata: { partial: true, loopDetected: true } }).
When level === 3, the agent MUST stop and complete the task:
Command:
// In the agent's task loop, when applyEscalation returns level 3:
TaskUpdate({
taskId: context.taskId,
status: 'completed',
metadata: {
summary:
'Task partially completed. Loop detected after 8 similar actions. ' + partialResultsSummary,
partial: true,
loopDetected: true,
loopDetails: {
runLength: runLength,
lastAction: buffer.lastNormalized,
bufferSnapshot: buffer.window.slice(-5),
},
},
});
Expected output: Task marked completed with partial: true metadata.
Verify: TaskList() shows the task as completed, not in_progress.
Input validated against schemas/input.schema.json before execution.
Output contract defined in schemas/output.schema.json.
Pre-execute hook at hooks/pre-execute.cjs validates that taskId is provided.
Post-execute hook at hooks/post-execute.cjs emits a loop-detection event to tool-events.jsonl.
level < 3 — nudges are sufficient at levels 1 and 2toolName differs — tool variety alone ≠ not loopingBefore starting: Read .claude/context/memory/learnings.md for previously detected loop patterns.
After completing: If a loop was detected and force-done triggered, append to .claude/context/memory/learnings.md:
## Behavioral Loop Detection — [date]
- Task [taskId]: [toolName] looped [N] times. Final normalized args: [args].
- Root cause hypothesis: [explain why the agent got stuck]
- Recommendation: [what to change in the task prompt or approach]
After issues: Append to .claude/context/memory/issues.md if force-done fires more than twice in a session.
verification-before-completion — Pre-completion gates that prevent false successjudge-verification — Independent LLM judge that verifies task completionerror-recovery-escalation — 5-level error recovery before force-donecontext-compressor — Compress context when loop detection fires repeatedlytools
Comprehensive biosignal processing toolkit for analyzing physiological data including ECG, EEG, EDA, RSP, PPG, EMG, and EOG signals. Use this skill when processing cardiovascular signals, brain activity, electrodermal responses, respiratory patterns, muscle activity, or eye movements. Applicable for heart rate variability analysis, event-related potentials, complexity measures, autonomic nervous system assessment, psychophysiology research, and multi-modal physiological signal integration.
tools
Comprehensive toolkit for creating, analyzing, and visualizing complex networks and graphs in Python. Use when working with network/graph data structures, analyzing relationships between entities, computing graph algorithms (shortest paths, centrality, clustering), detecting communities, generating synthetic networks, or visualizing network topologies. Applicable to social networks, biological networks, transportation systems, citation networks, and any domain involving pairwise relationships.
data-ai
Molecular featurization for ML (100+ featurizers). ECFP, MACCS, descriptors, pretrained models (ChemBERTa), convert SMILES to features, for QSAR and molecular ML.
development
Run Python code in the cloud with serverless containers, GPUs, and autoscaling. Use when deploying ML models, running batch processing jobs, scheduling compute-intensive tasks, or serving APIs that require GPU acceleration or dynamic scaling.