Skip to content

The stuck-loop warning works for thirty seconds, then the agent goes back to the same command #6480

Description

@macanderson

The problem

The stuck-loop steering warning works for about thirty seconds. Then the agent
returns to the very command the warning quoted, repeats it three times in a
row, and the turn is aborted.

nightly-bench has been red on this. The loop gate is right to fail: this is a
real aborted stuck loop, not the misclassification that was already fixed. That
fix is working — see the check at the bottom.

What the trace shows

From the red night of 2026-09-09, run 34336488244, artifact
loop-bench-34336488244, task cobol-modernization, stream
events/cobol-modernization__yV4uZPS.jsonl. Times are from the first event.

At +258.6s the engine detected an exact repeat — the same bash call, three
times consecutively, byte-identical output each time — and emitted a steered
event with aborted: false. The warning named the command and said "vary the
arguments, try a different tool, or report what is blocking you."

The agent then did change strategy. Nine tool calls over the next thirty
seconds, eight of them distinct:

# Time Command
1 +262.5s od -c /app/src/INPUT.DAT
2 +267.6s the flagged python3 -c reader
3 +274.7s git diff /app/src/INPUT.DAT
4 +277.1s git checkout /app/src/INPUT.DAT
5 +280.6s python3 /app/program.py
6 +282.8s a different python3 -c reader
7 +285.7s another different python3 -c reader
8 +290.1s a text-mode reader
9 +292.1s the same text-mode reader

Nothing repeats three times in a row there, so nothing trips the detector.

Then an 86-second gap with no tool call at all, and the agent comes back to
the exact command the warning had quoted:

# Time Command
10 +378.9s the flagged python3 -c reader
11 +385.8s the same
12 +389.5s the same

At +389.5s the detector fires again on the same kind, pattern and
repeats, this time with aborted: true. The turn stops with
DeliberateStopError: Stella exited with code 3. The task scores 0.0.

Why this is worth deciding

The second detection is the same call the first warning quoted by name. Nothing
in the engine remembers that. A call that has already been steered off is
treated on the second pass exactly like a call nobody has ever warned about, so
it gets a fresh three-repeat budget before anything happens — and those three
repeats are what end the turn.

Two directions, and picking between them is a maintainer's call:

  • Remember which exact call was steered, and act sooner when it comes back —
    abort on the first repeat after a warning rather than the third.
  • Leave the threshold alone and make the warning itself do more work.

There may be a third: the 86-second gap before the relapse suggests the warning
fell far enough back in the context to stop steering anything, which would make
this a context-placement question rather than a detector one.

Files

  • crates/stella-core — loop detection and the steering it emits.
  • bench/loop-bench/src/lib.rs — the verdict vocabulary and the two counters.
  • .github/workflows/nightly-bench.yml — the workflow and its pinned task set.

How to reproduce

The artifact is enough to re-read the whole sequence, and costs nothing:

gh api repos/macanderson/stella/actions/artifacts/10098814209/zip > a.zip
unzip a.zip -d lb
# then read lb/events/cobol-modernization__yV4uZPS.jsonl

Reproducing it live needs a bench run, which spends real money. That, plus the
design decision above, is why this is filed rather than fixed.

The earlier fix did not regress

Checked on the same night, both directions:

  • cobol-modernization — two loop_detected events, one aborted: false and
    one aborted: true. The STUCK-LOOP verdict is backed by a real abort.
  • overfull-hbox — one loop_detected with aborted: false and no
    STUCK-LOOP verdict. Its CRASHED row is a harbor timeout at 750s, which is
    a separate signal. An unaborted detection on its own no longer produces
    STUCK-LOOP, which is exactly what the earlier fix installed.

Which pillar this moves

Reliability. A steering mechanism that the agent walks back into is a loop the
user pays for twice — once in tokens and once in a lost turn.

What done looks like

  • A decision is recorded on whether an already-steered call keeps its full
    repeat budget when it recurs.
  • The chosen change ships with a test that fails on today's engine and
    passes on the new one.
  • A nightly-bench run on the change shows the cobol-modernization
    stream either not relapsing or aborting sooner, read from its own
    artifact rather than from the verdict table.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    triageUntyped request — convert by adding bug / feature / epic

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions