Skip to main content

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. --trace upgrades it to a step-by-step walk of the resolution algorithm.
  • All-flags (exd eval with the positional omitted) — evaluates every flag in the namespace against the supplied context. By default one <flag-key> <variant> row per flag; under --trace the output adds a "segments qualified" section listing every segment the context belongs to. JSON uses query: "eval-all" and groups results under result.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 eval answers "what would this specific context get?" — a dynamic, ctx-bound question. exd explain answers "what does this flag (or namespace) do?" — a static description with no --ctx-bound outcome. Both subcommands relax the <flag> positional symmetrically: omit it on eval to evaluate every flag for one context, or on explain to 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_123 get 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-banner can 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 prod with no --ctx and 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-namespace
    exd eval onboarding-banner --env prod --ctx user.id=u_canary --ctx user.country=DE --trace

Arguments and flags

Argument / flagRequiredNotes
<flag> (positional)noFlag key (filename stem). Omit to evaluate every flag in the namespace. Unknown key → exit 1.
--env <env>yesEnvironment 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, repeatableSee conventions § --ctx k=v parsing. Repeat once per attribute.
--tracenoUpgrades 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>noDefaults to .. See conventions § Common flags.
--format human|jsonnoDefaults to human. See conventions § JSON output.
--http-backend curl|in-processnoOnly 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

CodeCondition
0Any returned variant, including fall-through to _.variant.
1Unknown flag; manifest lint errors; typed-mode unknown env; eval ended in an attribute-type mismatch with no later rule matching.
2Bad --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 --trace renders.
  • Conventions--ctx parsing, exit-code policy, JSON envelope.