# DevAI.md - AI-Assisted Development Patterns
<!-- Context surfaces, progressive disclosure, MCP servers, hooks, subagents, and verification -->
<!-- Context engineering for agentic development - what agents load, what they can reach, what they may do -->
<!-- Last updated: 2026-07-27 -->

---

## Context Engineering, Not Prompt Engineering

Most teams over-invest in prompt wording and leave context loading at its defaults. That is
backwards. The wording of a request matters far less than what the agent already knows, what tools
it can reach, and what it is prevented from doing. Those three things are configuration, they live
in the repository, and they are reviewable.

This file is that configuration, documented. It covers:

1. **Context surfaces** - which files an agent reads, and which one wins when they disagree
2. **Progressive disclosure** - what loads always, what loads on demand, what loads at execution
3. **Tool access** - which MCP servers this repo expects, and at what scope
4. **Hooks** - what is enforced deterministically rather than requested politely
5. **Subagents** - how parallel work is split without collisions
6. **Verification** - independent review, deterministic scanning, risk-tiered approval
7. **Measurement** - whether any of it is working

Deliberately absent: model names, model versions, prices, and product UI element names. All four
expire, and a context file that has quietly gone stale is worse than one that says less.

---

## 1. Context Surfaces and Precedence

### AGENTS.md - the cross-tool file

`AGENTS.md` is the cross-tool standard for repository instructions. It originated at OpenAI in
August 2025 and was transferred to the Linux Foundation's Agentic AI Foundation later that year. It
is now read natively by Claude Code, OpenAI Codex CLI, Cursor, Aider, Devin, GitHub Copilot, Gemini
CLI, Windsurf, Amazon Q, and others.

The format is deliberately minimal: **plain Markdown, no YAML frontmatter, no required schema.**
That is the point. There is nothing to learn and nothing to break, so every tool can read the same
file instead of each getting its own dialect.

Analysis across large numbers of public repositories found the same thing repeatedly: the files
that actually changed agent behavior were **short and specific**, on the order of a couple of dozen
lines. Exhaustive files performed worse, because everything competes for attention and nothing
stands out.

**What belongs in it, in this order:**

1. **Project overview** - what this is, the language, the framework, and the pinned versions
2. **Build and test commands** - the exact commands with the exact flags, not the name of a tool
3. **Code style rules that DIFFER from the language defaults** - if the linter already enforces it,
   do not restate it

**What does not belong in it:** generic programming advice, restatements of what the linter or type
checker already enforces, long changelogs, architecture essays, or anything that changes weekly.
Those go in a reference file that loads on demand (see section 2).

```markdown
# AGENTS.md - Atlas Platform

TypeScript API and web client. Server components by default; client components only for
interactivity. Data access is through the ORM layer in src/server/db - never raw SQL.

## Commands
- Install:    pnpm install --frozen-lockfile
- Typecheck:  pnpm type-check
- Unit tests: pnpm test:unit -- --run
- E2E tests:  pnpm test:e2e
- Lint+fix:   pnpm lint --fix
- Runtime:    node --version   # expect the version pinned in .nvmrc

## Style rules that differ from defaults
- No `any`. Use `unknown` and narrow with a type guard.
- Every API input is validated with a schema at the boundary. No exceptions.
- Business logic lives in src/server/services. API route handlers stay thin.
- Errors: Result<T> inside services, typed API errors at the boundary. Never throw across it.

## Before you claim you are done
Run typecheck and unit tests. A change that has not been run is not a change.
```

That is the whole file. Roughly thirty lines. Resist the urge to grow it.

### Precedence

Precedence is **nearest-file-wins**: the `AGENTS.md` closest to the file being edited governs, and
an explicit instruction in the prompt overrides everything.

| Priority | Surface | Scope | When it changes |
|----------|---------|-------|-----------------|
| 1 (highest) | Explicit instruction in the current prompt | This turn only | Constantly |
| 2 | `AGENTS.md` in the edited file's own directory | That subtree | When that module's rules change |
| 3 | `AGENTS.md` in a parent directory | That subtree | Occasionally |
| 4 | `AGENTS.md` / `CLAUDE.md` at the repository root | Whole repo | Rarely |
| 5 | User-level context file outside the repo | Every project for that person | Rarely |
| 6 (lowest) | Model defaults | Everything | Not yours to control |

Claude Code reads both `AGENTS.md` and `CLAUDE.md` and merges them, along with files found in
subdirectories. If you keep both, keep the shared, cross-tool rules in `AGENTS.md` and reserve
`CLAUDE.md` for the parts that are genuinely specific to one tool. Duplicating rules across both is
how they drift apart and start contradicting each other.

**Use nested files where the rules genuinely differ.** A generated client directory, an
infrastructure directory, and a legacy module each earn their own short file:

```
repo/
  AGENTS.md                      <- overview, commands, repo-wide style
  packages/api/AGENTS.md         <- API-specific: schema-first, migration rules
  packages/web/AGENTS.md         <- UI-specific: component conventions, a11y floor
  packages/legacy/AGENTS.md      <- "different rules apply here, and here is why"
  infra/AGENTS.md                <- plan-then-apply, never apply without approval
```

A nested file that only repeats the root file adds noise and costs context. Write one when the
answer to "what is different here?" is non-empty.

---

## 2. Progressive Disclosure

The old model was one big context file holding everything anyone might need. Every token in it is
paid for on every request, whether relevant or not, and a large file dilutes the rules that matter.

**Agent Skills** replaced that pattern. A skill is a Markdown file with YAML frontmatter, and the
platform loads it in tiers:

| Tier | What loads | When |
|------|-----------|------|
| 1 | The skill's `name` and `description` only, roughly eighty tokens each | At session start, for every installed skill |
| 2 | The full instruction body | Only when the model judges the skill relevant to the task at hand |
| 3 | Supporting scripts, reference documents, templates, schemas | Only during execution, only if the instructions call for them |

That tiering is the whole idea: you get equivalent extensibility at a small fraction of the standing
context cost. Twenty skills cost about what one paragraph costs until one of them is actually
needed. Skills were released by Anthropic in December 2025 and were adopted by OpenAI, Google,
GitHub, and Cursor within weeks, so authoring them is not a bet on one vendor.

```markdown
---
name: database-migration
description: Use when adding, altering, or dropping a database table or column. Covers the
  migration file convention, the required backfill plan, and the review gate for destructive
  changes.
---

# Database Migration

1. Never edit the schema file directly - generate a migration.
2. Every migration needs a tested down path, or an explicit written note that it is one-way.
3. Destructive changes (drop column, drop table, narrow a type) require a separate PR from the
   code that stops using the column, and they ship at least one release apart.
4. Backfills run as a separate job, never inside the migration transaction.

See reference/backfill-runbook.md for the batching and progress-tracking pattern.
See scripts/check-migration.sh for the automated pre-flight check.
```

### Deciding where a piece of knowledge goes

| Kind of knowledge | Goes in | Why |
|-------------------|---------|-----|
| Build and test commands | `AGENTS.md` (always loaded) | Needed for essentially every task |
| Repo-wide non-obvious style rules | `AGENTS.md` (always loaded) | Cheap, and wrong on every task if missing |
| A multi-step procedure used occasionally | A skill (loaded on relevance) | Costs a description until it is needed |
| Domain rules for one subsystem | Nested `AGENTS.md` in that subtree | Scoped by location, free elsewhere |
| API schemas, long tables, config references | A reference file, linked from a skill (tier 3) | Large, and only relevant mid-task |
| Executable checks and generators | A script, invoked by a skill (tier 3) | Deterministic beats described |
| Volatile task state | The prompt, or a planning document | Would be stale by the next session |

**The anti-pattern:** a single context file that keeps growing because every incident adds a
paragraph. It is always loaded, it dilutes attention, it goes stale in the middle where nobody
reads, and its cost scales with the team's history rather than with the task. When a context file
grows past what someone will read in one sitting, split it: keep the always-true parts, move
procedures to skills, move detail to reference files.

---

## 3. MCP Servers This Repo Expects

The Model Context Protocol is how agents reach tools and live data. Anthropic donated it to the
Agentic AI Foundation under the Linux Foundation in December 2025, with OpenAI and Block as
co-founders and AWS, Google, Microsoft, Cloudflare, and Bloomberg among the platinum members. There
are more than ten thousand active public servers. It is infrastructure now, not an experiment.

Because a server is a capability grant, document them here in the repository. An agent that can
reach your production database is a very different agent from one that cannot, and that difference
should be reviewable in a pull request.

| Server | What it is for | Access | Credential | Notes |
|--------|----------------|--------|------------|-------|
| Repository host | Read issues and pull requests, post review comments | Read + comment | Scoped token, no admin, no force-push | Never grant merge or release scopes |
| Issue tracker | Read ticket context and acceptance criteria | Read-only | Service account, project-scoped | Writes stay manual |
| Database (dev) | Inspect the schema, run read queries against seeded data | Read-only | Dev credentials, dev database only | Never point this at production |
| Documentation search | Look up internal architecture decisions and runbooks | Read-only | Internal auth | Reduces the guessing that produces plausible-wrong code |
| Browser automation | Drive the app to verify a UI change | Local sandbox | None | Local only; egress allowlisted |
| Observability | Read logs, traces, and metrics for a bug investigation | Read-only | Scoped to this service | Read-only is the whole point |

**Rules for adding one:**

- **Write down what it is for.** A server nobody can justify is a standing risk with no owner.
- **Read-only by default.** Promote to write only with a named owner and a stated reason.
- **Least privilege on the credential.** Scope to the project, the environment, and the operation.
  A convenient broad token becomes the incident.
- **Never point an agent-reachable server at production data** without an explicit, separate,
  audited decision. Development and staging are the default.
- **Pin the server version and review it like a dependency**, because that is what it is. The
  protocol continues to evolve - a spec revision dated 2026-07-28 adds a stateless protocol core,
  an Extensions framework, Tasks, MCP Apps, hardened authorization, and a formal deprecation
  policy - so track the version you run and read the deprecation notes before upgrading.
- **Log tool invocations** the way you log API calls. See section 6.

---

## 4. Hooks - Deterministic Enforcement

The single most useful distinction in this file:

- A rule in a context file is **advisory**. The model usually follows it. Usually is not a control.
- A **hook executes regardless**. It does not depend on the model noticing, remembering, or
  agreeing.

So the test for where a rule belongs is: **would I accept this being skipped occasionally?** If yes,
a context file is fine. If no, it is a hook.

| Requirement | Where it belongs | Why |
|-------------|------------------|-----|
| Format code before commit | Hook | Mechanical, zero judgement, must never be skipped |
| Run typecheck before declaring a task done | Hook | The most common failure is a confident, uncompiled claim |
| Block edits to generated files, lockfiles, `dist/` | Hook | Advisory rules here fail eventually and silently |
| Block secrets from being written to disk or logs | Hook | Never negotiable |
| Refuse network calls to hosts outside the allowlist | Hook | A model cannot enforce your egress policy |
| Require approval before an infrastructure apply | Hook | Irreversible actions need a gate, not a preference |
| Append an audit event for every tool invocation | Hook | Must not depend on the agent choosing to log |
| Prefer composition over inheritance | Context file | Judgement, not enforcement |
| Match the naming conventions in this module | Context file | Judgement, with reasonable exceptions |

Keep the hook itself boring: fast, deterministic, non-interactive, and returning a clear message the
agent can act on. A hook that prompts for input will hang an unattended run, and a slow hook gets
disabled.

```bash
#!/usr/bin/env bash
# hooks/pre-edit-guard.sh - refuse writes to paths the agent must not touch.
# Receives the target path on stdin or as an argument, depending on your runtime.
set -euo pipefail

target="${TARGET_PATH:-}"
[ -n "${target}" ] || exit 0

case "${target}" in
  dist/*|build/*|*.generated.ts|*/node_modules/*|pnpm-lock.yaml)
    echo "BLOCKED: ${target} is generated or vendored. Change the source, then regenerate." >&2
    exit 1
    ;;
esac

exit 0
```

Document the hooks in this file as well as configuring them. A contributor who hits a blocked write
should be able to find out why without reading the hook source.

---

## 5. Subagents and Parallel Work

Two independent reasons to run subagents:

1. **Genuinely parallel work.** Three unrelated modules can be worked simultaneously.
2. **Context hygiene.** A single long conversation accumulates context from six unrelated tasks, and
   every later turn pays for all of it while relevance decays. A subagent starts clean, does one
   task, and returns a conclusion instead of the entire transcript.

The second reason applies even when there is nothing to parallelize, and it is the one teams miss.

**The hard constraint: split by file or module so that no two agents ever edit the same file.**
Concurrent edits to one file produce lost writes and merge damage that no test necessarily catches.
Any of these work:

| Split | Fits | Watch for |
|-------|------|-----------|
| By directory or package | Monorepos with clear boundaries | Shared config at the root - assign it to exactly one agent |
| By layer (schema, service, API, UI) | Vertical features | Serialize: schema first, then the layers above it |
| By file type (implementation vs tests) | Adding coverage | The test author needs the final signatures - run it after |
| Read-only investigation, many at once | Research and audits | Free of conflicts by definition - fan out widely here |
| Isolated worktrees, one per agent | Refactors that must touch overlapping paths | Merge cost moves to the end; keep the branches short-lived |

Serial work stays serial. Spawning an agent for a step that depends on the previous step's output
adds latency and a context handoff, and buys nothing.

**Briefing a subagent well:**

- State the file or directory boundary explicitly, and say that everything else is read-only.
- Give the verification command it must run before reporting success.
- Ask for a conclusion, not a transcript. The value of a subagent is the summary.
- Say what it must not do - commit, push, edit generated output, change configuration.

---

## 6. Verification, Review, and Governance

### The governance shift

When agents only produced code that a human then merged, the unit of risk was a **diff**, and diff
review was a sufficient control. Now that agents **act** - running commands, calling tools, touching
infrastructure - the controls have to sit around the **action**, because by the time you see the
diff, other things have already happened.

Controls that hold up in production use:

| Control | What it addresses |
|---------|-------------------|
| Remote development machines | Blast radius. The agent does not run on a laptop with everyone's credentials. |
| Egress allowlists | Exfiltration and dependency confusion. The agent reaches only what it needs. |
| Specialized review agents | Depth. A narrow reviewer beats a general "look for problems" pass. |
| Deterministic scanners | Reliability. Secret scanning, SAST, dependency audit, license checks do not have off days. |
| Risk-tiered human approval | Attention budget. Reserve human review for changes that warrant it. |
| Audit events shipped to a SIEM | Reconstruction. Every tool invocation, with actor, target, and outcome. |

### The self-review anti-pattern

**One agent that writes, reviews, fixes, and approves its own change is not a review.** It inherits
every blind spot in the plan that produced the code - if the plan misunderstood the requirement, the
review confirms the misunderstanding. Worse, a single agent holding both roles can silently revise
the code in response to its own finding, so nobody ever sees that the finding existed.

The pattern that works:

- **Independent reviewers**, launched fresh, without the authoring conversation's context. Not
  knowing why the author did it is a feature.
- **Narrowly scoped.** A security reviewer, a data-access reviewer, a test-coverage reviewer. "Look
  for problems" is not a scope.
- **Immutable findings.** A reviewer emits findings. It does not edit code. The findings are
  recorded before any fix is applied, so the record survives the fix.
- **Fixes are a separate, reviewable step**, applied against a written finding by a different actor.

```mermaid
flowchart TD
    A[Spec] --> B[Implement]
    B --> C[Deterministic scanners]
    C --> D[Independent reviewers]
    D --> E[Findings recorded, immutable]
    E --> F{Risk tier}
    F -- Low --> G[Auto-merge on green]
    F -- Medium --> H[One human reviewer]
    F -- High --> I[Independent review agent plus two human reviewers]
    G --> J[Fixes applied as a separate reviewable step]
    H --> J
    I --> J
```

### Risk tiers

| Tier | Examples | Gate |
|------|----------|------|
| Low | Docs, comments, test additions, formatting, dependency patch bumps | Automated checks green, auto-merge |
| Medium | Feature code inside existing patterns, refactors with unchanged behavior | Automated checks plus one human reviewer |
| High | Auth, permissions, billing, migrations, cryptography, public API contracts, infrastructure, CI configuration, anything touching production data | Automated checks, independent review agent, and two human reviewers - never auto-merged |

Tier by **blast radius**, not by diff size. A one-line change to a permission check is high risk. A
thousand-line generated-fixture update is low.

### Spec-driven development

For anything past trivial, write the spec first and treat the code as regenerable output. It makes
the disagreement happen while it is cheap, and it gives every reviewer the same reference.

A good spec defines exactly six things:

1. **Outcomes** - what is true when this is done, in observable terms
2. **Scope boundaries** - what is explicitly not included
3. **Constraints** - performance, compatibility, security, regulatory limits
4. **Prior decisions** - what was already settled, and why, so it is not relitigated
5. **Task breakdown** - ordered, each independently verifiable
6. **Verification criteria** - the exact commands and conditions that prove each task is done

If the spec cannot state verification criteria, the task is not understood well enough to delegate.
That is a finding, not a blocker to work around.

---

## 7. Working With Capability Tiers

Do not write model names into a context file. Write the **shape of the decision**, so it stays true
after the models change.

| Situation | Tier to reach for |
|-----------|-------------------|
| Mechanical edits, renames, formatting, straightforward test scaffolding | Fast tier - the work is pattern-shaped and volume matters |
| Search, triage, classification, "where does this live" | Fast tier - and run several in parallel |
| Architecture, subtle concurrency, security-relevant logic, ambiguous requirements | Reasoning tier - the cost difference is trivial against one production incident |
| A bug that has already survived one attempt | Reasoning tier, plus more context - the first failure means the context was wrong |
| Long autonomous runs that must not go off the rails | Reasoning tier, plus tighter hooks and smaller task scope |

### Large repositories

Bigger context windows did not make context selection irrelevant - they raised the ceiling on how
much irrelevant material you can accidentally load. Precision still wins.

- Point at exact paths. "Update the user service" invites a search; `src/server/services/user.ts`
  does not.
- Prefer an interface or a schema over the whole implementation when the caller only needs the
  contract.
- Give one worked example of the pattern instead of describing it in prose. One real router beats
  three paragraphs about routers.
- Let the agent search rather than pre-loading everything - search results are self-selecting.

### Widen or narrow

**Widen** when the agent is guessing at a convention that exists somewhere, when a change touches
several layers, when it needs the real schema, or when the first attempt failed in a way that
suggests it never saw the relevant file.

**Narrow** when responses drift toward generic code, when the agent contradicts an earlier
instruction (a sign the instruction is buried), when it edits files outside the task, or when a
long session starts making mistakes it was not making an hour earlier. That last one usually calls
for a fresh session with a written handoff rather than more context.

---

## 8. Context Patterns

These are patterns for **what the agent knows**, not for phrasing. Phrasing has diminishing returns;
context does not.

### Pattern: the worked example beats the description

Put a real, current file from your codebase in front of the agent and ask for another like it. This
belongs in a skill or a reference file, not in an always-loaded context file.

```markdown
This is how a router is defined in this project: src/server/routers/document.ts

Create src/server/routers/notification.ts following that structure exactly.
Fields: id, userId, message, type (info | warning | error), read, createdAt.
Procedures: list (paginated), markAsRead, markAllAsRead, getUnreadCount.
```

### Pattern: constrain by exclusion

What not to do is usually more informative than what to do, because it encodes the decisions your
team already made and the alternatives it already rejected.

```markdown
Add file upload to the document editor.

- Do NOT add an upload middleware - uploads go through presigned URLs in storage.service.ts
- Do NOT add a new HTTP route - add a procedure to the existing document router
- Do NOT store file bytes in the database - store the object key only
- Follow the error handling in document.service.ts
```

Recurring exclusions belong in `AGENTS.md`. One-off exclusions belong in the prompt.

### Pattern: state the verification, not just the goal

Every delegated task carries the command that proves it worked. Without one, "done" means "the
agent stopped."

```markdown
Task: fix the stale sidebar after a profile update.
Done when: pnpm test:e2e -- profile-update.spec.ts passes, and pnpm type-check is clean.
Do not modify the test to make it pass. If the test is wrong, say so and stop.
```

### Pattern: structure the investigation

```markdown
Bug: users see stale data after updating their profile.

Symptoms: name change saves, settings page updates, sidebar keeps the old value until a hard
reload. Sidebar only.
Relevant paths: the settings page, the sidebar component, the profile query.
Hypothesis: the sidebar reads cached server-rendered data and nothing invalidates it after the
mutation.
Confirm or refute the hypothesis before proposing a fix. If it is wrong, say what is actually
happening.
```

The last line matters. Without it, an agent will often implement against a wrong hypothesis rather
than contradict it.

---

## 9. Measuring Effectiveness

Measure the **agent loop**, not developer sentiment. Sentiment tracks novelty and then decays.

| Metric | Definition | Reading it |
|--------|-----------|------------|
| First-pass acceptance rate | Share of agent changes merged with no human code edits | Falling means the context is stale or the tasks are too large |
| Human edit distance | Lines changed by a human after the agent, over lines the agent produced | High with high acceptance means it is close but consistently off on one convention - write that convention down |
| Review findings per change | Findings from independent reviewers, per merged change, by severity | Rising security findings mean the guardrails need to be hooks, not prose |
| Time to verified | From task start to all verification criteria passing | The honest cycle-time number, because it includes the rework |
| Rework rate | Share of agent changes needing a follow-up fix within a week | The metric that catches "fast but wrong" |
| Escaped defects | Defects found in production that originated in an agent change | The one that actually matters - track it even at low volume |
| Context hit rate | Share of sessions where the agent asked for something the context files should have supplied | Directly names the gaps to fill |

Two failure modes these are designed to catch:

- **Fast and wrong.** High throughput with rising rework and escaped defects. The loop is producing
  volume, not value.
- **Slow and safe.** Perfect acceptance and near-zero findings, but time to verified is no better
  than doing it by hand. Usually over-scoped review on low-risk changes - fix the risk tiers.

### Maintenance

```markdown
## Monthly review
- [ ] Are AGENTS.md commands still the commands? Run each one.
- [ ] Delete rules the linter, type checker, or a hook now enforces.
- [ ] Any rule repeated in three prompts this month? It belongs in a context file.
- [ ] Any context file rule that keeps getting skipped? It belongs in a hook.
- [ ] Review MCP server access: still needed, still least-privilege, still not production.
- [ ] Skills: are descriptions accurate enough that the right one loads?
- [ ] Prune nested AGENTS.md files that no longer say anything different.
- [ ] Check the metrics above for a trend, not a number.
```

The single strongest signal that this file is working: **new contributors, human or agent, produce
correct code on the first attempt without asking.** If they keep asking the same question, the
answer belongs here.
