intermediatecorebest-practicesartverificationconvergence

Best Practices & The Art of Loop Engineering

Verification criteria, convergence strategies, state management, and loop testing from production systems.

At its core, an agent is just a model calling tools in a loop until it's done. The simplicity of this statement belies the skill required to design loops that converge reliably, handle errors gracefully, and produce consistent results. This guide distills best practices from real production systems — including Claude Code, Aider (30K+ GitHub stars), SWE-Agent (15K+ stars), and enterprise deployments documented across the Chinese AI engineering community.

Principle 1: Design Verifiable Goals

The most important aspect of loop engineering is defining goals that can be mechanically verified. If you cannot write a test or automated check that determines whether the goal is met, the loop cannot know when to stop.

Good Goals (Verifiable)

  • "All tests in src/__tests__/ pass"
  • "ESLint reports zero errors on lint"
  • "The API returns HTTP 200 on all documented endpoints"
  • "The build completes with zero warnings"

Bad Goals (Subjective)

  • "The code looks cleaner"
  • "The performance seems better"
  • "The implementation is good enough"
  • "The code follows best practices"

Real-World Example: SWE-Agent's Evaluation Loop

SWE-Agent from Princeton NLP exemplifies verifiable goals in practice. Each agent loop resolves a GitHub issue by running pytest against a repository and checking whether the specific test case referenced in the issue passes. The loop terminates when the test suite exits with code 0 — a clear, binary signal that the goal is met. No subjective judgment is involved.

Practice: Write the Verification First

Before defining the loop, write the verification check:

def is_goal_met():
    """Return True if the loop's goal has been achieved."""
    result = subprocess.run(
        ["npm", "test", "--", "--json"],
        capture_output=True, text=True
    )
    report = json.loads(result.stdout)
    return report["success"] and report["numFailedTests"] == 0

If you cannot write this function, your goal is not specific enough.

Principle 2: Design for Convergence

A well-designed loop should converge — getting closer to the goal with each iteration. If the loop oscillates (making and undoing changes), it is poorly designed.

Convergence Strategies

1. Monotonic Progress: Each iteration should only add or improve, never regress.

Iteration 1: Fix 3 of 10 failing tests
Iteration 2: Fix 3 more (6/10 passing)
Iteration 3: Fix 2 more (8/10 passing)
Iteration 4: Fix 2 more (10/10 passing) -> Done

2. Decreasing Error Count: Track and report the error count per iteration. It should trend toward zero.

3. Bounded Exploration: If an approach has not worked after N attempts, try something fundamentally different — do not keep retrying the same fix.

Real-World Example: Aider's Git-First Convergence

Aider (30K+ stars) enforces convergence through its Git-first architecture. Every code change the AI proposes is committed as a Git diff. If a change introduces regressions, Aider can revert to the last good commit and try a different approach. This creates a natural convergence mechanism: the Git history is a monotonically progressing sequence, and any non-convergent change is automatically rolled back.

# Aider enforces convergence via git — each iteration is a commit
aider --model claude-3.5-sonnet src/auth/login.ts

Practice: Log Iteration Progress

[Loop] Iteration 1/20: 3 errors remaining (was 10)
[Loop] Iteration 2/20: 2 errors remaining (was 3)
[Loop] Iteration 3/20: 2 errors remaining (stuck — trying different approach)
[Loop] Iteration 4/20: 1 error remaining (new approach working)
[Loop] Iteration 5/20: 0 errors remaining -> Goal met!

Principle 3: Set Explicit Boundaries

Essential Boundaries

BoundaryPurposeExample
Max iterationsPrevent infinite loopsMAX_ITERATIONS = 20
Max timePrevent resource exhaustionTIMEOUT = 300 (5 minutes)
Max tokensPrevent cost overrunsMAX_TOKENS = 100_000
Max errorsDetect stuck loopsIf error count unchanged for 3 iterations, escalate

Real-World Example: Token Budget Discipline

# Layered model strategy for cost-conscious loops
class LoopConfig:
    max_iterations: int = 20
    max_time_seconds: int = 300
    max_tokens: int = 100_000
    stuck_threshold: int = 3  # Escalate if no progress for N iterations

    # Use Haiku for verification, Sonnet for generation
    verification_model: str = "claude-3-haiku-20240307"
    generation_model: str = "claude-3.5-sonnet-20241022"

Principle 4: Handle Errors at the Right Level

Error Classification

Error TypeRecovery StrategyExample
ExpectedSelf-correct within the loopTest failure -> fix the code
IntermittentRetry with exponential backoffAPI timeout -> wait and retry
StructuralEscalate to humanArchitecture conflict -> ask for guidance
FatalStop and reportDisk full, API key invalid -> abort

Practice: Tiered Error Handling

Level 1 (Auto-retry): Transient failures — retry up to 3 times with backoff
Level 2 (Alternative approach): Same error repeated — try different strategy
Level 3 (Escalate): Fundamental issue — pause loop and notify human
Level 4 (Abort): Unrecoverable — stop the loop and save state

Principle 5: Manage State Carefully

State Management Best Practices

  1. Checkpoint regularly: Save progress after each successful iteration to disk
  2. Make state inspectable: Log the full state so humans can audit what happened
  3. Enable resume: If a loop is interrupted, it should resume from the last checkpoint
  4. Clean up on completion: Remove temporary files and state artifacts when done

Practice: Use Structured State

{
  "loop_id": "refactor-auth-2026-06-25",
  "goal": "Migrate auth from JWT to session-based",
  "current_iteration": 7,
  "max_iterations": 20,
  "files_processed": ["login.ts", "register.ts", "middleware.ts"],
  "files_remaining": ["logout.ts", "refresh.ts"],
  "errors_this_iteration": [],
  "last_successful_checkpoint": "iteration-6",
  "token_usage": {
    "total_input": 45000,
    "total_output": 12000,
    "budget_remaining": 43000
  }
}

Real-World Example: Claude Code's CLAUDE.md as Persistent State

Claude Code uses CLAUDE.md files as a form of persistent state that survives across sessions. A well-maintained CLAUDE.md captures project conventions, architectural decisions, and in-progress work — effectively serving as a checkpoint that any new loop iteration can read. This aligns with the context engineering principle of anchoring: providing stable reference points that ground the agent's understanding across iterations.

# Example CLAUDE.md checkpoint pattern
## Current Task
- Migrating auth from JWT to sessions
- Completed: login.ts, register.ts, middleware.ts
- Remaining: logout.ts, refresh.ts
- Last known error: Session cookie not set on CORS preflight

Principle 6: Keep Humans Informed

Communication Levels

LevelWhat to ReportFrequency
ProgressIteration count, error trendEvery iteration
MilestonesSignificant progressWhen achieved
BlockersIssues requiring human attentionImmediately
SummaryFinal result and statisticsOn completion

Practice: Structured Reports

Loop Summary: auth-migration
================================
Duration: 4m 32s
Iterations: 7 / 20 max
Files processed: 5/5
Tests: 42/42 passing
Build: Clean
Changes: 847 lines added, 312 lines removed
Token usage: 57K input / 12K output ($0.19 at Claude 3.5 Sonnet rates)
================================

Principle 7: Optimize Your Prompt Structure

Prompt Structure for Loop Engineering

  1. Define the goal explicitly — use testable success criteria
  2. Specify the output format — structured JSON or specific file patterns
  3. Include verification steps — the agent should run its own checks
  4. Provide error recovery instructions — what to do when things go wrong
  5. Set boundaries in the prompt — max files, max iterations, scope limits

Real-World Example: .cursorrules for Convergence

For Cursor users, a well-structured .cursorrules file enforces convergence by bounding the agent's behavior from the start:

# .cursorrules — enforce loop convergence
- Always run tests after modifying code
- If tests fail, read the error message before attempting a fix
- Do not modify more than 3 files per iteration
- Stop and ask if you have attempted the same fix twice without progress
- Commit after each successful test pass

Principle 8: Choose the Right Agent Framework

FrameworkGitHub StarsLoop Control ModelBest For
LangGraph10K+Graph-based state machines with explicit edgesComplex workflows with branching logic
CrewAI30K+Role-based task delegation with sequential/hierarchical executionTeam-oriented multi-agent collaboration
AutoGen40K+Conversational multi-agent with flexible message passingResearch and experimentation
MetaGPT45K+Standardized operating procedures (SOPs) as loopsReproducible software development processes

For loop engineering specifically, LangGraph offers the most explicit control: each node in the graph is an iteration step, edges define transition logic, and state is passed explicitly between nodes. This makes convergence and boundary enforcement first-class concepts in the architecture.

Key Takeaways

  1. Verifiable goals are the foundation — if you cannot test it, you cannot loop it
  2. Convergence design ensures the loop gets closer to the goal each iteration
  3. Explicit boundaries prevent runaway loops (max iterations, time, tokens)
  4. Tiered error handling matches recovery strategy to error type
  5. Structured state enables checkpointing, resumption, and debugging
  6. Human communication keeps stakeholders informed without requiring constant attention
  7. Prompt structure directly affects loop consistency across iterations
  8. Framework choice determines how naturally you can express loop control logic

Prerequisites