mech.app
Dev Tools

Harness Engineering: How Repository Structure Stops Coding Agents from Hallucinating

Structured repository scaffolding and Markdown control layers reduce agent hallucination by constraining context, defining file boundaries, and preventi...

Source: dev.to
Harness Engineering: How Repository Structure Stops Coding Agents from Hallucinating

Coding agents can navigate directories, write tests, and submit PRs. They also hallucinate file paths, rewrite unrelated modules, and get stuck in refactor loops that consume hours without shipping a single working commit. The problem is not the model. The problem is the absence of physical constraints in the repository itself.

Harness engineering treats the repository as a control surface. Instead of relying on prompt engineering alone, you embed a lightweight system of record directly into the workspace. The agent reads structured Markdown files that define boundaries, enforce style rules, and prevent scope creep before the first line of code is generated.

Why Prompts Fail at Scale

A typical coding agent workflow looks like this:

  1. User provides a natural language task.
  2. Agent reads the repository (or a subset).
  3. Agent generates code, writes files, runs tests.
  4. Agent submits changes.

The failure modes appear between steps 2 and 3:

  • Context window pollution: The agent reads too many files, loses track of the original task, and starts “fixing” unrelated code.
  • Agent amnesia: Long sessions exhaust context, and the agent forgets prior decisions or constraints.
  • Scope drift: A bug fix becomes a refactor, then a rewrite, then a multi-module overhaul.
  • File overwrites: The agent generates a new file with the same name as an existing one, silently clobbering production logic.

Prompt engineering can mitigate some of this, but it does not scale. Every new task requires a new prompt. Every new developer requires a new mental model. The repository itself remains unstructured, and the agent has no durable memory of what is safe to touch.

The Harness Pattern

A harness is a set of Markdown files stored in the repository root (or a .harness/ directory) that the agent reads before performing any work. These files act as a router, a style guide, and a safety boundary.

The core components:

FilePurposeConstraint Type
AGENTS.mdGlobal entry point, defines priority kernel and reply constraintsBehavioral
CODEX_CODING_GUIDELINES.mdSurgical coding rules (minimal changes, style-matching, goal-driven execution)Stylistic
TERMINAL_AND_GIT_RULES.mdSafe terminal navigation, git scoping, proxy configurationOperational
SESSION_HANDOFF_RULES.mdContext capture and continuity between sessionsState Management
ARTIFACT_RULES.mdRules for generated files, naming conventions, output boundariesStructural

Each file is a plain Markdown document. The agent reads them in order, and the rules cascade. If AGENTS.md says “never use git add -A”, the agent will not use it, even if the user prompt says otherwise.

Architecture: How the Harness Constrains Execution

The harness operates as a pre-execution filter. Before the agent writes code, it:

  1. Reads AGENTS.md to understand the priority kernel (what takes precedence: user prompt, harness rules, or model defaults).
  2. Reads CODEX_CODING_GUIDELINES.md to understand style constraints (e.g., match existing indentation, avoid unnecessary refactors).
  3. Reads TERMINAL_AND_GIT_RULES.md to understand operational boundaries (e.g., use pkexec for root commands, never commit untracked files without explicit review).
  4. Reads SESSION_HANDOFF_RULES.md to check for prior session state (e.g., “last session was refactoring auth.py, do not touch billing.py”).
  5. Reads ARTIFACT_RULES.md to understand file naming conventions (e.g., generated files must end in .gen.py, test files must live in tests/).

The agent then generates code. If the generated code violates a harness rule, the agent either self-corrects or surfaces the conflict to the user.

This is not a runtime enforcement layer. It is a context-shaping layer. The harness does not prevent the agent from writing bad code. It prevents the agent from misunderstanding the task in the first place.

Example: Preventing Scope Drift with CODEX_CODING_GUIDELINES.md

A common failure mode: the user asks the agent to fix a bug in auth.py, and the agent decides to refactor the entire authentication module, rewrite the session manager, and add a new logging framework.

The harness prevents this with a simple rule:

## Minimal Changes

When modifying existing code:
- Change only the lines necessary to complete the task.
- Match the existing style (indentation, naming, comment density).
- Do not refactor unrelated code unless explicitly requested.
- If a refactor is necessary, surface it as a separate task.

The agent reads this before generating code. If the generated diff touches more than 10 lines for a single-line bug fix, the agent pauses and asks: “This change is larger than expected. Should I proceed or split this into a refactor task?”

This is not magic. It is a deterministic constraint that the agent can evaluate before execution.

Example: Preventing File Overwrites with ARTIFACT_RULES.md

Another failure mode: the agent generates a new file called config.py, not realizing that config.py already exists and contains production secrets.

The harness prevents this with a naming convention:

## Generated File Naming

All agent-generated files must follow this pattern:
- `*.gen.py` for Python code
- `*.gen.md` for Markdown documentation
- `*.gen.json` for configuration files

Before writing a file, check if a non-generated version exists. If it does, surface a conflict.

The agent reads this, sees that config.py exists, and either renames the generated file to config.gen.py or asks the user to resolve the conflict.

Operational Boundaries: Git and Terminal Safety

The harness also defines operational constraints. For example, TERMINAL_AND_GIT_RULES.md might include:

## Git Scoping

- Never use `git add -A` or `git add .`
- Always use `git add <specific-file>`
- Before committing, run `git diff --cached` and surface the diff to the user
- Never push to `main` or `master` without explicit confirmation

This prevents the agent from accidentally committing untracked files, pushing to protected branches, or running destructive commands.

Similarly, for terminal commands:

## Root Commands

- Use `pkexec` for root commands instead of `sudo` (avoids CLI password prompts)
- Never run `rm -rf` without explicit confirmation
- Always use `--dry-run` flags when available

These rules are not enforced by the harness itself. They are enforced by the agent’s pre-execution context. The agent reads the rules, understands the constraints, and generates commands that comply.

Session Handoff: Continuity Between Runs

Long-running tasks often span multiple agent sessions. Without a handoff mechanism, the agent forgets what it was doing and starts over.

SESSION_HANDOFF_RULES.md defines how state is captured:

## Session State Capture

At the end of each session, write a `SESSION_STATE.md` file with:
- Current task and progress
- Files modified
- Next steps
- Known blockers

At the start of each session, read `SESSION_STATE.md` before reading the user prompt.

This is a simple, file-based state machine. The agent writes a Markdown file at the end of each session, and reads it at the start of the next. No database, no external service, no complex orchestration.

Failure Modes and Mitigations

Even with a harness, agents can fail. The harness reduces the likelihood, but it does not eliminate risk.

Failure ModeHarness MitigationResidual Risk
Context window exhaustionSession handoff rules capture stateAgent may still lose context mid-session
File overwritesArtifact naming conventionsAgent may ignore rules if prompt is too strong
Scope driftMinimal change guidelinesAgent may interpret “minimal” differently
Circular importsExplicit import rules in CODEX_CODING_GUIDELINES.mdAgent may not detect all cycles
Destructive commandsTerminal safety rulesAgent may execute before reading rules

The harness is not a sandbox. It is a guide. The agent can still violate the rules if the user prompt is strong enough or if the model hallucinates. The harness reduces the frequency of violations, but it does not prevent them.

When to Use a Harness

A harness makes sense when:

  • You are running coding agents in production repositories.
  • You have multiple developers or agents working in the same codebase.
  • You need deterministic, reviewable agent behavior.
  • You want to reduce the cost of prompt engineering by embedding constraints in the repository.

A harness does not make sense when:

  • You are running one-off experiments or demos.
  • The repository is small enough to fit in a single context window.
  • You trust the agent to operate without constraints.

Technical Verdict

Harness engineering is a practical, low-overhead approach to reducing coding agent hallucination. By embedding constraints directly in the repository, you shift the burden from prompt engineering to infrastructure design. The harness is not a runtime enforcement layer. It is a context-shaping layer that guides the agent before code is generated.

Use a harness when you need deterministic, reviewable agent behavior in production repositories. Avoid it for one-off experiments or small codebases where the overhead is not justified.

The pattern scales well because it is file-based, version-controlled, and human-readable. Developers can review the harness rules, propose changes, and enforce them through CI. The agent reads the same files every time, and the behavior is consistent across sessions.

The failure modes are known and mitigated, but not eliminated. The harness reduces the frequency of hallucination, but it does not prevent it. Pair the harness with code review, automated testing, and observability to catch violations before they reach production.

Tags

agentic-ai orchestration infrastructure

Primary Source

dev.to