# reference v0.1

Troubleshooting.

Every diagnostic code dif validate emits, what it means, and how to fix it.

Diagnostic codes

codelevelmeaningfix
E001errorFrontmatter failed to parse: missing --- delimiters, invalid YAML, or a required field is missing.Diagnostic includes the line and column. Open the file at that location and fix the YAML.
E003errorowner doesn't look like an email: no @, no domain, or whitespace.Use a syntactically valid email. name@example.com.
E004errorsurface doesn't resolve to a loaded surface file.Create dif/surfaces/<name>.md, or change the surface: field to one that exists.
E005errorVariant weights don't sum to 100.Adjust the weights so they total 100.
E006errorAn audience predicate references an attribute that isn't declared in dif/config.yaml.Add it to audience_attributes, or remove the predicate.
E007errorTwo active experiments target the same surface without a shared exclusion_group, and their audiences aren't provably disjoint.Three options: (1) add the same exclusion_group: to both, (2) narrow one audience so the analyzer can prove disjointness (e.g. country: US vs country: UK), or (3) move one to a different surface.
E008errorA declared audience attribute has no resolver file in dif/audiences/.Create dif/audiences/<name>.ts, or run dif scaffold-audiences for the starters.
W001warningSource code contains a dif("<id>", ...) call referencing an experiment that doesn't exist (or isn't active).Either create the experiment or remove the dif() call. Warnings don't fail the build.
W002warningA resolver file in dif/audiences/ has no matching declaration in config.yaml.Declare it under audience_attributes, or delete the unused resolver.

Diagnostics include the file, line, and column of the offending content. The CLI prints them rustc-style:

error E005 dif/experiments/active/bad.md:1:1: variant weights sum to 80, expected 100
  help: Distribute the variants so the weights total 100.

Use --json if you'd rather post-process them.

FAQ

What's the bucketing algorithm?

SHA-256 over a per-experiment salt concatenated with the user id, mod 10000 → a bucket in [0, 10000). Variant selection walks variants in declared order, accumulating weight; the first variant whose cumulative crosses the bucket wins. Bucketing is deterministic for a given salt and user id, and Rust and TypeScript share a cross-language fixture in the repo test suite: Rust generates it, TypeScript verifies, and CI runs both on every PR.

Can I run two experiments on the same surface?

Yes. Either share an exclusion_group (the runtime resolves with priority: earliest created wins) or write audiences the disjointness analyzer can prove don't overlap (country: US vs country: UK, for example). Neither, and dif validate refuses with E007.

Do I need a resolver for every audience attribute?

Yes. Each attribute referenced by an active experiment must be declared in dif/config.yaml and have a synchronous resolver at dif/audiences/<name>.ts that returns a compatible scalar. Missing the declaration is E006; missing the resolver is E008. dif build bundles only the resolvers active experiments actually use into dif/generated/audiences.ts.

Why does dif/context.json change every build?

It carries a generation timestamp. That's intentional: agents read freshness from it. The file lives outside the gitignored dif/generated/ directory so it's checked in. In CI, rebuilding it will normally produce a diff even when experiment inputs are unchanged; don't assert a clean context diff unless you normalize the timestamp.

Where does git config come into it?

dif new infers owner from git config user.email when --owner isn't passed. If neither is set, the command errors out. CI runs without a git identity should pass --owner explicitly.

Is the generated dif/generated/client.ts meant to be committed?

No, by default. dif init writes a dif/.gitignore covering generated/. Teams that want PR-visibility of the generated code can flip build.commit_generated (a reserved field today).

What about Windows?

The Rust CLI ships a Windows binary; install via the npm wrapper (npm install -g @dif.sh/cli) or download the dif-x86_64-pc-windows-msvc.zip from the GitHub Release page. The shell installer at dif.sh/install.sh is POSIX-only. Use WSL or the wrapper instead.

What's the cloud offering? Cloud

dif.sh Cloud mirrors your repo's spec, ingests dif.track() and exposure events, and computes lift / CI / SRM on top. It's strictly additive: connect at runtime through dif.init({ project, publishableKey, apiUrl }), the OSS CLI keeps working offline, and writes back to git always happen as PRs. See the tracking, publishable keys, and ingest endpoints sections for the integration.

How do I uninstall?

Delete the dif/ directory. Experiments and surfaces are just markdown files in your repo: leave them, delete them, archive them, whatever you'd do with any other source. Remove @dif.sh/sdk from your app's package.json and the dif(...) call sites are dead code your bundler will strip.

// missing?If a diagnostic code or a question isn't covered here, open an issue at github.com/dif-sh/dif/issues. Real bug reports → real diagnostic entries → real fixes. That's the loop.