exd eval
Variant lookup for a flag — or every flag in the namespace — in one environment, given an evaluation context.
Synopsis
exd eval [<flag>] --env <env>
[--manifest <path-or-uri>]
[--ctx <key>=<value>]...
[--trace]
[--format human|json]
[--http-backend curl|in-process]
Description
Runs the manifest through the same evaluation engine the SDKs use, against a context you supply on the command line, and prints the variant(s) that would be served.
Two shapes, distinguished by whether <flag> is supplied:
- Single-flag (
exd eval <flag>) — one line on stdout, the bare variant key.--traceupgrades it to a step-by-step walk of the resolution algorithm. - All-flags (
exd evalwith the positional omitted) — evaluates every flag in the namespace against the supplied context. By default one<flag-key> <variant>row per flag; under--tracethe output adds a "segments qualified" section listing every segment the context belongs to. JSON usesquery: "eval-all"and groups results underresult.flags.
exd eval is read-only: it never writes to disk and never contacts the server (unless the manifest is loaded from a URI). It is the shell-script complement to the SDK's Namespace::eval (single-flag) and Namespace::eval_all (all-flags).
Eval vs. explain.
exd evalanswers "what would this specific context get?" — a dynamic, ctx-bound question.exd explainanswers "what does this flag (or namespace) do?" — a static description with no--ctx-bound outcome. Both subcommands relax the<flag>positional symmetrically: omit it onevalto evaluate every flag for one context, or onexplainto render the namespace-wide structural view.
Use cases
-
"What does this user qualify for right now?" Omit
<flag>to evaluate the whole namespace for one context and see every variant plus every matching segment in one shot:exd eval --env prod --ctx user.id=u_42 --ctx user.country=US --ctx user.tier=pro --trace -
"Why did user
u_123get the off variant last night?" Reconstruct the same evaluation locally:exd eval onboarding-banner --env prod \--ctx user.id=u_123 --ctx user.country=US --ctx user.tier=free -
CI smoke test. Pin a known-good context → expected-variant check in your repo's test suite so a botched rule rewrite fails the build:
test -z "$(diff <(exd eval onboarding-banner --env prod --ctx user.id=u_smoke) <(echo treat_a))" -
Agent simulation before code. An agent about to author code that branches on
onboarding-bannercan verify the variant for a representative context first, without compiling anything. -
Catch-all probe. "What does a brand-new user with no context see?" Run
exd eval onboarding-banner --env prodwith no--ctxand read the result — same answer the engine would produce for an empty-context evaluation. -
Rule-change confidence. In a TOML pull request, prove the new rule fires for the intended audience:
cd my-namespaceexd eval onboarding-banner --env prod --ctx user.id=u_canary --ctx user.country=DE --trace
Arguments and flags
| Argument / flag | Required | Notes |
|---|---|---|
<flag> (positional) | no | Flag key (filename stem). Omit to evaluate every flag in the namespace. Unknown key → exit 1. |
--env <env> | yes | Environment slug. In typed-env mode (namespace.toml declares [namespace.environments]), unknown env → exit 1. In untyped-env mode, any [a-z][a-z0-9-]* slug is accepted. |
--ctx <key>=<value> | no, repeatable | See conventions § --ctx k=v parsing. Repeat once per attribute. |
--trace | no | Upgrades the human output from one line to a multi-section trace (resolution walk + outcome block). Has no effect on JSON output (JSON always carries the full trace). The name --trace distinguishes this from the exd explain subcommand: --trace shows the dynamic path this evaluation took; exd explain shows the static description of the flag. |
--manifest <path-or-uri> | no | Defaults to .. See conventions § Common flags. |
--format human|json | no | Defaults to human. See conventions § JSON output. |
--http-backend curl|in-process | no | Only affects URI loads and server interactions. |
Examples
Bare lookup
$ exd eval onboarding-banner --env prod --ctx user.id=u_42
treat_a
One line, trailing newline. Nothing else on stdout. Manifest-load warnings (if any) go to stderr.
No context
$ exd eval onboarding-banner --env prod
control
Predicate atoms that reference unset attributes evaluate as "not matched"; resolution falls through to the env's variant, then _-rules (if reachable), then _-variant. Useful for the "anonymous visitor" probe.
With --trace
$ exd eval onboarding-banner --env prod --ctx user.id=u_42 --ctx user.country=US --trace
[flag.environments.prod].rules
rule[0] segment `us-only` MATCHES → treat_a
rule[1] segment `pro-tier` (not reached)
[flag.environments.prod].variant (not reached)
[flag.environments._].rules SKIPPED — env declared its own rules
[flag.environments._].variant (not reached)
outcome: treat_a
via: rule[0] in env `prod` matched segment `us-only`
ctx: user.id="u_42", user.country="US"
JSON output
$ exd eval onboarding-banner --env prod --ctx user.id=u_42 --format json
{
"query": "eval",
"query_version": "1",
"schema_version": "1",
"exd_version": "0.4.0",
"inputs": {
"flag": "onboarding-banner",
"env": "prod",
"ctx": { "user.id": "u_42" }
},
"result": {
"outcome": {
"variant": "treat_a",
"via": { "kind": "rule", "rule_index": 0, "env": "prod", "segment": "us-only" }
},
"walk": [
{ "step": "env_rules", "env": "prod", "fired_index": 0 }
]
},
"diagnostics": [],
"provenance": {
"source": ["."],
"engine": "static+ctx",
"record_count": 0,
"time_range": {}
}
}
Note: result here is a reduced TraceJson — the static description fields (variants, unreachable_variants, rules_breakdown, default_variant, segment_closure, metadata, required_context, pitfalls, notes) are elided. To get those, use exd explain.
All-flags form (no <flag> positional)
$ exd eval --env prod --ctx user.id=u_42 --ctx user.country=US
checkout-v2 off
onboarding-banner treatment
pricing-experiment variant_b
$ exd eval --env prod --ctx user.id=u_42 --ctx user.country=US --trace
namespace: marketing env: prod (typed)
context:
user.id = "u_42"
user.country = "US"
segments qualified (1/3):
- us-only
flags (3):
checkout-v2 off default variant (no rule matched)
onboarding-banner treatment rule[0] in [flag.environments.prod].rules
pricing-experiment variant_b default variant (no rule matched)
All-flags JSON
$ exd eval --env prod --ctx user.id=u_42 --format json
{
"query": "eval-all",
"query_version": 1,
"inputs": { "flag": null, "env": "prod", "ctx": { "user.id": "u_42" } },
"result": {
"namespace": "marketing",
"env": { "name": "prod", "typed": true },
"segments_qualified": ["us-only"],
"flags": {
"onboarding-banner": {
"variant": "treatment",
"reason": "matched_rule",
"rule": { "index": 0, "source": "[flag.environments.prod].rules" }
},
"checkout-v2": { "variant": "off", "reason": "default_variant" }
}
},
"diagnostics": [],
"provenance": { "engine": "static+ctx", "record_count": 0, "source": ["."] }
}
Exit codes
| Code | Condition |
|---|---|
0 | Any returned variant, including fall-through to _.variant. |
1 | Unknown flag; manifest lint errors; typed-mode unknown env; eval ended in an attribute-type mismatch with no later rule matching. |
2 | Bad --ctx; missing --env; unknown CLI flag; manifest URI fetch failure. |
See also
exd explain— static description of the same flag.exd fixtures— generate a(ctx, expected variant)test table for SDK consumption.exd schema— list the context attributes the flag requires.- Resolution algorithm — the four-step resolution algorithm
--tracerenders. - Conventions —
--ctxparsing, exit-code policy, JSON envelope.