segments/<segment-key>.toml
Segments are reusable, named audiences. Flag rules reference segments by key (or use an inline predicate for one-off audiences). This page is the complete reference for a segment file.
The predicate expression language lives in predicates; percentage-bucket segments live in buckets.
Skeleton
schema_version = "0.1"
[segment]
description = "..."
# At least one of [segment.predicate] or [segment.bucket] MUST be present.
[segment.predicate]
# Predicate expression — see predicates.md
[segment.bucket]
# Percentage-bucket configuration — see buckets.md
The filename stem is the segment key. There is no separate segment.key field.
A segment file MUST contain:
- A top-level
schema_version. - A
[segment]table. - Either
[segment.predicate], or[segment.bucket], or both.
A segment defining neither raises E011.
[segment] fields
| Field | Type | Required | Default | Validation | Description |
|---|---|---|---|---|---|
description | string | no | "" | Free-form prose | Who is in this segment. The linter emits I003 when absent or empty. |
predicate | predicate expression | no (but see below) | — | See predicates | Boolean expression evaluated against the evaluation context. |
bucket | bucket table | no (but see below) | — | See buckets | Deterministic bucket-range definition. |
At least one of predicate or bucket MUST be present (E011 otherwise).
No
keyfield. Schema 0.1 removedsegment.key; the segment key is the filename stem and the only source of truth. Declaringkey = "..."under[segment]isE016.
Segment key semantics
The filename stem is the wire-level identifier referenced from:
- Flag rules:
segment = "<key>". - Other segments' predicates:
{ segment = "<key>" }.
The stem MUST match the key pattern [a-z][a-z0-9_-]* (max 63 chars). Violations raise E032 and the file is dropped before per-file lint.
description
Free-form prose explaining the audience. The linter emits info diagnostic I003 when the field is absent or empty.
A good description answers: who is in this segment, and why? ("First 10% of users hashing into the checkout-redesign-2025 rollout space, used to gradually expand the new checkout flow.")
Three segment shapes
1. Predicate only
# segments/internal-or-beta-users.toml
schema_version = "0.1"
[segment]
description = "Internal employees or users enrolled in the beta program"
[segment.predicate]
or = [
{ attribute = "user.segment", op = "eq", value = "internal" },
{ segment = "beta-users" },
]
The entity is a member iff the predicate evaluates to true. Predicates may include attribute checks, segment references, and compound expressions; see predicates.
2. Bucket only
# segments/checkout-redesign-rollout-10.toml
schema_version = "0.1"
[segment]
description = "First 10% of users for the checkout redesign rollout"
[segment.bucket]
entity_id_attribute = "user.id"
salt = "checkout-redesign"
start = 0
end = 999
The entity is a member iff its hash bucket falls inside [start, end]. See buckets.
3. Both (predicate + bucket hybrid)
# segments/beta-users-variant-a-bucket.toml
schema_version = "0.1"
[segment]
description = "First 10% of beta users (constrained 10% within the beta cohort)"
[segment.predicate]
segment = "beta-users"
[segment.bucket]
entity_id_attribute = "user.id"
salt = "checkout-redesign"
start = 0
end = 999
When both fields are present, the entity is a member iff both the predicate and the bucket match. This is the canonical pattern for "variant A for the first N% of users, but only inside a stable audience."
Cross-segment references
A segment's predicate MAY reference another segment via { segment = "<key>" }. References are resolved recursively: the referenced segment is evaluated against the same evaluation context, and the result feeds into the parent predicate.
The linter enforces:
- Existence. Every
{ segment = "<key>" }reference MUST resolve to a segment file in the same flag namespace'ssegments/directory; otherwiseE005. - Acyclicity. Circular references are forbidden. The linter detects cycles via depth-first search and emits
E012on each cycle's entry segment. Examples:a → a(self-loop):E012ona.a → b → a:E012on the first segment in the cycle reached during traversal.- Two disjoint cycles: each cycle reports
E012on its respective entry. - A reference to a missing segment:
E005only (the broken edge is not a cycle).
Negation via segment reference
A predicate-only segment can negate another segment's membership:
# segments/non-internal-users.toml
[segment]
[segment.predicate]
not = { segment = "internal-users" }
Members of non-internal-users are exactly those entities not in internal-users. Useful for layered rule sets that want to exclude an audience without copying the predicate.
Segments are flag-namespace-local
Segments are scoped to the flag namespace they live in. A flag in flag namespace payments cannot reference a segment in flag namespace growth; cross-namespace dependencies are not supported. The linter operates on one flag namespace at a time and treats every cross-namespace reference as a missing segment (E005).
To reuse audiences across flag namespaces, define the predicate logic redundantly in each one, or build out a shared flag namespace and have both teams contribute to it.
Common diagnostics
| Scenario | Diagnostic |
|---|---|
[segment] table missing entirely | E025 |
segments/<stem>.toml filename stem violates pattern | E032 (file dropped before lint) |
Neither predicate nor bucket declared | E011 |
description absent or empty | I003 |
predicate references a segment that doesn't exist | E005 |
predicate forms a cycle (direct or transitive) | E012 |
predicate exceeds 5 levels of compound nesting | W005 |
predicate contains a malformed atom or compound | E015 |
predicate is an empty table (no keys) | E015 |
predicate atom uses in / not_in with values = [] | E033 |
| Segment is defined but never referenced | W013 |
bucket missing required keys or has invalid range | E006 |
bucket has no salt field | W004 |
bucket and predicate both present, both well-formed | accepted |
Unknown field at [segment] | E016 |
Recommended practice
- Name segments after the audience they describe, not the rollout step.
internal-employeesis more reusable thanexperiment-42-treatment-a. - Use predicate-only segments for stable audiences — employees, beta cohorts, geographic regions.
- Use bucket segments for percentage rollouts. Always set an explicit
salt(eliminatesW004and lets you stabilize bucket assignments across segment renames). - Use predicate + bucket for "the first N% within a stable audience."
- Compose segments rather than copy predicates. A segment that references three other segments via an
oris easier to maintain than three separate flags each repeating the same predicate. The lint cycle check keeps you safe from the obvious failure modes.
See also
- predicates — the predicate language used inside
[segment.predicate]. - buckets —
[segment.bucket]fields, hash math,entity_id_attributesemantics. - flag — how flag rules reference segments.
- diagnostics —
E005,E006,E011,E012,E015,E025,E032,E033,W004,W005,W013.