Europe/Paris
Posts

AGENTS.md and skills are team code: a line budget, a review and a CI check

October 26, 2026 · 6 min de lecture
An AGENTS.md is re-read by the agent on every turn of the conversation, and every line is paid for on every turn. When several devs on a team add their own instructions with no rules, the file grows, contradicts itself and ends up ignored: here's how to treat it like code, with a budget, a review and a CI check. On OCTO Talks, Claude Camus spent ten weeks measuring how much his coding agents consumed (AI assistants that read the repository, run commands and edit files). His phrasing sums up the problem: context is "an active cache billed on every turn, not free memory". The rules file, the skills (packages of instructions the agent loads for a specific task) and everything read at startup are sent back to the model on each exchange. His numbers give useful orders of magnitude. He capped his CLAUDE.md at 150 lines, because buried rules get ignored. A diagnostic found 34 skills that were never invoked. His orchestration skill went from 1,130 to 334 lines by moving the details into seven reference files loaded on demand, and a test now prevents it from going back over 500 lines. He stays honest about the outcome: the 55% drop in median billable tokens per launched agent also coincides with a change in the kind of tasks, so it isn't proof of causation. The Claude Code documentation points the same way: aim for under 200 lines per instruction file, because a long file consumes context and reduces how reliably the instructions are followed. AGENTS.md is an open format for coding-agent instructions, picked up by several tools. Claude Code reads it directly (since v2.1.277) when the project has no CLAUDE.md; otherwise, an @AGENTS.md line in the CLAUDE.md imports it. One file serves the whole team, whatever tool each person uses. The content has to be verifiable. "Test your changes" is useless; the exact command is not:
# Billing API: Symfony + Vue 3 front end

## Commands
- PHP tests: `vendor/bin/phpunit`
- Static analysis: `vendor/bin/phpstan analyse`
- Front end: `npm run lint` then `npm run test:unit`
- A task is only done when `make ci` passes

## Layout
- src/Domain: no dependency on Doctrine or Symfony
- src/Infrastructure: Doctrine adapters and HTTP clients
- assets/: Vue 3, Composition API, strict TypeScript
- Versions: see composer.json and package.json

## Rules
- `declare(strict_types=1);` in every new PHP file
- No EntityManagerInterface in a controller: go through a handler
- Never edit a Doctrine migration that is already merged

## Before creating an endpoint
- Read docs/agents/api.md (naming, HTTP codes, error format)
Two choices are deliberate. The PHP and Symfony versions aren't copied in: they live in composer.json, which doesn't lie. And the last section points to a file instead of importing it. An @docs/agents/api.md import would load at launch, in every session: the Claude Code docs state that imports organize a long file without reducing its cost. A plain mention lets the agent read the file only when it needs it. Front-end conventions have no business sitting in context while the agent fixes a migration. Claude Code supports path-scoped rules in .claude/rules/, loaded only when the agent reads or edits a matching file:
---
paths:
  - "assets/**/*.{ts,vue}"
---

# Vue front end
- <script setup lang="ts"> and Composition API only
- HTTP calls go through the composables in assets/api/, never fetch inside a component
- A component over 200 lines gets split
Without Claude-specific files, a nested assets/AGENTS.md plays a similar role: Claude Code loads it when it opens a file in that folder, as long as that folder has no CLAUDE.md. The Agent Skills specification defines three levels. At startup, the agent only loads each skill's name and description, about 100 tokens. The body of SKILL.md loads only when the skill is activated, and the spec recommends keeping it under 500 lines. Files in references/, scripts/ and assets/ are read only on demand.
.claude/skills/symfony-endpoint/
├── SKILL.md
└── references/
    └── conventions.md
---
name: symfony-endpoint
description: Creates a Symfony REST endpoint (DTO, handler, controller, functional test) following the project conventions. Use it for any new API route.
---

# New endpoint

1. Read references/conventions.md before writing any code.
2. Input DTO with Validator component constraints.
3. Handler in src/Application, with no HttpFoundation dependency.
4. Functional test with WebTestCase.
5. Run vendor/bin/phpunit --filter on the new test, then vendor/bin/phpstan analyse.
The description is the line that matters most: it decides whether the agent loads the skill. The spec also requires name to be lowercase with hyphens, and identical to the folder name. On YoanDev, Yoan Bernabeu describes a training where the team built a skill together to bootstrap a Symfony project to its standards, then tested results with and without it across several cases. It will be distributed through the team's skill marketplace, versioned and reviewed like code. For skills shared across a whole organization, he released Skillcrew, a CLI that syncs a Git repository of skills to the various agents. The tool is at v0.1.0, and Yoan Bernabeu himself notes that project-specific skills stay in the project's repository. A limit written in a wiki doesn't hold. A short script does (save it as bin/check-agent-files.sh):
Bash
#!/usr/bin/env bash
set -euo pipefail

max_agents=150
max_skill=500
status=0

if [[ -f AGENTS.md ]]; then
  lines=$(wc -l < AGENTS.md)
  if (( lines > max_agents )); then
    echo "AGENTS.md: $lines lines (max $max_agents)"
    status=1
  fi
fi

while IFS= read -r -d '' skill; do
  dir=$(basename "$(dirname "$skill")")
  lines=$(wc -l < "$skill")
  name=$(sed -n 's/^name: *//p' "$skill" | head -n 1)

  if (( lines > max_skill )); then
    echo "$skill: $lines lines (max $max_skill)"
    status=1
  fi
  if [[ "$name" != "$dir" ]]; then
    echo "$skill: name '$name' does not match folder '$dir'"
    status=1
  fi
  if ! grep -q '^description: .' "$skill"; then
    echo "$skill: missing description"
    status=1
  fi
done < <(find .agents/skills .claude/skills -name SKILL.md -print0 2>/dev/null)

exit "$status"
Yaml
name: agent-files

on:
  pull_request:
    paths:
      - "AGENTS.md"
      - "CLAUDE.md"
      - ".claude/**"
      - ".agents/**"
      - "bin/check-agent-files.sh"

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - run: bash bin/check-agent-files.sh
The 150-line threshold is the one Claude Camus uses, below the 200 Anthropic recommends. Add AGENTS.md, CLAUDE.md, .claude/ and .agents/ to your CODEOWNERS file: a change to the agent's context changes the whole team's behavior, so it deserves the same review as a CI config change. Picking a script over an agent reviewer isn't just a matter of taste. Claude Camus compared both on the same check: 11 to 39 seconds and zero tokens for the script, about 6 minutes, 63,000 tokens per pass and 8 false positives out of 44 verdicts for the agent. This guardrail echoes the one in the post on CI as a definition of done: what the CI doesn't check eventually drifts.
  • Believing a written rule is applied. The Claude Code docs say it: these files are context, not enforced configuration. Anything that must be blocked goes through PHPStan, the CI or a PreToolUse hook (a command Claude Code runs before each tool call, which can block it), not one more sentence.
  • Copying numbers by hand. Claude Camus found that file sizes noted by hand were off by 9 to 36% the very next day. Versions, counters, sizes: point to the source or generate them with a script.
  • Piling up without ever removing. Each dev adds an instruction after an incident, nobody deletes one. Claude Camus's diagnostic found 34 skills never invoked: schedule the cleanup instead of waiting for it.
When two instructions contradict each other, the Claude Code docs warn that the agent may pick one arbitrarily; a cleanup pass at each retro costs less than a week of inconsistent PRs.
On this page
Book a call