Agents are only as good as the feedback loop the repo gives them. In a codebase with a 20-minute flaky test suite and no type checking, an agent flails exactly like a new hire would — worse, because it flails at 400 lines a minute.
Tier: Core · Workflow area: Feature development
An afternoon of harness engineering (fast deterministic tests, linters and type-checkers as machine feedback, seed docs) raises the quality of every agent session afterwards — and, not coincidentally, every human's too.
Skip heavy investment for repos in maintenance freeze that see one change a quarter.
Give the agent (and humans) one obvious command per feedback type. Agents discover Makefile/package.json scripts and use them. Target set:
test-fast: ## unit tests only, no network, < 2 min
pytest tests/unit -x -q --disable-socket
lint: ## style + static errors, auto-fixable
ruff check . && ruff format --check .
typecheck:
mypy app/
check: lint typecheck test-fast ## the agent's inner loop
The contract that matters: make check is fast (minutes, not tens of minutes), deterministic (same input, same result), and self-contained (no VPN, no shared staging DB, no secrets).
Kill nondeterminism in the fast lane. Flaky tests do double damage with agents: they train it to "just rerun", and they mask real regressions it introduces. Quarantine flaky tests out of test-fast (marker + separate CI job), fake the clock, stub the network (pytest-socket, nock, msw), and seed randomness.
Turn silent conventions into machine feedback. Every rule that lives only in reviewers' heads, an agent will violate. Promote the recurring ones into lint/type config — e.g., ban raw SQL outside the repository layer:
# ruff: flag naked datetime.now() (must use our clock service)
[tool.ruff.lint.flake8-tidy-imports.banned-api]
"datetime.datetime.now".msg = "Use app.lib.clock.now() (tz-aware, fakeable)"
Rules the linter cannot express go in the instruction file instead (pb-04); the split is: enforce mechanically what you can, instruct what you cannot.
Write the seed docs agents actually read. Three short files beat a wiki: CLAUDE.md/AGENTS.md (conventions + the make check contract + exemplar-file pointers), CONTRIBUTING.md (how to run one test, add a migration, where fixtures live), and an architecture sketch listing the load-bearing modules. Keep each under a screen or two.
Close the loop with a probe task. Give your agent a standard small task ("add field X to endpoint Y with tests") before and after the changes. Watch: does it find make check unaided? Does lint feedback correct it mid-session? Iterate on whatever it still fumbles.
Guard the fast lane in CI. Add a CI timer assertion or at least a dashboard for make check duration and flake rate — harnesses rot, and a 2-minute loop quietly becoming 9 minutes un-does the whole investment.
An agent-friendly repo is just a well-harnessed repo — every improvement here is a straight developer-experience win even if you never run another agent.
-x -q, pytest-socket, freezegun.tsc --noEmit + Vitest/Jest with msw for network fakes; scripts in package.json (npm run check).go vet + go test -short), strict compiler settings doing the type-feedback job natively.make check (or equivalent) runs green from a fresh clone, offline, in under ~2 minutes, three runs in a row with identical results.datetime.now()) and confirm the linter catches it.make check helps nobody mid-session.This playbook barely touches external AI — it is repo engineering, so it works fully inside client environments and is often the first thing worth doing on a restricted engagement (it pays off even with humans-only). If no hosted agent is sanctioned for the probe task, use a local model (Ollama + coder model) driven by a simple loop, or run the probe yourself pretending to be the agent: fresh clone, only documented commands, note every point where tribal knowledge was required. Each such point is a fix.
make check green predicts CI green ≥95% of the time — the local loop is trustworthy.