agent-loops

Agent Loops — Technical Specification

1. Overview

Agent Loops is a system that receives coding tasks as JSON specs, decomposes them into subtasks via LLM when needed, assigns each subtask to a specialized agent running inside an isolated Docker container, tracks progress on an SQLite-backed Kanban board, and consolidates results into a shared workspace.

The system is designed for a single VPS (4 cores / 8 GB RAM) and targets a maximum of 3 parallel agent containers.


2. Architecture

                    ┌─────────────────────────────────────────┐
                    │           VPS (4 cores / 8GB)            │
                    │                                         │
Input (JSON) ──────►│  ┌─────────────────────────────────┐    │
  queue/            │  │  Orchestrator (Node.js)         │    │
                    │  │  ├── dispatcher.js (loop 30s)    │    │
                    │  │  ├── decomposer.js (LLM split)   │    │
                    │  │  ├── docker-runner.js            │    │
                    │  │  ├── git-manager.js              │    │
                    │  │  ├── board.js (SQLite)           │    │
                    │  │  └── status-renderer.js          │    │
                    │  └──────────┬───────────────────────┘    │
                    │             │ dockerode                    │
                    │    ┌────────┼────────┬──────────┐         │
                    │    ▼        ▼        ▼          ▼         │
                    │ ┌──────┐┌──────┐┌──────┐┌──────┐         │
                    │ │backend││front  ││  qa  ││devops│         │
                    │ │iteratr││iteratr││iteratr││iteratr│         │
                    │ │KIT SDK││KIT SDK││KIT SDK││KIT SDK│         │
                    │ │NATS   ││NATS   ││NATS   ││NATS   │         │
                    │ └──┬───┘└──┬───┘└──┬───┘└──┬───┘         │
                    │    └───────┴───────┴───────┘              │
                    │             │ results                      │
                    │             ▼                              │
                    │     results/ + BOARD.md                    │
                    └─────────────────────────────────────────┘

Modules

Module File Responsibility
Dispatcher loop dispatcher.js Polls board every 30 s, picks ready tasks, hands to runner
Decomposer decomposer.js Calls Claude to split a triage task into subtasks
Docker runner docker-runner.js Creates, starts, watches containers via dockerode
Git manager git-manager.js Clones repo, creates branches, pushes results
Board board.js SQLite CRUD for tasks, transitions, history
Status renderer status-renderer.js Regenerates BOARD.md from current board state

3. Task Lifecycle

States

State Meaning
triage Received, awaiting decomposition decision
todo Decomposed; waiting to be picked up
ready Dependencies met, eligible for dispatch
running Agent container is active
done Agent finished, results committed
blocked Agent needs human input
crashed Container exited non-zero
gave_up Exceeded max retries (circuit breaker)

Transition Diagram

                    ┌─────────────────────┐
                    │      triage         │
                    └────────┬────────────┘
                             │ decomposer.js (LLM)
                             ▼
                    ┌─────────────────────┐
                    │       todo          │◄─────────────────────┐
                    └────────┬────────────┘                      │
                             │ deps met                           │
                             ▼                                    │
                    ┌─────────────────────┐                      │
                    │       ready         │◄──────┐              │
                    └────────┬────────────┘       │              │
                             │ dispatcher picks    │ unblock      │
                             ▼                      │              │
                    ┌─────────────────────┐       │              │
                    │      running        │───────┘              │
                    └──┬──────┬──────┬────┘  blocked             │
                       │      │      │                           │
                  done │      │      │ crashed                   │
                       ▼      │      ▼                           │
               ┌──────────┐   │  ┌──────────────┐               │
               │   done   │   │  │   crashed    │─── retry ──────┘
               └──────────┘   │  └──────────────┘   (back to ready)
                              │        │
                              │        │ max retries exceeded
                              │        ▼
                              │  ┌──────────────┐
                              │  │   gave_up    │
                              │  └──────────────┘
                              │
                              ▼
                       ┌──────────────┐
                       │   blocked    │─── human unblocks ──► ready
                       └──────────────┘

Rules


4. Input Format

Tasks are placed as JSON files in queue/. Two modes are supported.

4.1 Auto-decompose mode

The file contains a single high-level task. The decomposer splits it into subtasks.

{
  "mode": "auto",
  "title": "Add user registration flow",
  "body": "Implement sign-up with email verification for the REST API. Includes POST /register, email sending via SES, and verification endpoint.",
  "repo": "https://github.com/org/api-service.git",
  "base_branch": "main",
  "metadata": {
    "priority": "high",
    "acceptance_criteria": [
      "Endpoint returns 201 on valid registration",
      "Verification email sent within 5 seconds",
      "Unverified accounts cannot authenticate"
    ]
  }
}

4.2 Pre-decomposed mode

The file contains an array of tasks already broken down. The orchestrator creates each task directly in todo and resolves dependency ordering.

{
  "mode": "pre",
  "parent_title": "Add user registration flow",
  "repo": "https://github.com/org/api-service.git",
  "base_branch": "main",
  "tasks": [
    {
      "title": "Create User model + migration",
      "body": "Add users table with email, password_hash, verified_at columns.",
      "assignee": "backend",
      "priority": "high",
      "depends_on": []
    },
    {
      "title": "Implement POST /register endpoint",
      "body": "Validate input, hash password, create user, queue verification email.",
      "assignee": "backend",
      "priority": "high",
      "depends_on": ["Create User model + migration"]
    },
    {
      "title": "Send verification email via SES",
      "body": "Lambda or worker that sends a templated email with a verification link.",
      "assignee": "backend",
      "priority": "medium",
      "depends_on": ["Create User model + migration"]
    },
    {
      "title": "E2E tests for registration flow",
      "body": "Cover happy path, duplicate email, invalid input, and verification expiry.",
      "assignee": "qa",
      "priority": "medium",
      "depends_on": [
        "Implement POST /register endpoint",
        "Send verification email via SES"
      ]
    },
    {
      "title": "Add Terraform for SES resources",
      "body": "SES domain identity, DKIM, and IAM policy for sending.",
      "assignee": "devops",
      "priority": "low",
      "depends_on": []
    }
  ]
}

5. Worker Context

When a container is spawned for a task, the agent receives the following context as a structured JSON payload (mounted at /task/context.json inside the container):

{
  "task": {
    "id": "abc-123",
    "title": "Implement POST /register endpoint",
    "body": "Validate input, hash password, create user, queue verification email.",
    "assignee": "backend",
    "priority": "high",
    "acceptance_criteria": [
      "Endpoint returns 201 on valid registration",
      "Verification email sent within 5 seconds"
    ]
  },
  "parent": {
    "id": "parent-456",
    "title": "Add user registration flow",
    "result_summary": "User model and migration created in branch feat/user-model.",
    "metadata": {
      "branch": "feat/user-model",
      "commit": "a1b2c3d"
    }
  },
  "prior_attempts": [
    {
      "attempt": 1,
      "exit_code": 1,
      "stderr_last_lines": [
        "Error: test POST /register returned 500"
      ],
      "started_at": "2026-06-08T14:00:00Z",
      "finished_at": "2026-06-08T14:12:00Z"
    }
  ],
  "comments": [
    {
      "author": "human",
      "body": "Make sure to use bcrypt with cost factor 12.",
      "at": "2026-06-08T14:15:00Z"
    }
  ],
  "workspace": {
    "repo": "https://github.com/org/api-service.git",
    "branch": "task/abc-123",
    "base_branch": "main"
  }
}

Fields

Field Present when Purpose
task Always Title, body, assignee, priority, criteria
parent Task has a parent Summary and metadata from parent result
prior_attempts Task is a retry Stderr tail + timestamps from failed runs
comments Humans have commented Thread of human-supplied guidance
workspace Always Git repo, branch name, base branch

6. Constraints

Constraint Value Rationale
CPU 4 cores total VPS limit
RAM 8 GB total VPS limit
Max parallel containers 3 Leaves 1 core + headroom for orchestrator
RAM per container 1.5 GB --memory=1536m
CPU per container 1 core --cpus=1
Container timeout 30 min Killed after 30 min, marked crashed
Disk per container 5 GB --storage-opt size=5g
Network Outbound only Only LLM APIs (api.anthropic.com, openai.com)
    and git repos (github.com, gitlab.com)
Inbound network None Containers cannot receive external connections
Orchestrator dispatch loop 30 s dispatcher.js interval
Max retries before gave_up 3 Circuit breaker

7. Agent Roles

Role Description Default Model Maker / Checker
backend API routes, DB migrations, business logic, services claude-sonnet-4-20250514 Maker
frontend UI components, styles, client-side logic, accessibility claude-sonnet-4-20250514 Maker
qa Unit tests, integration tests, E2E, coverage enforcement claude-sonnet-4-20250514 Checker
devops Dockerfiles, CI/CD config, Terraform, monitoring claude-sonnet-4-20250514 Maker
sre Alerts, runbooks, on-call tooling, incident analysis claude-sonnet-4-20250514 Checker
security Dependency audits, SAST, secrets scanning, threat models claude-sonnet-4-20250514 Checker

Maker vs Checker

Role Resolution

When the decomposer generates subtasks, it assigns roles based on:

  1. Explicit assignee if provided in the input.
  2. Heuristic matching: file paths (e.g., *.tfdevops, *.test.*qa), keywords in the title/body.
  3. Default fallback: backend.