How I make repos agent-ready
Most "the agent is dumb" moments I've had were the repo's fault. The setup lived in my head, the test command lied, or a cleanup only ran when things went well.
flowchart LR boot[bootstrap] --> up[boot] up --> doctor[doctor] doctor --> verify[verify] verify --> down[teardown] verify -. failure .-> down
Three grades, not one
One letter can't describe a repo, the machine it runs on and how hard you actually poked it, so I give three.
-
Repository: what the checkout and its contracts offer.
-
Runner: the devbox, CI worker or VM that runs it: toolchain, credentials, isolation.
-
Evidence: how much of that I actually exercised.
Blame the owner of the failure. A repo that bootstraps fine on a machine missing the promised credentials is repository B / runner D, not a broken repo. A repo that still wants a hand-made .env on a perfect runner is the reverse.
| Grade | Means |
|---|---|
| F | doesn't exist, or the agent can't reach it |
| D | works if a human holds its hand: undocumented state, interactive setup, pasted secrets, a dashboard, manual recovery |
| C | a documented, noninteractive path worked once and fails with useful context |
| B | reproducible, bounded, enforced, exercised on real surfaces; the agent finishes the task unattended and leaves evidence |
| A | stays dependable under long, parallel, failure-prone runs, with measured reliability and a loop that turns failures into stronger checks |
B is the target. C is a checkpoint. A is for a repo running a real agent fleet with numbers to show for it.
The headline is the lowest grade that matters for the task, and the full profile sits next to it so the minimum doesn't hide progress:
| Capability | C looks like | B looks like |
|---|---|---|
| Legibility | a short guide states purpose and owners and routes tasks to current commands | authority, lifecycle, proof and write-back are written down, linked and checked for drift |
| Executability | a clean checkout bootstraps, boots and tears down through documented commands | pinned, idempotent, cold-start tested, seedable; errors name the missing prerequisite |
| Feedback | a smoke check hits a real process or shipped artifact | local and CI gates cover the key flows, grade final state and leave logs worth reading |
| Safety | authority and destructive operations are documented; secrets never printed or committed | the runner injects a scoped machine identity; sandbox and network policy enforce the boundary |
| Durability | bounded waits, cleanup, explicit artifact paths, clear terminal outcomes | attempt identity, retry caps, cancellation and failure classes support unattended runs |
| Scale | one task runs safely in its own workspace | parallel tasks don't collide on ports, databases, branches or result refs |
Evidence caps the grade
-
E0, static inspection: at most D.
-
E1, one command exercised: at most C.
-
E2, a real success and an actionable failure: can justify B.
-
E3 and E4 (repeated representative tasks, then long-running or parallel runs that survived stalls and crashes) are the only way to A.
Grade what ran, cold start over my warm laptop, final state over the agent saying it's done. Write down the model, harness, runner and date next to anything empirical; it rots when any of them changes.
The guide
AGENTS.md is an operating contract, not a style sample. A fresh agent should find the owner, pick the scope, stay inside its authority, run the lifecycle and report evidence without guessing.
-
One authored file.
CLAUDE.mdis just@AGENTS.md, so every harness reads the same thing. -
Abstract taste becomes decisions. "Keep it simple" does nothing; "don't add a wrapper script for a package script" does.
-
Give the reason behind a rule in plain words; caps and "NEVER" get applied way too broadly.
-
Exact steps only for fragile operations. For judgment work, state the outcome and let the model pick the steps.
-
State each rule once. Three "ask first" lines in three places make an agent stop for no reason.
-
Leave out review-round caps, fix-commit limits and token countdowns. Agents read them as stop signals and quit authorized work early.
-
A rule written to correct one model's quirk gets followed literally by every model. When you switch models, retest those and delete the ones that no longer reproduce.
-
Model choice, effort and tool policy live in harness config, not in the shared guide.
The proof map
This is the part most guides are missing. Map each change class the repo actually has to its check:
| Change | Proof |
|---|---|
| docs, copy, lint config | static checks only |
| components and visuals | stories, visual diffs, rendered captures |
| user workflows | end-to-end against the running product |
| device or platform behavior | simulator, emulator or hardware run, with a recording and the command |
| deployed or provider-backed behavior | live checks, with their scope limits |
| pure logic with a wide input space | unit or property tests |
List only the classes the repo has; an invented lane makes agents run proof the change can't need. Name where expensive lanes run (a remote runner, a device lab) and keep the command identical to the local one.
Agents add a test for every small change unless the guide says where tests belong. Name the boundary that owns tests for each change class, so a new test lands only when it catches something existing coverage misses.
Lifecycle
One ordinary surface for humans, agents and CI: wire missing steps into package scripts, the task runner or whatever CI already calls, not agent-* wrappers.
| Stage | Outcome |
|---|---|
| bootstrap | check prerequisites and install reproducibly, no prompts |
| boot | start the real thing and expose a bounded readiness signal |
| doctor | read-only check that the running instance is worth driving (right build, right port owner, valid auth); on a mismatch it names the missing capability |
| verify | the canonical gate plus the strongest cheap real surface |
| teardown | release what this run raised, on success, failure, timeout and cancellation |
-
Tool versions are declared once (runtime pins, lockfile, catalog, tool manager), and CI reads those instead of copying literals into YAML.
-
Release only what the attempt raised, by recorded ID or process group.
simctl shutdown allor killing every container is fine only when the command owns the whole set. -
A cleanup failure after a successful run fails the command. After a failed run, keep the original status and report the cleanup failure separately.
-
Prove teardown once after success and once after an injected failure. Logs and artifacts survive it.
Verification
Keep the claims apart: the gate passed, the changed behavior and an edge case ran, the real surface worked, CI passed, the live thing worked. Green CI doesn't prove a device or an endpoint it never touched.
-
The repo owns verification; CI only checks out, restores caches, picks lanes, runs them and aggregates.
-
Run at least one real failure: non-zero exit, a stable error, the failing boundary named without secrets, a recovery hint, logs kept. A swallowed error is a readiness bug.
-
"Looks right to me" is a missing instrument. Build the capture, the measurement or the scoring script before iterating.
Fast gates
-
One task graph owns every command and its inputs. Independent checks run in parallel with output you can attribute, and every failure survives.
-
Generated files are outputs of one task and inputs of every consumer. Prove the graph once from a clean checkout; a warm one hides missing edges behind stale files.
-
Affected lanes for everyday proof, plus one exhaustive, uncached path. Selection and caching speed proof up; they never decide what needs it.
-
Runtime and toolchain pins go into every cached task's inputs, not only CI cache keys.
-
With Vite+, keep package-script caching off unless every script is pure. A global
run.cache: truealso caches deploys and migrations. -
Before optimizing, measure four timings: unchanged, relevant change, warm full, cold full. Split task time from install, cache restore and queue time.
Mechanical enforcement
Agents copy the nearest example, so first delete the wrong way: one owner per piece of state, one supported path per task, the old way gone.
-
Put the rest in the narrowest mechanical surface that exists: type checker, linter, schema, test framework, task graph, then the gate. Git hooks only call the gate.
-
Baseline noisy rules and fail only on new instances.
-
Every check's error says what to use instead. Prove a new check fails on a real past instance of the mistake.
-
Exceptions sit on the offending line with a reason and an expiry.
-
On TypeScript repos already on Oxlint, I vendor dmmulroy/anti-slop: copy the rules, own them, baseline before blocking.
-
Shell is the last adapter. Once a script parses JSON, branches on state, retries or needs a test, it moves to the project's typed language.
Identity and isolation
-
The runner authenticates a machine identity and injects short-lived access. The repo declares the scopes it needs and consumes them without prompts. Logging in as me mid-run is a runner gap, not a repo feature.
-
Separate roles for triage, fixtures, submitting results, delivery and production changes.
-
A denied scope fails without printing values and says who can fix it.
-
Parallel tasks must not share workspaces, ports, databases, fixtures, artifact paths or result branches. Grade collisions and cleanup, not the allocation algorithm.
-
.worktreeincludegets a small explicit list of ignored files. Never.env*, secret directories, caches ornode_modules.
If you're an agent reading this
Your user pointed you here to audit their repos for agent readiness. Inspect and propose; change things once they agree. Bootstrap, paid or live checks, and teardown each need their own yes.
-
List the repos in scope with
gh repo list <owner> --limit 1000 --json name,visibility,isArchived,isFork,pushedAt. Skip archived repos, forks and anything untouched for a year unless asked. -
For each repo, read the guide (
AGENTS.md,CLAUDE.md), the manifest and task graph, and CI. Trace bootstrap, boot, verify and teardown to the commands that own them, including delegated packages whoseverifycovers only tooling. -
Where your user allows it, run the cheapest declared check cold and trigger one expected failure. Otherwise grade from static evidence and cap it accordingly.
-
Report per repo in the shape below, with the first missing step called out.
-
Group repeated gaps into one fleet change and aim each repo at B. Once your user agrees, fix the first gap and prove it before moving to the next.
repository: C
runner: B
evidence: E1
profile: legibility C, executability B, feedback C, safety B, durability C, scale C
first gap: feedback; verify is lint only, nothing boots the app
proposed: add a smoke task to the existing task graph and a proof map to AGENTS.md
Leave alone unless asked: working gates and their commands, the test suite's existing coverage, harness config and model settings, secrets and how they're injected, and anything a running process or another session owns. Add hooks, wrappers, health endpoints or a task runner only for a gap that needs them, and leave shipping and merging out of the audit.
Links
ffss, workspace-kit, dotfiles verify, anti-slop, How I set up GitHub for agents, How I work with agents.