Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
44bcebc
Unify text truncation behind a single Truncate primitive (#3660)
tom2drum Aug 25, 2026
d421410
Add a Slack reply watcher to the product-task workflow (#3662)
tom2drum Aug 25, 2026
d435795
update hooks for new claude worktree creation
tom2drum Aug 26, 2026
ce266aa
fixes for "resolve config request" skill
tom2drum Aug 27, 2026
cc81b4c
update comments rule
tom2drum Aug 27, 2026
6fabbb0
Surface batch-transaction recipients in the tx details To & Value fie…
tom2drum Aug 27, 2026
b02ec7f
Silence dotenv injection banner in vitest setup
tom2drum Aug 27, 2026
108298e
update unit test command list
tom2drum Aug 27, 2026
1089d07
Update styles for "In/Out/Self" badges (#3672)
tom2drum Aug 28, 2026
c69e275
Main page stats failover improvements (#3673)
tom2drum Aug 31, 2026
3c86fac
update comments guid
tom2drum Aug 31, 2026
65c8004
Add a cognitive-complexity and CRAP-score CI gate (#3670)
tom2drum Aug 31, 2026
128c7e2
Reserve the .tsx extension for files that contain JSX (#3676)
tom2drum Aug 31, 2026
90786ea
Fixes for Release v2.11.0 (#3669)
tom2drum Sep 1, 2026
468d8cc
Restructure the code-complexity tool into pipeline stages (#3677)
tom2drum Sep 1, 2026
0a247b8
Scope every interchain request to the deployment's slice of the share…
tom2drum Sep 7, 2026
35ffd9f
add docs rule
tom2drum Sep 7, 2026
6f32b11
add adr template
tom2drum Sep 7, 2026
e4eb31b
Measure test assertion strength with mutation testing (#3678)
tom2drum Sep 7, 2026
80cb1ea
Let operators pick Flashblocks or Subblocks naming for the OP Stack f…
pcw109550 Sep 7, 2026
b706c9e
Add Alexey to the Core API roster
tom2drum Sep 7, 2026
76fe517
update release version in docs
tom2drum Sep 7, 2026
fb9e7b5
Rework the review skills: explicit inputs, a task-folder output mode,…
tom2drum Sep 8, 2026
a7e677e
Let operators pick which color themes an explorer offers (#3691)
tom2drum Sep 8, 2026
f3a8ade
Point Hardhat and Foundry verification instructions at the Pro API on…
tom2drum Sep 9, 2026
d5297d0
remove mutation testing from code quality rules
tom2drum Sep 9, 2026
419b82e
update "add env var" skill
tom2drum Sep 9, 2026
cfd773e
Rewrite the tools/ scripts in TypeScript so they are type-checked, te…
tom2drum Sep 9, 2026
a7d5ce1
Let operators set the label on the DeFi dropdown button (#3695)
tom2drum Sep 10, 2026
bf60f9e
Enforce the app-config convention with ESLint rules (#3699)
tom2drum Sep 10, 2026
4806caf
fix skeleton loading state when text leaked due to color prop override
tom2drum Sep 11, 2026
3f0ee77
Add Playwright MCP server for agent browser checks
tom2drum Sep 11, 2026
9e2d3b5
add .worktreeinclude file
tom2drum Sep 14, 2026
ceb0ede
Take cross-chain list totals and the 24h widget from the stats servic…
tom2drum Sep 14, 2026
116d8fd
Add a configurable action button to ERC-20 token pages (#3692)
tom2drum Sep 14, 2026
28deac1
[skip ci] clean .env.extra
tom2drum Sep 14, 2026
6a9c38d
Fix page crash on token transfer rows whose token is null (#3705)
tom2drum Sep 14, 2026
a766e00
Make the URL the single source of truth for paginated lists and stop …
tom2drum Sep 15, 2026
2d57394
Select Playwright component tests with Playwright's --only-changed an…
tom2drum Sep 15, 2026
89a60c6
Move proxy.ts under src/ so Next.js runs the proxy again (#3708)
tom2drum Sep 16, 2026
19543f6
Show the transaction not-found screen on every tab, not only Details …
tom2drum Sep 16, 2026
0dfdef7
Tighten the magic-numbers rule so agents name a literal only when the…
tom2drum Sep 16, 2026
97daca7
Tell agents to extend and pack Playwright suites instead of adding a …
tom2drum Sep 16, 2026
7f50b55
Show the L2 timestamp of Super Root dispute games on the OP dispute g…
tom2drum Sep 16, 2026
2c36019
Fix the home page RPC fallback not starting when only one widget lose…
tom2drum Sep 17, 2026
2332d78
Fix address highlight getting stuck after a brief first hover (#3714)
tom2drum Sep 17, 2026
1391d57
Add a React Doctor check that blocks pull requests on new React bugs …
tom2drum Sep 17, 2026
ef8c2e6
Add token-transfer tooltips to advanced filter from/to addresses (#3701)
Sophran-fbj Sep 18, 2026
545bd02
Fix React bugs surfaced by the React Doctor backlog scan (#3716)
tom2drum Sep 18, 2026
322ce81
Stop the server-side pageview ping from logging unhandled rejections …
tom2drum Sep 18, 2026
9f0d0eb
add /ds-bundle to gitignore
tom2drum Sep 18, 2026
109ab84
Fix the white flash on reload for explorers with a default dark theme…
tom2drum Sep 18, 2026
264c7b3
lazy mount pool links popover
tom2drum Sep 21, 2026
13c0090
Upgrade Node.js to v24, pnpm to v12 and Vitest to v5 (#3721)
tom2drum Sep 21, 2026
d4c3bdd
Track header banner link clicks in Mixpanel (#3719)
ulyanas Sep 21, 2026
2883e78
Patch Stryker's Vitest runner for Vitest 5 test names
tom2drum Sep 21, 2026
02f3c54
Fix the Playwright Docker runner's pnpm activation
tom2drum Sep 21, 2026
cabe2e1
Allow Google Ad Manager hosts for Sevio ad zones in the CSP (#3725)
tom2drum Sep 22, 2026
fb31309
Replace the per-axis word cap in the agent code review with a per-fin…
tom2drum Sep 22, 2026
a2c5681
Replace mobile list views with table views across the app (#3723)
tom2drum Sep 23, 2026
0719a17
Migrate Swap to the official LI.FI widget (#3710)
maxaleks Sep 23, 2026
91eadd6
Open external marketplace dapps directly instead of embedding them (#…
tom2drum Sep 23, 2026
85c80de
Collect Slack message conventions in an on-demand agent doc (#3730)
tom2drum Sep 23, 2026
5a7e5e3
Add OP Stack transaction type labels (#3659)
nonsense Sep 28, 2026
a4a36c2
UI: Display of statistics tiles on mobile (#3735)
tom2drum Sep 28, 2026
279ea3c
Allow choosing full or icon view per multichain portfolio provider an…
tom2drum Sep 28, 2026
1c961c7
Scale ERC-8056 token amounts by the token's UI multiplier (#3700)
tom2drum Sep 28, 2026
6a8ac4e
Bump Next.js to v16.3.5 (#3739)
tom2drum Sep 28, 2026
a1295bc
Pilot a typed-judgment screen as a fourth subagent in the agent code …
tom2drum Sep 29, 2026
c71b256
Make the app action button a header element on token, NFT and transac…
tom2drum Sep 29, 2026
dec85c8
[skip ci] rollback "skip validation" env for review
tom2drum Sep 29, 2026
64b6a30
Prefer the configured default color theme over the OS preference on a…
tom2drum Sep 29, 2026
67bc406
Make flaky Playwright component tests deterministic (#3742)
tom2drum Sep 30, 2026
0abc566
Neutral default colors for selected elements, navigation and top bar …
tom2drum Sep 30, 2026
96d76d4
Support the xDai bridge and native tokens in cross-chain transactions…
tom2drum Sep 30, 2026
3a2cd33
chore: prepare release v2.12.0
tom2drum Sep 30, 2026
dae9e83
trim multiplier value in tables
tom2drum Oct 6, 2026
a25e30d
Add PostHog as an optional product analytics provider (#3755)
tom2drum Oct 6, 2026
c83b77b
Fix code editor crash when contract sources repeat a file path (#3765)
tom2drum Oct 8, 2026
b248dd0
Fix the white flash on reload in Firefox for explorers with a dark th…
tom2drum Oct 7, 2026
59e8799
Fix withdrawals counter showing the sum in wei instead of the native …
tom2drum Oct 7, 2026
8e1da88
Fix search on the bridged tokens tab when cross-chain txs are enabled…
tom2drum Oct 9, 2026
c85f3bb
WIP: Merge upstream v2.12.0 (has conflicts)
github-actions[bot] Oct 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
31 changes: 18 additions & 13 deletions .agents/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See `./rules/design-system.md` for Chakra UI v3 design system configuration and

## Code Style & Quality

See `./rules/code-quality.md` for code style, lint commands plus conventions linters don't catch.
See `./rules/code-quality.md` for code style, lint commands plus conventions linters don't catch, and the complexity/CRAP scores.

## TypeScript Conventions

Expand All @@ -27,7 +27,7 @@ See `./rules/env-vars.md` for where environment variables live, how they're deli
## Testing

- Vitest unit tests (`*.spec.ts` / `*.spec.tsx`): See `./rules/tests-unit.md` for purpose, setup, utilities, and conventions.
- Playwright component visual tests (`*.pw.tsx`): See `./rules/tests-visual.md` for purpose, setup, fixtures, and conventions.
- Playwright component visual tests (`*.pw.tsx`): See `./rules/tests-visual.md` for their purpose, when to use them, setup, fixtures, and conventions.

## Running locally

Expand All @@ -51,30 +51,34 @@ Some directories have a `CONTEXT.md` documenting non-obvious patterns specific t
- `deploy/scripts/` — how the frontend container is built and starts up (Dockerfile stages, entrypoint).
- `deploy/tools/envs-validator/` — startup validation of `NEXT_PUBLIC_*` envs against yup schemas.
- `src/api/` — how a request URL is assembled (resource registry, runtime config, `/node-api/config`) and where resource response types come from.
- `src/config/` — the app-config convention (default-only exports, one import surface, envs read only in config modules), its ESLint enforcement and exemptions.
- `src/features/connect-wallet/` — why the wallet stack is loaded lazily (off the critical path), how account state reaches boot-time consumers before a provider exists, and the connector-mode differences.
- `src/server/primedRequests/` — the early-fetch primer: why it exists, the CSP-driven determinism constraint, its correctness guarantee, and the drift-test contract.
- `src/shared/pagination/` — the paginated-list components and utils.
- `src/slices/` — slice ownership model (who owns an entity's rendering).
- `src/sprite/` — SVG sprite build pipeline and which outputs are tracked vs. generated.
- `src/toolkit/` — the `@blockscout/ui-toolkit` workspace package structure.
- `tools/code-complexity/` — the cognitive-complexity / CRAP code-quality gate: what a failure means and how to fix it.
- `tools/dev-server/` — how the dev server and demo deploy get their env vars from a running instance config.
- `tools/doc-links/` — the doc-link checker.
- `tools/mutation-testing/` — the mutation-testing gate for unit-test assertion strength.
- `tools/playwright/` — the runner for Playwright component tests.
- `tools/profiling/` — React render profiling: production profiling build and DevTools trace aggregation.
- `tools/react-doctor/` — the React Doctor PR gate: reading a failure, suppressing a finding, changing the rule allowlist.
- `tools/review-screen/` — the Jev review-screen pilot: what a suspect is, what a run must never do, where the sidecars go.

If you encounter a `CONTEXT.md` not listed here, read it too (and consider adding it to this list).
See `./rules/docs.md` before adding or editing any doc about the code — a `CONTEXT.md`, a file under
`docs/`, a module `README.md`.

## Architecture decision records
## Architecture decision records (ADRs)

Decisions with repo-wide consequences are recorded in `.agents/adr/`, named
`<0000>-<slug>.md`. Read the relevant one before changing what it decided — an ADR carries the
evidence and the trade-off, so it answers "why is it like this?" without a git archaeology session.
See `./rules/adr.md` when proposing or adding a new record.

The repo-wide records index:

- `0002-layer-shaped-ticket-leaves.md` — why a product task's tickets cut vertically while the leaves inside them run along layers.
- `0003-turbopack-for-production-builds.md` — why production builds moved back to Turbopack.

Add a new record (next free number, and a line here) whenever a decision is expensive to rediscover:
it constrains future work, was reached by measurement or an investigation worth not repeating, or
looks wrong without its context. Supersede rather than rewrite — flip the old record's `Status` to
`superseded by <n>` and leave its reasoning intact.

## Product task workflow

Product tasks (GitHub issues) are worked through a spec-driven workflow — interview, spec, agent
Expand All @@ -89,4 +93,5 @@ rules contract, the per-file symlink Cursor needs, and how references here are c
## Reaching people & channels on Slack

To reach anyone or any channel on Slack, resolve the Slack IDs from `./TEAM.md`. Draft and get approval
before sending anything — unless the skill you are running names an explicit exception.
before sending anything — unless the skill you are running names an explicit exception. Before composing
any Slack message, read `./slack-message.md` (language, addressing, links, attribution).
1 change: 1 addition & 0 deletions .agents/GLOSSARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ vars are documented in `docs/ENVS.md`. Architectural concepts like
| **Hot Contracts** | feature | Ranked list of the most recently and frequently interacted-with smart contracts on the network. |
| **Interchain Indexer** | service | Microservice that indexes cross-chain messages and token transfers across heterogeneous chains. General-purpose interop indexer, not ZetaChain-specific. Provides "Cross chain txs" feature. Distinct from **CCTX**. |
| **Interop Messages** | entity | **Deprecated** Cross-rollup messages passed between OP Stack chains using the native interoperability protocol. Distinct from **Interchain Indexer** messages. |
| **Jev** | service | TypeSafe's typed-judgment model: answers a structured question (yes/no, a choice) with a probability, never a claim, so whatever consumes a score verifies it before acting on it. |
| **Kettle** | entity | In the **SUAVE** architecture, a Kettle is a trusted execution environment (TEE) node that processes MEV bundles. SUAVE transactions are associated with a Kettle. |
| **Marketplace** | feature | Curated directory of dApps and DeFi applications integrated with Blockscout. |
| **MetaSuites** | service | Third-party browser extension that enhances the Blockscout UI with additional data and links. |
Expand Down
2 changes: 1 addition & 1 deletion .agents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ here; each tool reads it through its own directory via symlinks.
| `AGENTS.md` | Always-loaded project context | `.claude/CLAUDE.md` → here; Cursor reads `AGENTS.md` natively |
| `rules/*.md` | Coding rules, scoped to file patterns | `.claude/rules/` and `.cursor/rules/` — see below |
| `skills/*/SKILL.md` | Workflows loaded on invocation | `.claude/skills/` → here |
| `delegation.md`, `GLOSSARY.md`, `TEAM.md`, `slack-thread.md` | Read on demand, by pointer from `AGENTS.md` or a skill | — |
| `delegation.md`, `GLOSSARY.md`, `TEAM.md`, `slack-thread.md`, `slack-message.md` | Read on demand, by pointer from `AGENTS.md` or a skill | — |
| `tasks/` | Product-task specs — see `tasks/README.md` | — |

Cross-references between all of these are machine-checked: `pnpm lint:doc-links`.
Expand Down
11 changes: 2 additions & 9 deletions .agents/TEAM.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,7 @@ Slack IDs are stored so addressing is deterministic (no runtime name lookup). Th
identifiers, **not credentials** — knowing one grants no access. To find your own member ID in Slack:
your profile → **⋯ More** → **Copy member ID**. Do not add emails to this file.

## How to address

- **Person** — the *Slack member ID* (`U…`) from the People table. Mention as `<@U…>`; to DM, pass the ID as
the channel to the send tool.
- **Team / group** — the *Slack group ID* (`S…`) from the Groups table. Mention as `<!subteam^S…>`.
- **Channel** — the *Channel ID* (`C…`). Pass it as the channel target when sending; refer to it in prose by
its `#name`.
- **Permalink** — `https://blockscout.slack.com/archives/<channel-id>/p<message-ts>` (the message timestamp
with the dot removed).
How to turn an ID into a mention, a DM target, or a permalink: `./slack-message.md` → Addressing.

## Product managers

Expand Down Expand Up @@ -54,6 +46,7 @@ Own: core API endpoints and response models, field propagation across services,
| --- | --- | --- | --- |
| Victor | @vbaranov | U8L403FEG | ✓ |
| Nikita P. | @nikitosing | U0218K3MTC5 | |
| Alexey | @Qwerty5Uiop | U03EE7T5TJS | |

### Groups

Expand Down
56 changes: 56 additions & 0 deletions .agents/rules/adr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
description: ADR rules
paths:
- "**/adr/*.md"
globs: "**/adr/*.md"
alwaysApply: false
---
# Architecture decision records

An ADR answers "why is it like this?" without a git archaeology session. It carries the evidence and the
trade-off, so read the relevant one before changing what it decided.

## Template

```md
# {Short title of the decision}

| | |
| --- | --- |
| Status | Accepted \/ Superseded by <NNNN> |

## Decision

{1-3 sentences: what's the context, what did we decide, and why.}
```

That's it. An ADR can be a single paragraph. The value is in recording *that* a decision was made and *why*, not in filling out sections.

## Where a record goes

| Scope | Folder | Listed in |
| --- | --- | --- |
| Affects the project as a whole | `.agents/adr/` | a line in `.agents/AGENTS.md` |
| Relevant only to one area or module | `<module-root>/adr/` | that module's `CONTEXT.md` |

Numbering is sequential and **scoped to its folder**; the filename is `<0000>-<slug>.md`.

## When to offer one

All three must be true:

1. Hard to reverse: the cost of changing your mind later is meaningful.
2. Surprising without context: a future reader will look at the code and wonder "why on earth did
they do it this way?"
3. The result of a real trade-off: there were genuine alternatives and you picked one for specific
reasons.

If a decision is easy to reverse, skip it: you'll just reverse it. If it's not surprising, nobody will
wonder why. If there was no real alternative, there's nothing to record beyond "we did the obvious thing."

Offer the ADR; do not write one unasked.

## Superseding

Supersede rather than rewrite. Change the old record's `Status` to `superseded by <NNNN>` and leave its
reasoning intact.
2 changes: 1 addition & 1 deletion .agents/rules/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Blockscout frontend — a blockchain explorer UI. Distributed as a Docker image;
| Schema validation | Valibot |
| Unit tests | Vitest |
| Visual tests | Playwright |
| Package manager | pnpm (Node >=22.14.0) |
| Package manager | pnpm (Node >=24.21.0) |

## Directory layout

Expand Down
86 changes: 62 additions & 24 deletions .agents/rules/code-quality.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,41 @@ alwaysApply: false
---
# Code Quality

We use ESLint, cSpell and Typescript to maintain code quality and consistency across the project.
In order to check code quality run the following commands:
We use ESLint, cSpell, TypeScript to maintain code quality.

```bash
pnpm lint:eslint:fix
pnpm lint:tsc
pnpm lint:cspell
```

Moreover, please find below the general sense rules that linters do not cover.
Pull requests also pass a React Doctor gate for React bugs ESLint misses, on changed code only:
`pnpm lint:react-doctor --scope changed --base origin/main`. Reading a failure and suppressing a
finding: `tools/react-doctor/CONTEXT.md`.

The rest of this file covers code complexity scores and conventions that the linters do not.

## Complexity and CRAP

Two independent per-function gates:

- **COG** (cognitive complexity) — readability. A `BROKE` here means decompose the function (flatten nesting, extract, early-return).
- **CRAP** — under-tested *behavior* (handlers, hooks, utils — not JSX render bodies). A `BROKE` here means add a Vitest spec; simplifying the function does not lower CRAP.

How a score is counted, which functions are gated, and how to read a failure: `tools/code-complexity/CONTEXT.md`.

How to run:

```bash
# The file you are on — every function in it
pnpm test:code-complexity path/to/file.ts

# This branch vs origin/main, including uncommitted edits
pnpm test:code-complexity --changed

# For the full command usage
pnpm test:code-complexity --help
```

## Code Style and Structure

Expand Down Expand Up @@ -43,18 +69,36 @@ This applies to hooks, components, functions, and variables alike..

### Magic numbers

Extract magic numbers into named `UPPER_SNAKE_CASE` constants, placed above the component or function that uses them. This makes intent clear and avoids silent duplication.
Name a literal only when a reader would still ask why this value. The name is `UPPER_SNAKE_CASE`, sits above the component or function, and carries a domain rule the surrounding identifiers do not: a limit, threshold, interval, or similar choice.

```ts
// BAD
// BAD: 4 is a product limit with no name
const visibleItems = items.slice(0, 4);

// GOOD
const MAX_VISIBLE_ITEMS = 4;
const visibleItems = items.slice(0, MAX_VISIBLE_ITEMS);
```

The same applies to magic strings used as discriminators, keys, or thresholds. In tests, unexplained magic values should also be extracted into named constants so their meaning is clear.
Leave the literal when it is the meaning. A constant that restates the value, a nearby identifier, or a universal convention is noise. `1` is the first page, `0` is the first index, a URL is a URL.

```ts
// BAD: the name restates href and page
const URL = 'https://example.com';
const link = <a href={ URL }/>;

const FIRST_PAGE = 1;
if (page !== FIRST_PAGE) return null;

// GOOD
const link = <a href="https://example.com"/>;

if (page !== 1) return null;
```

Apply the same test to strings used as discriminators, keys, or thresholds, and to values in tests. CSS-style props (margin, padding, font weight, color) are already named by the prop.

If the same unexplained value must stay in sync at more than one call site, one constant is the share point.

#### Shared unit constants

Expand Down Expand Up @@ -134,31 +178,25 @@ Before writing custom utility logic (clamping, deep cloning, grouping, etc.), ch

### Comments

Comment the *why*, not the *what*. The code already shows what it does; a good comment explains what the
code can't — the rationale, a non-obvious constraint, a gotcha, a link to the issue or spec that motivated
it.
**Do not write comments. Never. Period.**
Prefer a name or a structure change that makes the comment unnecessary.

A comment in the diff is allowed only when it explains *why* the obvious code is wrong — a quirk, workaround, or invariant git cannot show — optionally with a link to the issue. Required mechanical comments (`eslint-disable`, unavoidable double-cast, raw color) still explain *why*, in their own sections.

Never write *diff-narration* comments — what the code replaces, what it used to do, what behavior it
preserves. That belongs in the commit message and the PR, not in the source, where it goes stale the moment
the next change lands.
Never write *diff-narration* (what this change replaces). That belongs in the commit and the PR. Test: if the comment would confuse someone who never saw the previous file, delete it.

Do not add JSDoc that restates the name or types. Do not rewrite comments you did not have to touch. Delete commented-out code; git keeps it. TODOs only with an issue or a concrete follow-up.

```ts
// BAD — narrates the change and restates the obvious
// Replaces the old useEffect approach; now we memoize the filtered list
// BAD — narrates the change and restates the next line
// Replaces the old useEffect; memoize the filtered list
const active = useMemo(() => items.filter(isActive), [ items ]);

// GOOD — explains the non-obvious why
// Memoized because <VirtualList> bails out of re-rendering on referential equality of `items`.
const active = useMemo(() => items.filter(isActive), [ items ]);
// GOOD — why the obvious approach fails
// Firefox: drag outside the window swallows mouse events until re-entry; see onMouseLeave().
const onMouseMove = useCallback(() => {}, [ ]);
```

Quick test: if a comment would confuse a reader who never saw the previous version of the file, it is
diff-narration — delete it. If it still helps that reader understand a decision, keep it.

### Commented-out code

Remove commented-out code blocks. The git history preserves anything that might be needed later. TODOs are acceptable only when paired with a clear follow-up plan or issue reference.

### Links

When links to **application pages** are constructed, verify that `nextjs-routes` or `src/shared/router/routes` utilities are used instead of string concatenation or template literals. The full list of application routes is available in `src/shared/router/nextjs-routes.d.ts`.
Expand Down
76 changes: 76 additions & 0 deletions .agents/rules/docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
description: Rules for CONTEXT.md and other documentation about the code
paths:
- "**/CONTEXT.md"
- "**/docs/**/*.md"
globs: "**/CONTEXT.md,**/docs/**/*.md"
alwaysApply: false
---
# Documenting the code

The code is the only source of truth for what it does. A doc that restates it is a second source that
nobody executes, so it drifts within weeks and then the reader has to guess which one is lying. Write
only what the code cannot say.

Applies to every doc about the code: a `CONTEXT.md`, a file under `docs/`, a module `README.md`.

## What earns a line

- **Navigation.** Which directory or file to open for what, so the reader skips a repo-wide search.
Earned only by an area large or nested enough that the split of responsibility is unclear on a
first read. Two or three files need no map.
- **Recurring questions.** How to read the tool's output, which command answers a given question, where a
failure is explained. Prefer a table of question to location over prose.
- **Local vocabulary.** Terms that mean something only in this area. Project-wide terms go in
`.agents/GLOSSARY.md` (`update-glossary` skill).
- **Constraints.** What an editor here must keep true, and what breaks if they don't.
- **Gotchas**, under the test below.
- **Pointers to the why.** Decisions with real alternatives belong in an ADR (`./adr.md`), local or
repo-wide. Link to it; do not summarise it.

## What the code and the environment already say

Leave these out. Every one of them has a source that cannot go stale:

- Control flow, signatures, prop lists, types, algorithms. The reader opens the file.
- Anything one lookup gives: `--help` output, `package.json` scripts, the directory listing, env vars
(`docs/ENVS.md`).
- What the change replaced. The commit and the PR carry that.
- Anything another doc states. Link instead, so the fact has one home.

## The gotcha test

"Not obvious" is subjective, so bind it. All three:

1. A competent reader of this code would get it wrong.
2. Someone was actually caught: you, CI, a reviewer, or production.
3. Stating it takes a sentence or two, and does not require describing how the code works.

Write the surprise and its consequence, not the mechanism.

```md
BAD — describes the code; the file says this, and says it accurately
`generate.ts` runs vitest with a config that writes coverage to a temp dir, then `read.ts`
parses `coverage-final.json` and maps ranges onto functions.

GOOD — the surprise and what it costs
A `behavior` function with no co-located spec reads 0% coverage. Simplifying it lowers COG, not CRAP.
```

If it fails the test, it belongs in the code as a name, a structure change, or (rarely) a comment.
See `./code-quality.md` for when a comment is allowed.

## Keep it maintainable

- Point at directories and files, not at symbols or line numbers.
- Prefer facts that survive a refactor: ownership, constraints, intent.
- Changing code a doc describes: fix or delete the stale lines in the same change.
- Shorter is more likely to stay true. Cut a line rather than qualify it.

## Adding a new doc

A directory earns a `CONTEXT.md` when getting oriented in it takes real reading and the layout does not
speak for itself. Offer it; do not write one unasked.

A new `CONTEXT.md` needs a line in the per-directory list in `.agents/AGENTS.md`. Cross-references are
checked by `pnpm lint:doc-links`.
Loading
Loading