A worked exemplar of the scaffolding that makes a large codebase legible to AI coding agents — so they stop writing generic code and start writing code that fits your project.
Everything here is built around a fictional app. Copy the structure, not the content.
📄 Why this works, and what it cost: I Stopped Prompting Better and Started Writing Things Down also on Medium
The essay makes the argument. This repo is the argument as files.
AGENTS.md the conventions file — start here
CLAUDE.md one-line pointer, so every tool reads one source
.github/skills/auth-flow/SKILL.md an exemplar flow skill
.claude/skills symlink → ../.github/skills
docs/ the essay, served via GitHub Pages
experiments/ a controlled test of whether any of this works
AGENTS.md — architecture, layer boundaries as a table, house rules stated as
absolutes with the reason attached, the design-handoff rules, and the skill routing table.
Copilot and Codex read this file natively.
CLAUDE.md — a single @AGENTS.md import line. One file, three tools, no drift.
Ten minutes of work and the highest-value item on the list.
.github/skills/auth-flow/SKILL.md — what a flow skill
contains when a control path crosses six directories and no single file shows it: the verified
path, the invariants that live between files, and the traps that cost somebody a day.
.claude/skills — a committed symlink, so Claude Code and Copilot read one set of files.
experiments/ — ten runs measuring whether an AGENTS.md changes what an agent
writes, on a freshly generated Ignite app. It came out null; the writeup says so and explains what
that does and doesn't imply.
-
Write
AGENTS.md. Architecture, layer boundaries, house rules with reasons — a rule with a hedge in it gets interpreted; a rule with a reason attached generalises to the cases you didn't enumerate. -
Add
CLAUDE.mdcontaining a single pointer at it. -
Create the symlink so one set of skills serves both tools:
mkdir -p .claude ln -s ../.github/skills .claude/skills
-
After your next genuinely hard debugging session, write that flow up as a skill — not before. Every good one is the residue of a week you don't want to spend twice; written speculatively they come out as documentation-shaped filler. And not every module earns one.
-
Add a routing row for it, phrased in the language a ticket actually uses.
-
Fix the skill in the same PR where you find it stale.
Step 5. Writing skills is the easy half; getting them loaded before the agent starts exploring is the half that matters, and it does not happen on its own. A skill nobody loads is a diary.
The table in AGENTS.md maps symptoms to skills:
| If the work touches… | Load |
|---|---|
| "logged out randomly", "stuck on splash", "401 loop", "filters leaked from the previous user" | auth-flow |
Plus one line that does a lot of work: the match does not have to be exact, and a skill costs little to consult.
experiments/ is a small controlled test of the claim this repo makes, run against
a freshly generated Ignite app: ten runs, two arms
identical except for one line telling the agent an AGENTS.md exists, mechanical grep scoring.
The conventions file changed essentially nothing. Both models hit ~100% convention adherence with no file at all — reusing the right components, the import alias, the theming helpers, the storage idiom. A weaker model with no file matched a stronger one with it.
The reason is the useful part: a clean Ignite app is ~20 demo screens of near-perfect exemplars, and an agent that can copy a good neighbour doesn't need prose telling it what the neighbour already demonstrates. So:
- Where exemplars are uniform and good, the code is the conventions file. Writing a layer table for a fresh boilerplate restates what the files already show.
- The file earns its place on what isn't in the code — that a generator exists, the verification commands, where new things go, scope discipline, and the product context no boilerplate supplies.
- Value scales with drift, not size alone. "Copy the neighbour" holds until several neighbours disagree. That's the case this scaffolding was built for — and the case the experiment does not test.
One finding worth carrying into your own file: the arm that read AGENTS.md produced the larger
diff in four of five pairs, despite the file saying "prefer the smallest possible diff." An agent
tries to satisfy everything it reads. Keep the file short and the scope rules sharp.
This is one developer's experience. The ROI isn't something anyone can prove with a controlled comparison, and none of it is an argument for using AI without guardrails — the speed is real only because the scaffolding is. The essay covers that at length.
Prose and examples: CC BY 4.0. Use them, adapt them, no need to ask.