Skip to content

Latest commit

 

History

History
143 lines (114 loc) · 6.31 KB

File metadata and controls

143 lines (114 loc) · 6.31 KB

Loop Engineering — Architecture & Primitives

The Anatomy of a Loop

┌─────────────────────────────────────────────────────────┐
│  SCHEDULE (cron / trigger)                              │
│  ┌─────────────────────────────────────────────────┐   │
│  │  READ STATE (~/.hermes/loops/state/<name>.json) │   │
│  │  ┌───────────────────────────────────────────┐  │   │
│  │  │  EARLY-EXIT CHECK                         │  │   │
│  │  │  ┌─────────────────────────────────────┐  │  │   │
│  │  │  │  MAKER SUB-AGENT                    │  │  │   │
│  │  │  │  (implement, build, scan, write)    │  │  │   │
│  │  │  └─────────────────────────────────────┘  │  │   │
│  │  │  ┌─────────────────────────────────────┐  │  │   │
│  │  │  │  CHECKER SUB-AGENT                  │  │  │   │
│  │  │  │  (verify, score, find flaws)        │  │  │   │
│  │  │  └─────────────────────────────────────┘  │  │   │
│  │  │  ┌─────────────────────────────────────┐  │  │   │
│  │  │  │  RESOLUTION                         │  │  │   │
│  │  │  │  Pass → complete/record             │  │  │   │
│  │  │  │  Fail → iterate or escalate         │  │  │   │
│  │  │  └─────────────────────────────────────┘  │  │   │
│  │  └───────────────────────────────────────────┘  │   │
│  └─────────────────────────────────────────────────┘   │
│  WRITE STATE                                            │
└─────────────────────────────────────────────────────────┘

Core Primitives

Maker/Checker Split

The single most important safety primitive. Never let the implementer validate its own work.

  • Maker — a task-oriented sub-agent that does the work (scan, fix, write, build)
  • Checker — a separate sub-agent (ideally a different model family) that reviews the maker's output

Configuration in state file:

{
  "maker": {
    "provider": "deepseek",
    "model": "deepseek-chat"
  },
  "checker": {
    "provider": "gemini",
    "model": "gemini-3.5-flash",
    "threshold": 7
  }
}

When the maker and checker use different model families, they have different blind spots. DeepSeek may miss UI issues that Gemini catches, and vice versa.

Early-Exit Guard

Every loop run costs tokens even when there's nothing to do. The early-exit guard detects this and bails in ~3,000 tokens instead of 80,000+.

Rules:

  • Read last_hash or last_commit from state
  • Compare current state of target (git log, file timestamps, API response)
  • If nothing changed since last run → write last_check timestamp, return "no-op"
  • Keep state compact (no accumulated noise)

State field:

{
  "last_scan_hash": "abc123def456",
  "last_scan_result": "no_changes"
}

L1 → L2 → L3 Rollout

Never deploy a loop at full autonomy. Phase in:

Level What the Loop Does Human Involvement
L1: Report-Only Observes, analyses, writes report to state file Human reads, decides
L2: Assisted Fixes Proposes changes (drafts PRs, writes to branch) Human reviews, approves
L3: Unattended Fully autonomous within allowlisted boundaries Exception-only

Patterns specify a recommended start level.

Worktree Isolation (Optional)

For loops that modify files, use Git worktrees to prevent parallel agents from conflicting:

git worktree add ../fix-auth-env fix/auth-env
cd ../fix-auth-env
# ... agent makes changes ...
git add -A && git commit -m "fix: auth env timeout"
cd ../original-repo
git worktree remove ../fix-auth-env

State File Structure

Every loop stores its state at ~/.hermes/loops/state/<name>.json.

Required fields:

{
  "name": "string",
  "type": "deterministic|adversarial|self-critique|post-merge|dependency-sweep",
  "iteration": 0,
  "max_iterations": 10,
  "status": "active|completed|max_iterations_reached|failed|paused",
  "maker": { "provider": "...", "model": "..." },
  "checker": { "provider": "...", "model": "...", "threshold": 7 }
}

Pattern-specific fields are documented in each pattern's markdown file.

Provider Strategy

The repo is provider-agnostic. Set any model for any role:

{
  "maker": { "provider": "openrouter", "model": "anthropic/claude-sonnet-4" },
  "checker": { "provider": "deepseek", "model": "deepseek-chat" }
}

For adversarial patterns, different provider families are recommended (different training data = different blind spots).

For self-critique patterns, a single provider is used with different system prompts per phase.

Pitfalls

Pitfall Mitigation
No max_iterations Default 5 in every state template
Same model for maker/checker Enforce different providers in checker config
Loop runs on empty target Early-exit guard per pattern
State file corruption JSON schema validation on read; write atomically
Token burn on no-ops Early-exit targets <5k no-op cost
Loop keeps trying on same failure Escalate-to-human after 2 consecutive same-failure iterations
Worktree conflicts with parallel agents Worktree isolation or serialise per-repo loops