AltimateAI / AltimateAI/altimate-code

todowrite runaway: 9,139 calls/session escapes per-message doom-loop detector

Abierto
#834 1 comentario 0 reacciones 0 asignados Ver en GitHub
bug
Lenguaje dominante
TypeScript
Estrellas
811
Forks
134
Merge medio
3 d 2 h
PR fusionados (30 d)
50

Descripción

## Problem

Telemetry-2026-05-21 (2,503 machines, 14 days) shows the todowrite loop pattern from March is **still active and worse**:

- 29,562 total todowrite calls in the window
- Top 3 machines = 63% of all calls
- Single machine peak: **9,139 todowrite calls across 28 sessions** (~325/session)

Sessions at this scale burn tokens, induce context bloat, and produce visibly broken UX. The existing doom-loop detector in `session/processor.ts` (`TOOL_REPEAT_THRESHOLD = 30`) is per-assistant-message scope: its counter resets every time a new assistant message starts. The pathological pattern is *slow-burn* — ~10-15 calls per LLM turn, accumulating across many turns within a single session.

## Root cause

`SessionProcessor.create()` instantiates a fresh `toolCallCounts: Record` per message. A new processor is created per assistant message, so the counter never accumulates across the full session. The detector only catches a single turn that spams a tool 30+ times.

Real loops look like this: agent calls todowrite 10x in turn 1, 10x in turn 2, 10x in turn 3... totaling 100 within 10 turns. Each turn is under threshold; the cross-turn pattern is invisible to the per-message counter.

## Fix

Add a tool-level per-session counter inside `TodoWriteTool` itself. New constants:
- `TODOWRITE_WARN_THRESHOLD = 25` — emit warning in tool output + telemetry event; the agent sees the warning text and can self-correct
- `TODOWRITE_REFUSE_THRESHOLD = 50` — refuse the call entirely; return a structured message telling the agent to stop calling todowrite and continue with the work

Both thresholds emit `doom_loop_detected` telemetry events so dashboards see the pattern even when the agent ignores the warning.

Extracted into a testable helper `recordTodoWriteCall(sessionID)` returning `{ action: "ok" | "warn" | "refuse", count }`. Counter is module-scoped in-memory Map; stale entries from old sessions are harmless because lookup is per-sessionID.

## Impact

Telemetry should show:
- `todowrite` call volume per session capped at ~50 (down from 9,000+ peaks)
- `doom_loop_detected` events for `todowrite` at much higher rates (every session that crosses the threshold now reports)
- Reduced token/context burn from runaway todo updates

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.