Skip to main content

exd schema

Emits the eval-context shape a flag (or the whole flag namespace) expects — attribute names, inferred types, requiredness, and source citations.

Synopsis

exd schema [<flag>] [--env <env>]
[--manifest <path-or-uri>]
[--format human|json|jsonschema|rust|typescript]
[--http-backend curl|in-process]

Description

The schema is inferred from lint::attr_types: every well-formed predicate atom and every [segment.bucket].entity_id_attribute contributes one attribute → type mapping. This is the same cross-file inference that backs the E034 lint diagnostic.

Without <flag>, emits the flag-namespace-wide union schema — every attribute every flag's transitive predicates touch.

Schema vs. fixtures. exd schema answers "what type is each attribute?" — type-based. exd fixtures answers "what values trigger each rule?" — example-based. Pair both for thorough SDK testing.

Use cases

  • Codegen a typed context struct. Emit a Rust struct or TypeScript interface for the consuming SDK:

    exd schema onboarding-banner --env prod --format rust > src/contexts.rs
    exd schema onboarding-banner --env prod --format typescript > src/contexts.ts
  • Wire up runtime validation. Emit a JSON Schema 2020-12 document and validate user requests against it before evaluation:

    exd schema --format jsonschema > schemas/marketing-context.json
  • Audit a flag namespace's attribute surface. Without <flag>, see every attribute every flag depends on — useful before declaring a [namespace].private_attributes set:

    exd schema
  • Discover the input shape for an agent. An agent constructing an evaluation context can call exd schema <flag> --env <env> --format json to learn exactly which keys to populate.

Arguments and flags

Argument / flagRequiredNotes
<flag> (positional)noWhen present: schema for that flag's transitive attribute closure. When absent: flag-namespace-wide union.
--env <env>noWhen present: filters to attributes reachable from rules that fire in that env. When absent with <flag>: union across every env block in the flag. When absent without <flag>: union across every (flag, env) pair.
--manifest <path-or-uri>noDefaults to ..
--format <fmt>no, default humanOne of human, json, jsonschema, rust, typescript.
--http-backend curl|in-processnoURI loads only.

What "the schema" contains

For each attribute:

  • Name — the dotted-path attribute name as it appears in predicates.
  • Type — one of boolean, integer, float, number, string, semver. number is the loose type used when only relational operators have been applied; it accepts both integer and float.
  • Requiredtrue iff at least one site referencing the attribute uses an operator other than is_set / is_not_set.
  • Source citations — list of { kind, file, operator?, line? } items, one per site (segment / segment_bucket / flag_rule).

Output formats

--format human

$ exd schema onboarding-banner --env prod
schema for flag 'onboarding-banner' in env 'prod'
user.id string required sources:
- segments/onboarding-banner-bucket-treat-a.toml [segment.bucket].entity_id_attribute
user.country string required sources:
- flags/onboarding-banner.toml rule[0] inline predicate `in ["US","CA"]`
user.tier string optional sources:
- segments/tier-pro.toml predicate `eq "pro"`

Flag-namespace-wide: each flag gets its own subsection, with a # union across all flags row at the bottom.

--format json

{
"query": "schema",
"result": {
"scope": { "kind": "flag", "flag": "onboarding-banner", "env": "prod" },
"manifest_fingerprint": "marketing@a1b2c3",
"attributes": [
{
"name": "user.id",
"type": "string",
"required": true,
"sources": [
{ "kind": "segment_bucket", "file": "segments/onboarding-banner-bucket-treat-a.toml" }
]
}
]
}
}

Flag-namespace-wide: scope.kind = "namespace", flag field omitted; result.per_flag carries the per-flag breakdowns, top-level attributes carries the union.

--format jsonschema

Standard JSON Schema 2020-12, with vendor extensions for exd-specific provenance:

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://exd.dev/schemas/marketing/onboarding-banner/prod",
"title": "EvalContext for marketing/onboarding-banner in prod",
"type": "object",
"properties": {
"user.id": { "type": "string" },
"user.country": { "type": "string" },
"user.tier": { "type": "string" }
},
"required": ["user.id", "user.country"],
"additionalProperties": true,
"x-exd-fingerprint": "marketing@a1b2c3",
"x-exd-sources": { /* per-attribute source citations */ }
}

additionalProperties: true is normative — passing extra context to the eval engine is legitimate.

Type mapping: boolean"boolean"; integer"integer"; float"number" + "format": "double"; number"number"; string"string"; semver"string" + "pattern": "^\\d+\\.\\d+\\.\\d+(?:[-+].+)?$".

--format rust

// Generated by `exd schema onboarding-banner --env prod --format rust`.
// Manifest fingerprint: marketing@a1b2c3 — rerun if predicates change.
pub struct OnboardingBannerContext {
pub user_id: String, // required: segments/onboarding-banner-bucket-treat-a.toml [segment.bucket].entity_id_attribute
pub user_country: String, // required: flags/onboarding-banner.toml rule[0] inline predicate
pub user_tier: Option<String>, // optional: segments/tier-pro.toml predicate
}

Field names: snake_case(<dotted-attr-name>) with ._. Optional attributes become Option<T>. integeri64, float / numberf64.

--format typescript

// Generated by `exd schema onboarding-banner --env prod --format typescript`.
// Manifest fingerprint: marketing@a1b2c3 — rerun if predicates change.
export interface OnboardingBannerContext {
"user.id": string; // required
"user.country": string; // required
"user.tier"?: string; // optional
}

Quoted property names preserve the dotted form — Record-typed eval contexts in the TS SDK accept dotted keys natively.

Exit codes

CodeCondition
0Success.
1Unknown flag (positional); manifest lint errors; typed-mode unknown env.
2Bad CLI args; manifest URI fetch failure.

See also

  • exd explain — same data appears in the "Required context" section.
  • exd fixtures — concrete (ctx, variant) examples.
  • exd lintE034 is the diagnostic backed by the same inference table.