AWS Builder Center
Two harnesses every coding agent needs, and why sensors beat prompts

Two harnesses every coding agent needs, and why sensors beat prompts

Coding agents need two distinct harnesses, one to execute and one to verify, and most teams are investing in the wrong one. A practical template showing why adding sensors pays off forever while tuning prompts doesn't

Two harnesses every coding agent needs, and why sensors beat prompts

A reviewer model scanned a CDK module against Well-Architected checklists. It found ten architecture gaps. The top one (SQS queues missing in-transit encryption enforcement) became a task. An editor agent read the task, edited the code, and submitted it to four sensors: TypeScript compiler, ESLint, Jest, and cdk-nag. All passed. A reviewer model evaluated the diff against Security and Reliability checklists. It passed. cdk deploy pushed the change to a preview environment. A post-deploy harness validated the live stack. The loop terminated with success. One iteration, four minutes, under fifteen cents at current Sonnet pricing.
No human between "find the gap" and "gap is fixed in deployed infrastructure." The system found its own work and did it. The rest of this post explains the two layers that make this possible, where teams should invest, and what the responsible boundary looks like today.

1. Two layers, one overloaded word

If you've read recent writing on coding agents, you've seen the shorthand: agent equals model plus harness. The model is the part everyone talks about. The harness is everything else, and "everything else" is doing too much work. It conflates two genuinely different things with different owners, different lifecycles, and different failure modes.

Runtime harness

The layer that lets the agent act. Orchestration loop, sandbox, session state, tool wiring, permission model. What AWS ships as AgentCore Managed Harness, what OpenAI calls Codex, what frameworks like LangGraph provide. The defining property: it executes the agent. Without it, there is no agent.
This layer is commoditising. Most teams consume it rather than build it. The leverage is in configuring boundaries (sandbox limits, tool scopes, memory policies), not in reimplementing the loop.

Engineering harness

The layer that decides whether what the agent produced is good enough to keep. Steering files, sensors, review gates, deploy gates, post-deploy validation. The accumulated discipline that makes a particular codebase, with a particular team's risk tolerance, trustable to an agent.
This layer is not commoditising. It's specific to a codebase, a team's conventions, a risk tier. A steering file that's right for a fintech service is wrong for an internal CLI. Whatever a vendor ships as a default, the team accumulates their own.

The seam

Capability versus confidence. The runtime harness decides what the agent is able to do. The engineering harness decides whether what it did is good enough. Conflating the two leads to either over-trusting a permissive sandbox or under-trusting outputs that have actually passed strong sensors.
The Seam
The Seam
The runtime harness (vendor-provided, configured once) executes the agent. The engineering harness (team-built, compounds over time) checks what the agent produced. Different owners, different lifecycles, different failure modes.
The naming follows Birgitta Böckeler's harness engineering primer , which emphasises the team-built scaffolding as the core meaning of "harness." The broader industry usage (LangChain's "anatomy of an agent harness" , for instance) uses the same word for what this post calls the runtime layer. Neither is wrong; the conflation is what this post names.

2. Where to invest

Investment in the two layers compounds at different rates.
Runtime-harness work has a flat payoff curve: configure it once, stay configured until the vendor ships a breaking change. There is a finishing line.
Engineering-harness work has no finishing line. Every sensor you add fires on every future change to the code it watches. Every steering rule shapes every future edit. A pre-commit hook that catches one class of bug catches that bug forever. The investment compounds.
The pattern worth flagging: effort tends to flow toward prompt tuning (helps one task) rather than sensor addition (helps every future task). And there's a recurring temptation to build custom orchestration plumbing, the part that's commoditising fastest.
A simple test: of the last ten hours your team spent improving how an agent works in your codebase, how many went to prompt iteration, how many to adding a sensor, and how many to rebuilding orchestration plumbing? If prompt iteration dominates, the work isn't compounding.
The runtime harness cannot tell you whether the code the agent produced is correct for your codebase, your conventions, your risk tier. That answer is local to the team, and the only place it can live is the engineering harness. No vendor can ship it.

3. The bounded closed loop

Once a team has sensors wired, a natural question follows: what if the agent could read those signals and iterate on its own?
The fully autonomous version (post-deploy telemetry from production triggers agent investigation and remediation without human approval) is being built today, mostly outside coding agents. AWS's DevOps Agent  and Microsoft's SRE Agent for Azure  both investigate and remediate operational incidents autonomously. VirtusLab's maturity matrix  names it as a production pattern for "well-defined operational tasks."
Those operational loops succeed because scope is narrow: scaling adjustments, parameter tuning, self-healing for known failure patterns. A coding-agent loop is broader: the action space is "any change to source," success criteria are partial, and the rollback path is whatever git and CI/CD give you. Specifically, four prerequisites that operational loops meet and coding-agent loops mostly don't:
  • Rollback discipline. The agent's fix made things worse. Who reverts, how fast, with what blast-radius limits? If "the agent reverts itself" is the answer, the rollback path needs to be more reliable than the forward path.
  • Sensor reliability. The agent acts on the post-deploy signal. If the sensor has a meaningful false-positive rate, the agent is changing code in response to noise. False positives in a closed loop don't just waste effort; they actively degrade the system.
  • Oscillation and drift. Two sensors that disagree can put a closed loop into oscillation: fix for sensor A breaks B, fix for B breaks A, repeat. Drift is the slower version: each iteration moves the codebase further from any human's mental model. Detecting either requires explicit machinery.
  • Blast radius. The agent has the keys to deploy. What's the largest thing it can break in one iteration? If the answer is "anything in the account," the trust budget required is enormous.

The responsible version today

A smaller loop with the same shape but a lower trust budget: an ephemeral preview environment with hard scope limits.
The agent makes a change. Sensors check it. The change deploys to a preview that exists only for this session. A synthetic post-deploy signal fires against the preview. The signal becomes context for the agent's next iteration. The agent either converges or stops. A human reviews the result. The preview tears down.
The loop is closed: post-deploy feedback flows back without human intervention. It is bounded: nothing reaches production, nothing persists past the session, the blast radius is scoped. Extending past this boundary requires prerequisites (reliable rollback, low-false-positive sensors, oscillation detection, narrowed action space) that the bounded loop surfaces in working code.

4. The template

A CDK module (API Gateway → Lambda → SNS → SQS → Lambda) maintained by an editor agent, checked by four computational sensors plus an inferential reviewer, deployed to preview, and validated by a post-deploy harness. The runtime is AgentCore Managed Harness (swappable). The engineering harness is the interesting part.

The gate chain

The gate chain
The gate chain
Computational sensors (tsc, ESLint, Jest, cdk-nag): deterministic, structured output, fast. If any fail, the agent gets file/line/message and iterates. Code never reaches the reviewer or deploy with known defects.
Inferential reviewer: a separate model invocation evaluates the diff against Well-Architected checklists. The reviewer cannot edit; the editor cannot grade its own work. Catches IAM scope creep, missing encryption, reliability gaps that static tools miss.
CDK deploy to preview: real CloudFormation, real resources, ephemeral. Catches circular dependencies, IAM denials, config that synthesizes but doesn't deploy.
Post-deploy harness: synthetic tests against the live stack. Hits the API, drives messages through the fan-out, validates properties. Catches the gap between "code is correct" and "deployed system works."

Two task types

The loop receives a JSON payload and doesn't care where it came from.
Two task types
Two task types
Task 1: Feature change. A human describes what they want. The agent does it.
Task 2: Autonomous discovery. A reviewer model scans the module, finds gaps, and builds a task payload from the top finding. The same loop remediates it. In production, this runs on a schedule; the system continuously maintains its own architectural standards.
Both are invoked from the same script:
1
2
./scripts/setup.ps1 -FromStep 5b -TaskType feature # Task 1
./scripts/setup.ps1 -FromStep 5b -TaskType discover # Task 2

Worked example: Feature change

Trigger: "Increase DLQ retention to 30 days, reduce maxReceiveCount to 3."
Iteration 1: Editor modifies stack and test. eslint fails: unused variable. Agent iterates.
Iteration 2: Editor fixes variable. All sensors pass. Reviewer passes. Deploy succeeds. Post-deploy passes.
Result: success. 2 iterations, ~5 min, under $0.15 token spend at current Sonnet pricing.

Worked example: Autonomous discovery

Discovery: Reviewer scans module. 10 findings. Top: "SQS queues missing enforceSSL" (high severity).
Iteration 1: Editor reads finding, adds queue policies. All sensors pass. Deploy succeeds. Post-deploy passes.
Result: success. 1 iteration, ~4 min, under $0.15 token spend at current Sonnet pricing. The finding contained a precise diagnosis; the agent converged in one shot.

Stop conditions

ConditionDefaultOutcome
Successn/aPost-deploy passed. Done.
Iteration cap8Stop, surface session log
Wall-clock cap12 minStop, surface session log
Token-spend cap$10Stop, surface session log
Oscillation5-6 iterationsStop, surface session log
Kill switchManualStop immediately
Oscillation detection checks two heuristics: same diff produced twice within the last 5 iterations (stuck), or gate results alternating between two states over 6 iterations (ping-ponging). Either triggers an immediate stop. Every non-success termination is a correct outcome: the system detected it couldn't converge and handed off with full context.

Steering

AGENTS.md at the module root: conventions, tag policy, encryption requirements, construct-factoring rules. The agent reads it before every edit. Feedforward: shapes the first attempt rather than catching errors after.

5. What this does not claim

Five things, named so the boundary is explicit:
  1. Autonomous production deployment. A human approves changes. The loop closes on the preview, not on main.
  2. Multi-agent coordination. One agent, one task, one preview.
  3. Harness self-evolution. The agent does not edit its own sensors or steering files.
  4. Full correctness guarantee. Sensors reduce risk; they don't eliminate it.
  5. Generality beyond IaC. The template demonstrates the pattern on a CDK module. Application code, config management, or other targets need their own sensors.

6. Four questions for your own team

1. Can you name which runtime harness you're consuming and which boundaries you've actually configured? If "runtime harness" doesn't map to a specific thing in your stack, you may be conflating it with the engineering harness. First move: write down the orchestration loop, sandbox, and permission model for one agent. Mark which you configured deliberately vs accepted as defaults.
2. Can you point to one sensor that fires after the agent thought it was done? If every sensor runs while the agent works (linters, in-IDE feedback), you have no post-action signal. First move: add one. A CI step that feeds its output back as context is a start. A closed loop with a preview environment is the next step.
3. When the agent's output breaks something, does the breakage become context for its next attempt? If the failure leaves the agent's context, the loop isn't closed; the team is closing it manually each time. First move: wire one failure signal back into agent context automatically.
4. If you wanted to extend a sensor into a closed loop with a preview environment, what's missing? Walk through the answer. It surfaces the engineering-harness gaps. First move: sketch the bounded loop. Mark what exists, what exists but isn't wired, and what's missing entirely. The marked sketch is the roadmap.

In production: wiring the trigger

The template triggers the loop from a script. In a team's daily workflow, the same JSON payload arrives from whatever dispatch mechanism the team already uses:
Task 1 (human-initiated): A developer labels a GitHub issue agent-task. A GitHub Action builds the trigger payload from the issue fields and POSTs it to a webhook that starts CodeBuild. Alternatively: a Slack command, a CLI tool, a button in an internal dashboard, anything that can construct the payload and start the build.
Task 2 (scheduled discovery): A cron job (GitHub Actions schedule, EventBridge rule, or a simple cron on any server) invokes the reviewer model against the module, collects findings, and starts the loop for each finding above the severity threshold. The template's discover mode demonstrates this path end to end.
The kill switch: The loop checks a KillSwitchPoll interface before each iteration. The template's implementation is a no-op. A production implementation polls for a label on an issue, a flag in DynamoDB, or a file in S3, whatever the team's "stop everything" signal is.
The bounded loop itself is platform-agnostic. The integration is straightforward CI/CD wiring on top, the kind most teams have done before for other automation.
The output: When the loop succeeds, the module's source files are modified in the CodeBuild workspace and a session record is written (what changed, which sensors fired, what the reviewer said, how many iterations). In production, a post-build step would commit, push to a feature branch, and open a PR for human review. Partial terminations (iteration cap, oscillation) produce the same session record with the termination reason, so the human sees exactly where the agent stopped and why.

Try it

Clone from this repository 
1
2
3
4
5
6
7
8
9

# Deploy infrastructure (CDK bootstrap, CodeBuild, AgentCore harnesses)
./scripts/setup.ps1 -AccountId "<your-account>" -FromStep 3

# Task 1: human-initiated feature change
./scripts/setup.ps1 -AccountId "<your-account>" -FromStep 5b -TaskType feature

# Task 2: system discovers a gap and fixes it autonomously
./scripts/setup.ps1 -AccountId "<your-account>" -FromStep 5b -TaskType discover
Watch the loop find a gap, fix it, deploy it, and validate it. Then add a sensor for your codebase. That's where the investment compounds.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article