# Agent skills.

`dif init` installs three Claude Code skills into `.claude/skills/`. They teach a coding agent the dif workflow: reading the surface log, drafting frontmatter, and writing a decision that becomes a learning.

The three skills map onto the daily loop: **generate surfaces** → **author an experiment** → **conclude it**. Each is a plain `SKILL.md` with a trigger description; an agent loads one when your request matches (say "draft an experiment" and `dif-author-experiment` picks it up). They're markdown. Read or edit them like any file in the repo.

```
.claude/skills/
  dif-author-experiment/     # draft + iterate on an experiment
  dif-conclude-experiment/   # write the decision, archive, learn
  dif-generate-surfaces/     # propose the surface set from the codebase
CLAUDE.md                    # managed dif block
AGENTS.md                    # managed dif block
.cursorrules                 # managed dif block
```

All of this is installed by `dif init` unless you pass [`--agents none`](https://dif.sh/docs/cli/#init) (the deprecated `--no-agent-files` alias still works too). Scaffold a subset with, say, `--agents claude`. The skills read from `dif/context.json` and the surface logs, so they stay current as your experiments change, with no separate sync step.

## dif-author-experiment

Drafting, editing, fixing, or iterating on an experiment `.md` under `dif/experiments/active/`. It covers the loop from `dif new` through `dif validate` to `dif build`.

**Triggers on:** create / draft / edit / fix an experiment, or any mention of an A/B test, variant, audience, hypothesis, surface, exclusion group, or the commands `dif new`, `dif validate`, `dif build`.

### Reads context first

The draft is downstream of three reads, so the skill does them before writing anything:

-   `dif/context.json`: what's active, on which surface, and how long it's been running.
-   `dif/surfaces/<surface>.md`: the `## Learnings` log, so it builds on prior findings instead of re-testing an answered question.
-   `dif/config.yaml`: the declared `audience_attributes` it's allowed to write predicates over.

### The loop

```
$dif new <kebab-id> --surface <surface>
# fill in hypothesis, ## Brief, ## Rationale, audience, metrics
$dif validate          # collects every error at once
$dif build             # also runs validate
$git add dif/ && git commit
```

It fills what `dif new` leaves as stubs, with opinions:

-   **hypothesis:** one falsifiable sentence naming the change, the expected primary-metric direction, and the audience.
-   **variants:** at least two, weights summing to exactly 100, `control` kept as the first id.
-   **audience:** predicates only over attributes declared in `dif/config.yaml`.
-   **metrics.primary:** exactly one metric this experiment is moving.
-   **exclusion\_group:** added when `E007` fires on a same-surface overlap.

The skill ships reference files for the details: `frontmatter.md` (full schema + a worked example), `validation-errors.md` (every `E`/`W` code with its fix), and `audiences.md` (predicate grammar and the resolver contract).

## dif-conclude-experiment

Concluding an experiment that has run its course: writing a decision, archiving the file, and running `dif conclude`. The whole operation is atomic; if any step fails, every file change rolls back.

**Triggers on:** conclude / finish / ship / wrap up an experiment, recording a learning or writing a decision, or the command `dif conclude`.

### The decision is load-bearing

What you write becomes the next line in the surface's `## Learnings` log, and the next `dif new` on that surface embeds it into the draft. So the skill won't conclude until it can answer four things:

-   Is there a real analysis of the primary metric over the run, not a hunch?
-   Did the experiment run long enough, on enough of the right traffic, to answer its hypothesis?
-   Can the outcome compress to one sentence with a metric direction and magnitude?
-   Did the guardrails hold? A primary win that regresses a guardrail isn't a ship.

```
$dif conclude checkout-cta-v2 --decision "Shipped variant_a. +2.1% checkout conversion, p<0.01 over 14d. Guardrails clean."
```

That one command, atomically:

1.  moves `dif/experiments/active/<id>.md` → `dif/experiments/concluded/<YYYY-MM>-<id>.md`;
2.  sets `status: concluded` and `concluded: <today>`;
3.  fills the `## Decision` block;
4.  appends one dated line under `## Learnings` on the surface.

A good decision is one line: did the hypothesis hold, by how much, over what period, and what's the next move (ship / kill / iterate). After concluding, the skill runs `dif build` and, if the decision was to ship, removes the now-dead `dif("<id>", ...)` call site (it would otherwise trip `W001`) and bakes the winning variant into the code.

> // note Bad decisions to avoid: `"Inconclusive."` (no next move), `"Looked good, shipping it."` (no metric or magnitude), `"See Notion doc."` (the surface log is the source of truth for future drafts; links rot). Either you have an answer or you're not ready to conclude.

## dif-generate-surfaces

Producing the initial `dif/surfaces/<name>.md` set by reading the app's structure and product context. Most useful right after `dif init` (which writes only the one default surface), or when `dif new --surface X` exits with "surface does not exist."

**Triggers on:** set up / scaffold / populate / propose surfaces, especially a sparse `dif/surfaces/`, or a `dif new` that failed on a missing surface.

A surface is a unit of _experimentation agency_, not a unit of code: the place where you'd reasonably A/B test something. A typical app has **3-8** surfaces, not one per route or component. The skill:

1.  surveys existing surfaces and `dif/context.json`, scoping to what's missing;
2.  reads product context (`README.md`, `package.json`, the routing files) to learn where experimentation actually happens;
3.  drafts 3-8 coarse-grained candidates, each with a one-line rationale;
4.  confirms the list with you before writing anything;
5.  writes each file in the same format `dif init` emits, then runs `dif validate` and `dif build`.

```
# Surface: checkout

(One-sentence description: where in the app is this surface? Who sees it?)

## Known landmines

(Vendor DOM, regulated regions, race conditions — one bullet each. Empty to start.)

## Learnings

(One line per concluded test, appended by dif conclude. Empty to start.)
```

It keeps names kebab-case and singular (`checkout`, `search-results`), leaves `## Known landmines` and `## Learnings` empty (both accrue from real experience; guesses pollute future drafts), and never auto-flips `default_surface` in your config. From here, `dif-author-experiment` takes over.

## Agent guidance files

Alongside the skills, `dif init` writes a managed dif block into `CLAUDE.md`, `AGENTS.md`, and `.cursorrules`. dif owns only its marked block; anything else you've written in those files is preserved across re-runs.

The block points the agent at the workflow and at `dif/context.json`, the session-start read that lists active experiments, their surfaces and variants, and each surface's most recent learning. `dif build` regenerates it, and it lives outside the gitignored `dif/generated/` directory so it's checked in and any agent picking up the repo sees the current state.

> // note Pass `dif init --agents none` (or the deprecated `--no-agent-files` alias) to skip the skills and the guidance blocks entirely. The CLI, SDK, and build are unaffected. The skills are an accelerant for the agent-assisted workflow, not a dependency of it.
