pub struct WorkloadPhase {Show 28 fields
pub cycles: Option<String>,
pub concurrency: Option<String>,
pub rate: Option<String>,
pub daemon: bool,
pub adapter: Option<String>,
pub errors: Option<String>,
pub tries: Option<u32>,
pub tries_backoff: Option<BackoffSpec>,
pub interval: Option<String>,
pub repeat: Option<u64>,
pub error_rate_max: Option<f64>,
pub timeout: Option<String>,
pub stop_when: Vec<StopConditionSpec>,
pub throttle: Option<ThrottleField>,
pub tags: Option<String>,
pub ops: Vec<ParsedOp>,
pub for_each: Option<String>,
pub continue_if: Option<ContinueIfSpec>,
pub loop_scope: Option<String>,
pub iter_scope: Option<String>,
pub checkpoint: Option<Checkpoint>,
pub status_metrics: Vec<String>,
pub bindings: BindingsDef,
pub metrics: HashMap<String, MetricSpec>,
pub dimensions: BTreeMap<String, DimensionSpec>,
pub poll: Option<PhasePollSpec>,
pub optimize: Option<OptimizeBlock>,
pub key_metrics: Vec<KeyMetric>,
}Expand description
A workload phase: runs as a separate Activity with its own cycle count, concurrency, rate limit, and op selection.
Fields§
§cycles: Option<String>Number of stanzas for this phase. Each stanza executes all
ops in sequence once. String type to support Polydat constant
references like "{train_count, dimensions: Default::default(),}". Default 1 (one stanza).
concurrency: Option<String>Concurrency (async fibers). String type to support Polydat constant
or workload param references like "{concurrency}". Default 1.
rate: Option<String>Rate limit (ops/sec). A number, or a {param} / iter-var
reference resolved at the phase gather (the SRD-83
timeout: discipline: a rate that cannot be resolved
fails the phase up front — it never silently becomes
“unrated”). Default unlimited.
daemon: boolSRD-82 Part 6 — daemon phase. When true, this phase runs
CONCURRENTLY with its foreground sibling phases (off the
scenario’s foreground concurrency budget) and is stopped
cooperatively when the scope’s foreground phases complete (a
background “daemon unit” at the phase shell). Its own scope gives
it an independent cursor base. Pair with an open-extent cursor
(e.g. until_elapsed) so it runs for the foreground’s duration.
adapter: Option<String>Adapter override for this phase.
errors: Option<String>Error routing spec override.
tries: Option<u32>Total-attempts budget for this phase’s ops. tries: is the SIGIL for
the conditional tries wrapper (SRD-82 Part 3b): when NO budget
resolves anywhere in scope (op field, this phase field, the
workload-root tries param, or an in-scope tries wire), the
wrapper is not constructed and the op runs single-attempt. 1 = the
same single-attempt behaviour, explicitly (shadows an inherited
budget); 0 = ops FAIL WITHOUT EXECUTING; N ≥ 2 = up to N total
attempts on adapter-retryable errors (CQL timeouts/overloads).
None = inherit.
tries_backoff: Option<BackoffSpec>Retry-backoff overrides parsed from the map form of tries:
(tries: {count: N, backoff: {ratio, min, max}}). None when the
sugared numeric form was used (or tries absent) — the wrapper then
falls back to the op’s standalone retry_backoff* params or the
built-in defaults (ratio 2.0, min 100ms, max 10s). See
BackoffSpec.
interval: Option<String>SRD-82/92 cross-level wrapper (scoping P0) — phase-execution pacing.
interval: is the discriminator for a future WrapperLevel::Phase
interval wrapper: re-run this phase, sleeping interval between runs.
A raw duration string (e.g. "5m"), parsed at wrap time by the
runtime. Declarative today — the phase-level cascade that consumes it
is not built yet (see docs/cross-level-wrapper-cascade-scope.md).
repeat: Option<u64>Bound for interval — how many times to run the
phase. None alongside interval = repeat until the session stops.
error_rate_max: Option<f64>OPT-IN error-rate circuit breaker (e.g. 0.1 = fail this phase
once >10% of its ops error, after a 50-op floor). Overrides a
session-wide error_rate_max= param when one was set. There is
NO built-in default (SRD-82 §“AggregateGuard retired as a
default”) — aggregate health belongs to visible stop_when:
conditions; this field exists as an explicit shorthand only.
timeout: Option<String>SRD-83 governance timeout (GAP-12). A duration ("2.5h", "150ms",
bare fractional seconds) or a {param} reference; on expiry the
phase ends Interrupted+Failed with reason class timeout — the
protocol OUT-OF-RANGE disposition: the system is disqualified at
this tier, the partial result is not usable. Desugars at the
phase gather into a synthesized, logged elapsed_ms > stop
condition (the error_rate_max precedent). Distinct from a
BUDGET: a clean time-boxed measurement is a bounded cursor or a
stop_when … effect: stop (Interrupted+Succeeded), not this.
stop_when: Vec<StopConditionSpec>SRD-83 — stop conditions for this phase shell. Each is a polydat predicate over runtime-state wires, plus a firing trigger and an effect. Evaluated at triggers; a true predicate stops the phase with its effect.
throttle: Option<ThrottleField>SRD-83 §throttle — adaptive backpressure governor: keep the
windowed attempt-failure fraction under a bound by walking a
dynamic control (concurrency/rate) down under overload and
back up on recovery. throttle: true = defaults.
Tag filter to select ops from blocks (e.g., "block:schema").
ops: Vec<ParsedOp>Inline ops for this phase (parsed into ParsedOp list).
for_each: Option<String>Phase template iteration: "var in expr".
The phase is instantiated once per element of the Polydat expression
result (which must be a comma-separated string). Each instance
has {var} available as a workload param in its ops and config.
Example: for_each: "profile in matching_profiles('{dataset}', '{prefix}')"
continue_if: Option<ContinueIfSpec>SRD-101 — optional continue_if pre-entry gate bounding this phase’s
for_each sweep (see ContinueIfSpec). Ignored when for_each is
absent (no sweep to bound).
loop_scope: Option<String>Loop scope mode for for_each phases.
Controls how the loop context is seeded from the outer scope:
clean(default): snapshot of outer scope at loop entryinherit: outer scope’s live state (includes prior phase mutations)
iter_scope: Option<String>Iteration scope mode for for_each phases.
Controls how each iteration is seeded from the loop scope:
inherit(default for for_each): each iteration starts from the loop scope’s current state. All loop-level variables are implicitly shared with iterations, so iteration N+1 sees what N wrote.clean: each iteration starts from the loop scope snapshot (isolated)
checkpoint: Option<Checkpoint>Summary report configuration for this phase.
Checkpoint declaration: skip-on-resume eligibility plus
optional sub-properties (hashing, verify op). None =
no declaration = phase always re-runs on resume. See
SRD-44 §“Eligibility — checkpoint: per-phase declaration”.
Parsed via Checkpoint’s custom deserialize from the
three YAML forms (short string, disabled string/bool,
full mapping).
status_metrics: Vec<String>Names of metrics to surface on the inline progress line and the per-phase ✓ DONE summary. Empty (default) → no extra metrics shown; the status line carries only the universal counters (pct, throughput, ok-rate, errors, retries, concurrency, duration).
Each name is matched against the live relevancy
aggregates (recall_at_10, precision_at_10, …) by exact
equality. Workloads that compute custom relevancy metrics
list the names they want emphasized; nothing is presumed
to be present.
Example:
phases:
ann_query:
status_metrics: [recall_at_10]bindings: BindingsDefPhase-level Polydat bindings: block (SRD-13c, SRD-13d).
Captured on the phase AST so the scope-tree pre-walk
(SRD-13d §3) can classify phase-level Polydat content via
crate::polydat_matter::HasPolydatMatter and so the runtime
can compose a phase kernel layered between the
workload kernel and any op-template kernels.
Today the parser ALSO merges this block into per-op
bindings (legacy parse.rs::parse_phases behaviour) so
the existing runtime keeps working unchanged. Once
SRD-13d phases 3–9 land (per-template kernels with
proper bind_outer_scope chaining through the phase
kernel), the per-op merge is removed and ops resolve
phase bindings via the Polydat scope chain.
metrics: HashMap<String, MetricSpec>Phase-level synthetic-metric declarations. Mirror of
ParsedOp::metrics (same MetricSpec schema and YAML
shapes), but evaluated once at phase completion against
the phase scope kernel rather than per-cycle. Each entry’s
value: is a Polydat expression over phase-scope wires
(bindings, captures, params, iter-vars) plus the
executor-injected phase_start wire (epoch millis at phase
start). The canonical phase-duration metric reads a clock via
a volatile phase binding and subtracts the injected origin:
bindings: |
volatile now_ms := current_epoch_millis()
metrics:
time_to_index: { value: now_ms - phase_start }yielding the phase’s wall-clock duration in millis. Empty when
absent. No dedicated clock node is needed: current_epoch_millis()
is a single read, and phase_start arrives as plain data, so the
expression re-evaluates correctly at the completion-time pull.
Declaring the clock read as its own volatile binding (rather
than nesting it in the metric value) explicitly acknowledges the
non-deterministic node, so the phase kernel stays clean under
--strict.
The synthesiser emits volatile __metric_<name> := <value>
onto the phase kernel (see
nmbrs_runtime::scope::synthesize_metric_binding_name); the
executor pulls each at completion and records it on the
phase component as the declared instrument (gauge by default).
dimensions: BTreeMap<String, DimensionSpec>Dimensions this phase introduces: label NAME → declaration.
Declared at the tier that owns the name, per the component tree’s
label-ownership rule (a name is set on exactly one tier and
inherited downward). Values are not enumerated — they arrive from
data via a metric’s cell:.
BTreeMap for deterministic synthesis order: a coordinate’s
rendering is what keys a cell, so an order that varied between runs
would key one coordinate two ways.
poll: Option<PhasePollSpec>Phase-level poll spec — when present, the phase’s
cycle execution runs in a wall-clock loop until a GK
predicate over captures returns true. SRD-75.
The presence of this field carries semantics beyond
the data: it forbids concurrency > 1 (serial-cycle
loop is the unit of work), and it triggers
scope-synthesis to allocate shared cells on the
phase scope for capture names referenced by the
predicate / if: conditions / metric values so
cross-op visibility happens through the canonical
Polydat chain (no sidecar HashMap; see SRD-75
§“Architectural shape”).
optimize: Option<OptimizeBlock>SRD-86 — when present, the executor dispatches the named optimizer over
the phase: it writes each axis as an input wire on the phase’s binding
kernel and reads the objective wire back (the objective is just a
wire read). Workload-local config; nmbrs-runtime maps it to its
optimizer contract and discovers the optimizer via the link-time
registry (nmbrs describe optimizers).
Sugar: a bare string value is shorthand for { objective: <str> }
with every other field defaulted (method: sweep, no servo:) — so
optimize: "0 - err_rate" ≡ optimize: { objective: "0 - err_rate" }.
See [de_optimize].
key_metrics: Vec<KeyMetric>SRD-109 — key-metric designations: key_metrics: {column: "agg(family)", ...}. Aggregate qualification is mandatory
(no implied aggregates); the report synthesizer attaches
these columns to the row of the phase’s nearest enclosing
anchor, or the spine. Empty = spine-only via the SRD-91
instrument contract defaults.