← Blog

AGENTS.md for feature flags: the block dif writes for your coding agent

AGENTS.md feature flags start with one paragraph in the file your coding agent reads on session start. That paragraph tells the agent where flag files live, what shape they take, how to gate code with one, and how to validate before opening a PR. Without it, the agent guesses, and the guesses do not match your repo. The block is one file with five things in it.

AGENTS.md is now an open specification. As of December 2025 it is adopted by 60,000+ open-source projects and 20+ AI coding tools, and hosted by the Linux Foundation Agentic AI Foundation. Codex, Cursor, and most agentic coding tools read it first. Claude Code still reads CLAUDE.md by default, with @AGENTS.md on the first line as the settled bridge. This post shows the block, a coding-agent transcript with and without it, and how feature flags for AI agents work once it is in place.

Key Takeaways

  • AGENTS.md is an open specification adopted by 60,000+ open-source projects and 20+ AI coding tools by December 2025. Codex and Cursor read it; Claude Code still reads CLAUDE.md by default.
  • A coding-agent-ready feature-flag block contains five things: file location, frontmatter schema, gating pattern, naming rule, and the local validation command (dif validate).
  • Without the block, coding agents invent flag patterns that do not match the repo. With it, they write the correct file on the first turn.
  • dif init writes managed blocks into AGENTS.md, CLAUDE.md, and .github/copilot-instructions.md, plus a Cursor rule at .cursor/rules/dif.mdc; dif init --agents-only refreshes them.
  • The block only helps if the agent reads it. Some IDE integrations bypass repo instruction files. Check yours before you rely on it.

What AGENTS.md, CLAUDE.md, and .cursorrules actually do

Each is a Markdown file at the repo root that tells a coding agent how this codebase works. The agent reads it on session start, before it edits anything. Different tools favor different filenames.

AGENTS.md was formalized as an open spec in August 2025 by OpenAI, Google, Cursor, and Factory, and donated to the Linux Foundation Agentic AI Foundation in December 2025. Codex, Cursor, Aider, and most other agentic coding tools read it. CLAUDE.md predates the spec; Claude Code still reads it first, and the settled workaround is to put @AGENTS.md on the top line of CLAUDE.md so the shared block is imported. .cursorrules is Cursor’s legacy format, still read alongside AGENTS.md. The spec also supports per-package files with closest-file-wins resolution, so a monorepo can hold different flag conventions in per-package AGENTS.md files.

Coding toolReads firstFallback
CodexAGENTS.mdNone (co-authored the spec)
CursorAGENTS.md.cursorrules (both read; AGENTS.md wins)
Claude CodeCLAUDE.md@AGENTS.md import (August 2026 default)
AiderAGENTS.mdCONVENTIONS.md

What a coding-agent-ready feature-flag block contains

Feature flags are one of the highest-payoff conventions to document. Guessing produces flags with the wrong shape, wrong location, wrong naming, and no validation. Any of those means the PR bounces in review or the SDK breaks at runtime.

A coding-agent-ready feature-flag block covers five things:

  1. Where flag files live. The exact directory (dif/experiments/active/), the filename shape (<id>.md), and where concluded files move.
  2. The frontmatter schema. Real fields with real types: owner email, surface, variants with weights that sum to 100, audience, exclusion group.
  3. The gating pattern. The exact SDK call at the render site (dif("flag-id", branches)), the import path, and where it goes.
  4. The naming rule. Kebab-case, surface-prefixed, whatever the team uses. An agent invents a rule if you do not name one.
  5. How to validate before the PR. The command (dif validate), and what a green pass and a red failure look like.

Every one of these lands in the file the agent reads at session start.

A real AGENTS.md feature-flag block

Here is the block dif emits into AGENTS.md on dif init, abridged at each [...]. CLAUDE.md and .github/copilot-instructions.md get the same block with small differences, and Cursor gets the same guidance as a rule at .cursor/rules/dif.mdc. The block is delimited by managed comments so dif init --agents-only can refresh it without touching the rest of your file:

<!-- dif:start -->
<!-- generated by dif v0.7.0; safe to re-run `dif init` to refresh -->

# Working with dif.sh in this repo

[...] Experiments live as `.md` files under `dif/experiments/`, and a Rust CLI compiles them into a typed TypeScript artifact (`dif/generated/client.ts`) the app imports at render time.

## File layout

    dif/experiments/active/<id>.md              drafts + running experiments
    dif/experiments/concluded/<YYYY-MM>-<id>.md archived; renamed by `dif conclude`
    dif/surfaces/<surface>.md                   one per surface; owns the Learnings log
    dif/audiences/<attr>.ts                     one resolver per declared audience attribute
    dif/config.yaml                             project config (audience attrs, bucketing, events)
    dif/generated/                              gitignored; output of `dif build`
    dif/context.json                            agent-facing summary of active experiments

## The verbs

| Verb | What it does |
|---|---|
| `dif init` | Scaffold the convention. Done once per repo. Add `--key <dif_pk_…>` to connect to dif.sh Cloud at the same time. |
| `dif connect --key <dif_pk_…>` | Point an existing workspace at dif.sh Cloud: writes the publishable key + cloud mode into `dif/config.yaml`. Re-run `dif build` after. |
| `dif new <id> --surface <name>` | Draft `dif/experiments/active/<id>.md`. Embeds the surface's last 3 learnings as an HTML comment in the Brief. |
| `dif validate` | Schema, weights, audience, exclusion checks. Exit 1 on any error. |
| `dif build` | Compile to `dif/generated/client.ts` + `dif/context.json`. Runs validate first. |
| `dif qa --user <id>` | Trace a user's assignment chain (debugging). |
| `dif conclude <id> --decision "<text>"` | Atomic: move to `concluded/`, fill the Decision block, append one line to the surface's Learnings log. |

[...]

<!-- dif:end -->

The parts cut here are the install note, the list of validation codes, a checklist for drafting and concluding, and pointers to the skills. The block is identical across repos. It does not carry a naming rule or your gating pattern, so write those into AGENTS.md outside the markers, where dif init --agents-only leaves them alone.

What the coding agent does without the block, and with it

Same repo, same task (“add a feature flag to gate the new checkout CTA”), same coding agent. The only difference is whether the block is committed.

Without the block. The agent sees no repo convention and improvises:

user: add a feature flag to gate the new checkout CTA

agent: I'll add a feature flag. Creating a config file.

  Created: config/features.json
    { "new_checkout_cta": { "enabled": true, "rollout": 100 } }

  Updated: src/lib/components/OrderSummary.svelte
    + {#if features.new_checkout_cta.enabled}
    +   <button id="checkout-cta">Complete order</button>
    + {:else}
    +   <button id="checkout-cta">Checkout</button>
    + {/if}

  Flag added. Ready to commit.

Wrong location (config/features.json instead of dif/experiments/active/), wrong file format (JSON, not the .md schema), wrong naming (new_checkout_cta in snake_case, no surface prefix), no validation. The PR bounces in review or the SDK errors at runtime.

With the block. The agent reads AGENTS.md first and writes the right file:

user: add a feature flag to gate the new checkout CTA

agent: Reading AGENTS.md. This repo uses dif. Flags are .md files under
       dif/experiments/active/, gated with dif("flag-id", branches), validated with
       `dif validate` before PR.

  Created: dif/experiments/active/checkout-cta-copy.md
    ---
    id: checkout-cta-copy
    status: active
    owner: chris@niftic.agency
    surface: checkout
    hypothesis: >
      "Complete order" on the checkout CTA lifts completed checkouts.
    variants:
      - { id: control, weight: 50 }
      - { id: benefit_led, weight: 50 }
    metrics:
      primary: completed_checkout
    created: 2026-09-08
    ---

  Updated: src/lib/components/OrderSummary.svelte
    + import { experiment } from '@dif.sh/svelte';
    + const cta = experiment('checkout-cta-copy', {
    +   control: () => 'Checkout',
    +   benefit_led: () => 'Complete order',
    + });
    <button id="checkout-cta">
    + {$cta.value}
    </button>

  $ dif validate
    ✓ all checks passed

  Flag added and validated. Opening PR.

Right directory, right frontmatter, right gating pattern, right naming, ran the validator. The agent got the boring parts correct on the first turn because it read them off the block. The PR review is now about the hypothesis and the copy, not whether the file is in the right folder. See ai agent create feature flag for the full workflow this enables.

How dif generates the AGENTS.md feature-flag block for you

dif init writes the block into AGENTS.md, CLAUDE.md, and .github/copilot-instructions.md in one pass, and writes the Cursor rule at .cursor/rules/dif.mdc. If a file exists, dif appends the managed section between <!-- dif:start --> and <!-- dif:end --> markers instead of overwriting your content. If a file does not exist, dif creates it.

$ npm install -g @dif.sh/cli
$ dif init --surface checkout
✓ created dif/experiments/{active,concluded}
✓ created dif/surfaces/
✓ created dif/audiences/
✓ wrote dif/config.yaml
✓ wrote dif/.gitignore
✓ wrote dif/surfaces/checkout.md
✓ wrote dif/audiences/locale.ts
✓ wrote dif/audiences/device_type.ts
✓ wrote .claude/skills/ (dif-author-experiment, dif-conclude-experiment, dif-generate-surfaces, dif-triage-experiments)
✓ wrote .cursor/rules/dif.mdc
✓ merged dif guidance into CLAUDE.md
✓ merged dif guidance into AGENTS.md
✓ merged dif guidance into .github/copilot-instructions.md

After a CLI upgrade, dif init --agents-only refreshes the block. If dif ships a new verb, validation code, or config field, it rewrites what is between the markers and leaves dif/ alone. A plain dif init exits 2 if it would overwrite a dif-owned file that differs, such as an edited dif/config.yaml, and --force overwrites those files too, so read the diff before you commit. dif build does not touch the block. It emits context.json, so the block explains how flags work and context.json carries the current flag state alongside it. dif also installs agent skills that let an agent author and conclude experiments once it knows the rules.

For a monorepo, run dif init in each package. The AGENTS.md spec’s closest-file-wins rule means an agent editing packages/checkout/ reads that package’s AGENTS.md, not the root one. Three packages, three flag conventions, three blocks, each refreshed by dif init --agents-only in that package.

The tradeoff

The block is a contract with the coding agent, not a guarantee. It only helps if the agent reads it.

Codex, Cursor, Aider, and Claude Code all read repo instruction files by default. Some IDE-embedded assistants (older Copilot integrations, vendor-locked in-editor bots) do not. For those, the block is invisible and the agent falls back to guessing.

Check which files your coding agent reads before you rely on the block. The agents.md spec homepage lists current adopters.

FAQ

Should feature flags go in AGENTS.md or CLAUDE.md? Both. The same managed block goes in both files (and in .github/copilot-instructions.md, with a Cursor rule at .cursor/rules/dif.mdc) so any coding agent your team uses reads the same rules. dif init writes them all at once so they never drift.

Does Claude Code read AGENTS.md? Not by default, as of August 2026. Claude Code reads CLAUDE.md first. The settled workaround is @AGENTS.md on the first line of CLAUDE.md, which imports the shared block.

What is the difference between AGENTS.md and .cursorrules? AGENTS.md is the open spec (August 2025) backed by OpenAI, Google, Cursor, and Factory, and hosted by the Linux Foundation. .cursorrules is Cursor’s original format from before the spec. Cursor still reads both; new projects should write AGENTS.md.

How does dif keep the AGENTS.md block in sync? Run dif init --agents-only. The managed section between <!-- dif:start --> and <!-- dif:end --> gets rewritten; your own content outside those markers is left alone. dif build regenerates the typed client and context.json, not the block.

Can I use AGENTS.md for feature flags without dif? Yes. The block is Markdown. Copy the template above, replace the file paths and commands with your project’s conventions, and commit it. You lose the auto-refresh and the dif validate line, but the agent still reads the block.

Getting started

The block is one file, five things: file location, frontmatter schema, gating pattern, naming rule, validation command. Without it, a coding agent invents a pattern that does not match your code. With it, the agent writes the right file, runs dif validate, and opens the PR. AGENTS.md feature flags are the contract that makes agent-authored flag work a review problem instead of a rewrite problem.

dif writes and maintains the block for you. Install the CLI:

npm install -g @dif.sh/cli
dif init

The block lands in AGENTS.md, CLAUDE.md, and .github/copilot-instructions.md, with a Cursor rule at .cursor/rules/dif.mdc, and dif init --agents-only keeps it current. The dif docs cover the full CLI reference.