Agentic Control Loop Architecture: Understanding Sensors, Controllers, and Actuators

An agentic control loop treats a codebase as a dynamic system where a sensor measures current state against a set point, a controller selects the next low-risk change, and an actuator executes the change automatically to continuously maintain code quality.

The agentic control loop is a framework for automating codebase maintenance by treating software repositories as dynamic systems subject to constant developer changes, dependency updates, and generated code. According to the humanlayer/skills repository, this architecture enables teams to deploy autonomous agents that continuously measure, decide, and act upon code quality metrics without manual intervention.

What Is an Agentic Control Loop?

An agentic control loop is an iterative automation pattern that keeps a codebase aligned with defined quality standards. In plugins/design-control-loop/skills/design-control-loop/SKILL.md, the architecture is described as a continuous cycle that counteracts system disturbances—such as accumulating TODO comments, decreasing test coverage, or obsolete imports—by repeatedly measuring the gap between current reality and desired targets, then applying targeted fixes.

Unlike traditional CI/CD pipelines that only validate changes, this loop actively generates and proposes improvements. It runs on a schedule (for example, daily at 02:00 UTC) or on-demand, creating pull requests that move the repository toward its defined set point one safe increment at a time.

The Four Core Components of an Agentic Control Loop

The architecture consists of four tightly-coupled elements, each defined in plugins/design-control-loop/skills/design-control-loop/references/control-loop-taxonomy.md:

Set Point: The Target State

The set point defines the desired condition for a specific property of the repository. This could be quantitative ("test coverage ≥ 80%") or qualitative ("no obsolete imports remaining"). According to the design specification in SKILL.md, the set point is established during Phase B of the design interview and recorded in the skill's design document. It serves as the reference signal that the sensor compares against actual system state.

Sensor: Measuring the Gap

The sensor is any repeatable tool that measures current repository state and computes the deviation from the set point. Implementations range from static analysis linters and AST searches to test runners and custom scripts.

As documented in the control-loop taxonomy, the sensor outputs a structured signal—typically JSON—containing measurable findings such as violation counts or file lists. A minimal implementation is provided in the repository's reference workflow, illustrating how the sensor runs first in the pipeline to capture the current baseline.

Controller: Deciding the Next Action

The controller transforms sensor output into a specific, actionable change plan. This component decides what to act on, how many items to address, and in what order. The implementation can be deterministic (such as sorting findings by priority and selecting the first N) or fully agentic (using LLM reasoning to choose targets).

According to SKILL.md, the controller operates during the workflow's second phase, consuming the sensor's JSON output and producing a target.json file that specifies the exact file or violation the actuator should address. This abstraction layer ensures the actuator receives only validated, low-risk targets.

Actuator: Executing the Change

The actuator is the component that physically modifies the repository. It combines a coding agent—such as Claude Code, Codex, or OpenCode—with repository-local skills that format pull requests and run validation before committing.

As specified in SKILL.md, the actuator reads the controller's target.json input, invokes the chosen coding agent to generate edits, executes validation scripts (like npm test or go test), and creates a pull request if checks pass. The actuator's logic lives in a generated skill directory (for example, <skill-name>/SKILL.md) that defines the specific editing instructions and PR templates.

Supporting Elements: Disturbance and Memory

Two optional components enhance loop resilience:

  • Disturbance: External factors that modify the system outside the loop's influence, such as team commits or dependency bumps. A damping mechanism—such as a PR check that compares sensor output against a baseline—can block regressions caused by these disturbances.

  • Memory: A persistent feedback file (stored in .github/agent-memory/<task>.md according to references/memory-template.md) that records reviewer feedback, false-positive exclusions, and tuning parameters for future iterations. This allows the loop to learn and adapt across runs.

How the Agentic Control Loop Works in Practice

The loop operates iteratively through four stages, as defined in the workflow template at plugins/design-control-loop/skills/design-control-loop/references/workflow-template.yml:

  1. Sensor execution produces a measurable signal (for example, a JSON list of lint violations).

  2. Controller processing consumes the signal, selects a subset of findings, and outputs an action plan to target.json.

  3. Actuator invocation runs the coding agent to implement the plan, validates the change, and opens a pull request with a generated body.

  4. Feedback integration merges the PR result into the repository state, which feeds back into the next sensor run, completing the cycle.

The repository emphasizes running each piece locally first (Phase D) to ensure debuggability before wiring components into CI/CD (Phase E).

Implementing the Three Components in Code

The following examples demonstrate minimal, runnable implementations derived from the repository's reference files.

Creating the Sensor Script

A sensor that counts TODO comments and outputs structured JSON:

#!/usr/bin/env bash

# scripts/sensor.sh

set -euo pipefail

# Find all TODOs and output structured data

count=$(rg -c "TODO" .)
files=$(rg -l "TODO" . | jq -R . | jq -s .)

jq -n --argjson c "$count" --argjson f "$files" \
  '{count:$c, files:$f}'

This script uses ripgrep to scan the repository and jq to format the output as JSON, providing the standardized input expected by the controller.

Building the Controller Logic

A deterministic controller that selects the first available target:

#!/usr/bin/env python3

# scripts/controller.py

import json
import sys

# Load sensor output

sensor = json.load(open(sys.argv[1]))

# Pick the first file containing a TODO

target = {"file": sensor["files"][0]} if sensor["files"] else {}

# Output action plan

json.dump(target, open(sys.argv[2], "w"))

This Python script implements a simple selection algorithm, though production controllers might use LLM-based reasoning to prioritize complex refactoring tasks.

Configuring the Actuator Skill

The actuator skill defines how to apply changes and create PRs:

---
name: my-loop-actuator
description: Applies a change selected by the controller and opens a PR.
---

# Actuator Execution Steps

1. **Read input** (`target.json`) – contains the file to edit.
2. **Generate patch** using the coding agent:
   ```bash
   clcode edit --file "{{target.file}}" \
     --instruction "Remove the TODO comment"
  1. Run validation – execute npm test or equivalent.
  2. If validation passes, create a commit and push a branch.
  3. Generate PR body using the response template and save to pr-body.md.

This Markdown file follows the specification in [`references/agent-runner-templates.md`](https://github.com/humanlayer/skills/blob/main/references/agent-runner-templates.md), defining the interface between the controller's decision and the coding agent's execution.

### Orchestrating the Workflow

The GitHub Actions workflow that orchestrates all three components:

```yaml
name: Agentic Control Loop
on:
  schedule: [{cron: "0 2 * * *"}]
  workflow_dispatch: {}

jobs:
  run-loop:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Run Sensor
        id: sensor
        run: ./scripts/sensor.sh > sensor.json

      - name: Run Controller
        id: controller
        run: ./scripts/controller.py sensor.json target.json

      - name: Run Actuator
        env:
          CLAUDE_API_KEY: ${{ secrets.CLAUDE_API_KEY }}
        run: |
          clcode run --skill ./skills/my-loop/actuator \
            --input target.json \
            --output pr-body.md

      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v5
        with:
          commit-message: "Agentic loop: apply change"
          title: "🤖 Agentic update"
          body-path: pr-body.md

Summary

  • A set point defines the target state (such as test coverage thresholds or zero obsolete imports) that the loop aims to maintain.

  • The sensor measures current repository state using tools like linters, test runners, or AST searches, outputting structured findings.

  • The controller selects specific, low-risk targets from sensor output, deciding what the actuator should modify and in what order.

  • The actuator executes changes using coding agents, validates results, and creates pull requests to merge improvements.

  • Optional memory files persist feedback between iterations, while disturbance handlers protect against external regressions.

Frequently Asked Questions

What is the difference between a sensor and a controller in an agentic control loop?

The sensor measures and reports current state without making decisions, outputting raw data like violation counts or file lists. The controller consumes this data to make decisions about which violations to fix and how to prioritize them, transforming raw measurements into specific action plans. According to control-loop-taxonomy.md, the sensor answers "what is broken?" while the controller answers "what should we fix first?"

How does the actuator ensure changes are safe before creating a PR?

The actuator runs validation scripts—such as npm test, go test, or custom lint checks—immediately after applying edits with the coding agent. As specified in SKILL.md, the actuator only creates a commit and opens a pull request if all validation passes. This dampening mechanism prevents the loop from introducing regressions into the main branch.

Can an agentic control loop handle external disturbances like dependency updates?

Yes, the architecture accounts for disturbances—external factors like team commits or dependency bumps that modify the system outside the loop's control. The repository recommends implementing a dampener, such as a PR check that compares sensor output against a baseline before allowing the actuator to merge changes. This ensures disturbances do not override the loop's progress toward the set point.

Where should I store feedback from previous loop iterations?

Persistent feedback should be stored in .github/agent-memory/<task>.md according to the references/memory-template.md specification. This memory file is loaded by the actuator on every iteration and can contain reviewer comments, false-positive exclusions, or tuning parameters. By maintaining this state between runs, the loop adapts to repository-specific nuances and avoids repeating rejected proposals.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →