Using dif skills for agent-driven experimentation
dif init installs more than a folder. It drops four Claude Code skills into .claude/skills/ and writes a guidance block into your agent config files, so a
coding agent can draft, edit, conclude, and triage experiments next to you. The skills read dif/context.json and the surface logs on the way in, so prior learning travels
with the work.
What dif init put in your repo
Alongside the dif/ workspace, dif init writes:
.claude/skills/ dif-author-experiment/ # draft and iterate on an experiment dif-conclude-experiment/ # write the decision, archive, learn dif-generate-surfaces/ # propose the surface set from the codebase dif-triage-experiments/ # act on Cloud verdicts: ramp, turn off, conclude CLAUDE.md # a managed dif block AGENTS.md # the same block, for other agents .github/copilot-instructions.md # the same block, for Copilot .cursor/rules/dif.mdc # the same guidance, as a Cursor rule
dif owns only its marked block in CLAUDE.md, AGENTS.md, and .github/copilot-instructions.md, so anything else you keep there survives a
re-run. To skip all of it, pass dif init --agents none (the deprecated --no-agent-files alias still works too). The CLI, the SDK, and the build work the
same without it.
context.json: the session-start read
dif build regenerates dif/context.json. It lives outside the
gitignored generated/ folder, so it is committed, and any agent that opens the
repo sees the current state. Think of it as a CLAUDE.md for your experiments:
{
"active": [
{ "id": "checkout-cta-v2", "surface": "checkout",
"variants": ["control", "variant_a"], "running_for_days": 7 }
],
"surfaces": [
{ "name": "checkout",
"recent_learning": "Get it today lifted conversion 2.1% on returning" }
],
"conventions": ["Fire exposure at render, never at assignment."]
}The file carries what is active, on which surface, for how long, each surface's most recent learning, and the project conventions. Because the skills read it first, a new draft starts from what the last test found instead of from a blank page.
The four skills
Each skill is a plain SKILL.md with a trigger description. Your agent loads one
when your request matches: say "draft an experiment" and dif-author-experiment answers. Three follow the daily loop. The fourth works across every running experiment at
once. The skills reference covers each one in full.
Two of them read results from Dif Cloud and need a read token, through the MCP server or in DIF_TOKEN: see MCP and API. Without Cloud, the
conclude skill asks you for the numbers and the triage skill stops.
dif-author-experiment
For drafting, editing, or fixing an experiment. It reads three files before writing
anything: dif/context.json for what is already running, the surface's ## Learnings log so it does not re-test a settled question, and dif/config.yaml for the audience attributes it may use. Then it runs the loop:
$ dif new <id> --surface <surface> # fill in the hypothesis, brief, rationale, audience, metrics $ dif validate $ dif build
It fills the stubs with opinions: one falsifiable hypothesis, variants that total 100 with control first, a single primary metric, and predicates only over declared
attributes.
dif-conclude-experiment
For closing a test that has run. The decision you write becomes the next line in the surface
log, and the next draft on that surface reads it. So the skill gets the numbers first: it
calls get_experiment_results on Dif Cloud, or asks you when Cloud is not
connected. It concludes only when the verdict is ship, turn_off, or conclude_inconclusive, unless you override it in chat. It starts from the
one-line decision_draft Cloud writes and keeps its numbers.
$ dif conclude checkout-cta-v2 \ --decision "Shipped variant_a. +2.1% completed_checkout (95% CI +0.8% to +3.4%), p=0.004 over 14d. Guardrails: refund_rate clean."
That command moves the file to concluded/, fills the ## Decision block, and appends a dated line to the surface log. A useful decision reads like the one
above. "Inconclusive." tells the next draft nothing.
dif-generate-surfaces
For a fresh repo, where dif init left a single surface. A surface is a place
you would run a test, not a route or a component, so most apps have three to eight. The
skill reads your README, your routes, and your config, proposes a list with a one-line
reason for each, and waits for your yes before it writes the files.
dif-triage-experiments
For the question "what needs action?" across every running experiment. It reads the Dif
Cloud overview, shows one row per experiment with its verdict and next action, and acts on
the next action: it edits weights for a ramp or a kill, and hands a conclusion to dif-conclude-experiment. Broken data and metrics with no direction are
reported, never acted on. It confirms each change with you by default. On an unattended
run, it opens one pull request per change, pushes at most 5 branches per run, and never
merges.
A session looks like this
You talk to the agent the way you would a teammate. To start a test:
Draft an experiment on checkout testing a more urgent CTA, for returning visitors.
It reads the checkout log, runs dif new, fills the frontmatter, and validates. When the numbers are in:
Conclude checkout-cta-v2.
With Dif Cloud connected, it pulls the result, checks the verdict, writes the decision from
the draft and asks you to confirm it. Then it archives the file, appends the learning,
removes the call sites that W001 names and commits. You review the commit
before you push. To check everything at once:
What needs action across my experiments?
It shows each experiment's verdict and proposes the ramps, kills, and conclusions, one change at a time.
The skills are an accelerant, not a requirement. The CLI and SDK behave the same without
them, and the AGENTS.md, .github/copilot-instructions.md, and .cursor/rules/dif.mdc guidance points non-Claude agents at the same workflow. Once a test ships, the next step is a team-wide live read, which
is what Dif Cloud adds.