Example

Detect a loop from hashes, then name which kind it is

Run three windowed traces through the chapter's novelty and repetition detectors and see an exact loop, a thrash and a retry storm separated by signature, not by step count.

An agent can burn ninety steps and never move. Step counts measure effort, not progress, so a guard that watches the step budget watches the wrong thing. The chapter’s answer is a novelty metric computed from hashes over a pre-registered window, plus three repetition signatures that name the shape of the non-progress: an exact loop, a thrash, and a retry storm. This program composes the chapter’s window_novelty and repetition_shape detectors over three constructed windows plus a progressing one.

Run it

Save this as loop_detection.py and run it with Python 3.11 or later. It uses the standard library only.

"""Chapter 39: detect non-progress from hashes, and name the loop shape.

The detectors are the chapter's. The trace is a local construction: three
windows built to match the chapter's stated signatures.
"""

from dataclasses import dataclass


@dataclass(frozen=True)
class Step:
    state_hash: str
    arg_hash: str
    obs_hash: str
    error: str | None = None
    backoff_s: int = 0


def window_novelty(steps):
    """OBSERVATION: hashes, not impressions."""
    return len({s.state_hash for s in steps}) / len(steps)


def repetition_shape(window):
    """The chapter's detector: H1 exact loop, H2 thrash, H3 retry storm."""
    seen, cycles = set(), 0
    for s in window:
        key = (s.arg_hash, s.obs_hash)
        if key in seen:
            cycles += 1
        seen.add(key)
    if cycles >= 2:
        return "H1-exact-loop"
    args = {s.arg_hash for s in window}
    states = {s.state_hash for s in window}
    if len(args) > 1 and len(states) == 1:
        return "H2-thrash"
    errors = [s.error for s in window if s.error]
    repeats = max(errors.count(c) for c in set(errors)) if errors else 0
    delays = [s.backoff_s for s in window]
    if repeats >= 2 and not all(b > a for a, b in zip(delays, delays[1:])):
        return "H3-retry-storm"
    return "progressing-or-UNKNOWN"


WINDOWS = {
    "12-21 exact loop": [Step("h7a1", "q-same", "obs-same") for _ in range(10)],
    "42-51 thrash": [Step("h7a1", f"q-{i % 6}", f"obs-{i}") for i in range(10)],
    "62-71 retry storm": [Step("h7a1", "q-fetch", f"obs-{i}", error="parse_failed")
                          for i in range(8)],
    "82-91 progressing": [Step(f"h7b{i}", f"q-{i}", f"obs-{i}") for i in range(10)],
}

for label, window in WINDOWS.items():
    states = len({s.state_hash for s in window})
    args = len({s.arg_hash for s in window})
    print(f"{label:20} | states {states}/{len(window)} "
          f"(novelty {window_novelty(window):.2f}) | args {args} | "
          f"{repetition_shape(window)}")

counts, span, halted = {}, 0, False
for s in WINDOWS["12-21 exact loop"]:
    counts[s.arg_hash] = counts.get(s.arg_hash, 0) + 1
    if counts[s.arg_hash] >= 3:
        halted = True
        break
    span += 1
print(f"H1 tabu: span 10 -> {span} steps, halt verdict UNKNOWN, halted={halted}")

Expected outcome

12-21 exact loop     | states 1/10 (novelty 0.10) | args 1 | H1-exact-loop
42-51 thrash         | states 1/10 (novelty 0.10) | args 6 | H2-thrash
62-71 retry storm    | states 1/8 (novelty 0.12) | args 1 | H3-retry-storm
82-91 progressing    | states 10/10 (novelty 1.00) | args 10 | progressing-or-UNKNOWN
H1 tabu: span 10 -> 2 steps, halt verdict UNKNOWN, halted=True

Three windows of ten, ten and eight steps, and three different verdicts. The step counts are nearly identical and useless; the hashes separate them. The fourth window moves, and the detector says so.

The signature, not the prose

The chapter’s detector reads a different signature in each branch, and each branch calls for a different guard: a tabu list on equivalent arg hashes for H1, a cap on state-equivalent windows for H2, and backoff with a branch requirement for H3. Its rule is direct: hashes and error codes detect, prose variety is inadmissible. The chapter’s record:

PROGRESS WINDOWS (constructed; window=10, hashes verbatim):
steps 12-21 | states: h7a1 ×10 (novelty 0.0) | args: q-hash identical ×10
            -> H1 EXACT LOOP (2+ equivalent cycles, no new information)
steps 42-51 | states: h7a1 ×10 (novelty 0.0) | args: 6 distinct q-hashes
            -> H2 THRASH (surface varies, state fixed, outcomes identical)

Mechanism and limitations

Documented: the chapter states that a research agent can burn ninety steps with the working-state hash unchanged across ten cycles, that effort is not evidence and state novelty is, that exact loops repeat arg and observation hashes, thrashing varies surface form under state-equivalence, and retry storms repeat error-triggered actions without backoff growth or a branch, and that a halt verdict is UNKNOWN, not a fabricated synthesis.

Observed: the run above. The exact loop, the thrash and the retry storm were separated by signature, and the progressing window was classified as progressing. The tabu guard cut the exact loop’s span from ten steps to two and reported the halt verdict as UNKNOWN.

Not shown. The trace is constructed here, not recorded from a real agent, and the chapter reports no measured run for this case. The novelty figures are distinct state hashes per step on a window of ten, so a flat window reads 0.10 rather than the chapter’s 0.0; what matters is the contrast with the progressing window’s 1.00. The H2 cap and H3 backoff guards are described but not implemented here, and only the H1 tabu is exercised, once rather than over the three trials the chapter requires.

Read the chapter .

Understand this example

Copy this prompt into your AI tool. No code runs here.

Apply this example

Copy this prompt into your AI tool. No code runs here.