Agent docs as a navigation index
AGENTS.md is the primary file Claude and other agentic coding AI assistants read upon session initialization. BoringStack ships it as a highly focused navigation index, preventing context bloat and keeping agent edits isolated.
It avoids the trap of the giant thousand-line document that fights agents the same way a massive file fights humans: too much context to scan, too much irrelevant detail next to the bit you need, and rapid drift. Instead, it is a navigation index pointing to focused topic guides under docs/agents/.
AGENTS.md is one table. One row per topic. One link per row. Nothing
else.
# Patterns: index
`bun run check` is the oracle. If anything below disagrees with what`check` says, the lint config wins. Flag the drift.
## Deep dives
| When you're doing this | Read this || ---------------------------------------------- | --------------------------------- || Touching schema, writing service queries | [drizzle](docs/agents/drizzle.md) || Throwing in a service; wrapping a caught error | [errors](docs/agents/errors.md) || Writing a `logger.*` call; PII masking | [logging](docs/agents/logging.md) || Adding a BullMQ job; touching `src/queues/` | [queues](docs/agents/queues.md) || Writing tests, fixing failures | [testing](docs/agents/testing.md) |Each docs/agents/<topic>.md is single-concern: the one rule, the one
idiom, the one example, the one anti-pattern. No “context” section, no
glossary, no overview prose. The reader is already in the task; the
file is the answer.
Why a table, not paragraphs
Section titled “Why a table, not paragraphs”A paragraph pointer (“Touching Drizzle? Read docs/agents/drizzle.md, which covers schema conventions, the db.transaction(tx) pattern, raw-SQL
bans…”) looks helpful and reads as noise. Every paragraph below the
fold pushes the rule the agent actually needs further away.
A two-column table reduces each entry to trigger → file. The agent scans, picks one, loads it. The index never grows beyond a screen, no matter how many topics live underneath.
Why split files at all
Section titled “Why split files at all”Splitting enforces a useful discipline: each guide answers one question. If a guide grows past a screen, the topic was actually two topics. Split it again.
- Context Efficiency. A single 500-line file injects 500 lines of context into every agent turn. Splitting keeps tokens clean and focused.
- No Rotten Sections. Single-file documentation rots unevenly. Isolating topics prevents mixed-trust prose from polluting accurate guidelines.
- Zero Git Collisions. Multiple agents modifying different parts of one file will hit git merge conflicts. Per-topic files isolate your changes cleanly.
Naming the file by the trigger, not the noun
Section titled “Naming the file by the trigger, not the noun”The “When you’re doing this” column matters more than the topic name. Agents arrive with a task (“I need to add a webhook handler”), not a topic (“I want to learn billing”). The triggers in the table match the verbs an agent would write in their own plan.
This is why the table works as a navigation index but a sidebar of topic names doesn’t: a sidebar lists nouns, the agent needs verbs.
Where the rules actually live
Section titled “Where the rules actually live”Even the focused guides don’t enforce anything. The lint config is the contract. See Lint as the contract. The docs explain the why; the plugins block the what.
So AGENTS.md answers “which page is the why?” and nothing else.
Where to look in this repo
Section titled “Where to look in this repo”apps/api/AGENTS.md: 30 lines, one table, points at 16 guides underapps/api/docs/agents/.apps/ui/AGENTS.md: same shape, 16 guides underapps/ui/docs/agents/.
If you fork BoringStack and add a new pattern, the cost is one row in
the table plus one new file. Never edit prose-heavy sections in
AGENTS.md; there are none to edit.