Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Compose Sweeps

Audience: Go embedders, CLI users and agents who run one question over a grid of parameters.

ComposedRequest.Sweep (types.SweepSpec) is one request body plus axes of values. Before any slot runs, Pulse expands it into ordinary Compose slots appended after the explicit Requests. Each expanded slot then runs exactly like a hand-written one: same limits, same labels, same overlay fold, serial or parallel. The same payload works through Compose, ComposeParallel, PredictCompose, pulse api compose, pulse api predict-compose, pulse_compose and pulse_predict (composed). It is gated by capability:compose_sweep.

{
  "sweep": {
    "axes": [
      {"name": "region", "values": ["north", "south"]},
      {"name": "cut",    "values": [10, 20, 30]}
    ],
    "mode": "grid",
    "label": "{{region}}_{{cut}}",
    "request": {
      "cohort": {"filename": "sales.pulse"},
      "filterers": [{"type": "FILTER_INCLUDE", "field": "region", "values": [{"$var": "region"}]}],
      "aggregations": [{"type": "AGG_SUM", "field": "revenue", "label": "rev_{{cut}}"}]
    }
  }
}

Axes and modes

An axis has a unique identifier name and a non-empty list of scalar values (string, number or boolean; numbers keep the digits as written). mode grid (default) is the Cartesian product, first axis slowest, so a 2-value and a 3-value axis give 6 slots. zip walks the axes in lockstep and every axis must be the same length.

Placeholders

{{axis}} inside a string splices the value as text; {"$var": "axis"} as a whole value keeps its JSON type. A $when guard may name an axis. Every axis must be referenced by the body, an authored label pattern or the overlays, and a placeholder that names no axis is refused. This is a purpose-built substitution, not an expression language and not the stored-template surface: it runs before the body is decoded.

The substituted body decodes strictly into a Request: an unknown key or a wrong-typed value fails with PULSE_SWEEP_INVALID, reason request_decode, even where a plain compose body would ignore it.

Labels

Without label, a slot is named by its axis=value pairs joined with _. An authored pattern may use any axis. The body must not set its own label, and a pattern that renders empty is refused. Labels stay unique across explicit and sweep slots (PULSE_COMPOSE_LABEL_COLLISION). Compose overlays name sweep slots by these labels.

Overlays

sweep.overlays is an array of compose overlay specs with placeholders. It is substituted per expanded slot and appended after the request’s own Overlays, in expansion order.

Limits and predict

MaxComposeSlots counts the expanded total (explicit plus sweep); an over-limit batch is PULSE_LIMIT_EXCEEDED before any slot runs (Tuning Limits). PredictCompose expands too and applies the same limit, slot-gate and label checks as the runtime. It adds a sweep summary to the result: axes (name and value count), mode, expanded_count (sweep slots only) and labels. A malformed sweep is PULSE_SWEEP_INVALID with details.field and details.reason.

Ranking

sweep.rank is {by, order, top}. by is a dot path into each sweep slot’s unshaped response (an object key, or on a list the element whose name or label equals the segment, never an index; for example components.aggregations.rev.n); order is asc (default) or desc; top keeps the first N. ComposedResponse.Ranking lists {label, value, rank} in rank order. Explicit slots are never ranked. A null value, or a path missing in one slot only, excludes that slot with a PULSE_SWEEP_RANK_PATH warning on its own response; a non-number, ambiguous or unresolved path refuses the batch with the same code (details.reason syntax, missing, not_number, ambiguous). A ranked slot always computes the ranked part even when its return excludes it, and ranking is never trimmed or rounded by the Compose return. Under --stream one trailing {"ranking": [...]} line follows the rows.

Known gap: a figure serialised with omitempty disappears at exactly 0 (for example residual_std_err), so that slot reads as missing and is excluded rather than ranked first (TODO #300).

Echo and return shaping

With Options.EchoRequest / --echo-request the echo carries the expanded slots the service ran (ComposedResponse.NormalizedRequest, not on the wire). The MCP pulse_compose default return of standard also fills a sweep body that sets no return; an authored one is never overwritten. The library default is unchanged.

See also: Parallel Compose, the payload schema.