Authoring Σ_pol & Σ_flow for this host
This host routes one OpenAI-compatible call to the best provider for it. You
do not pick a provider or a model name — you submit a policy (a piece of
data) that says how to choose, and the host evaluates it over its live catalog
(prices, benchmarks, latency, breakers) and picks. Two languages:
- Σ_pol (
policy_ir) — decides which model serves one call: a filter
(who qualifies) → a score (rank the survivors) → a selector (pick / cascade).
- Σ_flow (
flow_ir) — decides how several calls compose: a DAG of nodes,
each node carrying its own Σ_pol policy.
Both are data: serializable JSON arrays, hashable, admitted before they run.
There are no loops, no I/O, no side effects — a term decides, it does not do.
How to send a policy
POST /v1/chat/completions (OpenAI-compatible). Put the term in policy_ir
(or flow_ir). The model field is ignored for selection when policy_ir is
present — the policy drives the choice.
{
"model": "policy:auto",
"messages": [{"role": "user", "content": "..."}],
"policy_ir": ["policy",
["and", ["meets_req"], ["not", ["is", "disabled"]]],
["add", ["scale", 0.7, ["normalize", ["field", "bench_intelligence"]]],
["scale", 0.3, ["neg", ["normalize", ["field", "price_out"]]]]],
["argmax"], ["id"], ["always", {"action": "next_candidate"}]]
}
The feedback loop — author → preview → run → read
Every step is a request with the same Authorization: Bearer <key> you used to fetch this guide. You never have to fly blind:
Start from an intent template whenever it fits. GET /x/policy/templates
lists the blessed choices. Compile one with
POST /x/policy/templates/{id} (for example
{"family":"glm-5.2","provider_strategy":"ordered"} against
cheapest-family, or agent for a tool-running autonomous client). Author
raw policy_ir only when the templates cannot
express the intent. The published default template is byte-for-byte
equivalent after normalization to the policy used when an
OpenAI-compatible request sends no policy_ir.
The published agent template is likewise identical to profile:agent and
owns its first-token/per-attempt budgets, trust gate, and fast-fallback plan;
callers should select the profile instead of copying its raw term.
Admit & identify — no spend. POST /x/policy/normalize {policy_ir} → {policy_ir, fingerprint, version}. A 400 here pinpoints what's invalid (unknown op, undeclared field, …) so you fix the term before paying.
Preview the ranking — no spend. POST /x/rank {policy_ir} → {ranked, rejected}: the candidates this host would admit and how it orders them, plus the ones it filtered out, each with the reason it failed. This is how you see what your policy does without a single call.
Run it for real. POST /v1/chat/completions with policy_ir (or flow_ir) + messages (the example above). A real call — real spend.
Read how it routed. The response carries x_router — your debugger for what actually happened:
x_router field |
what it tells you |
provider · served_model_id |
the model that answered |
served_by |
the executed route — the marketplace peer, or the provider for a direct route |
cost_usd · price_in · price_out |
what the call cost |
policy_fingerprint |
the identity of the policy that ran (matches /x/policy/normalize) |
decision_trace |
ranked (the candidates considered) and decision_path (the real fallback attempts — which routes were tried, and ok or the error each hit) |
session_acc |
running totals, when the call carries a session |
decision_trace.decision_path is the fallback story — exactly which routes were tried and why it fell through; served_by + cost_usd say where it landed and what it cost. Refine the term and loop.
The same loop for a Σ_flow (a DAG of nodes, each with its own policy_ir — see Σ_flow below):
- Author the
flow_ir.
- Admit & identify — no spend.
POST /x/flow/normalize {flow_ir} → {flow_ir, fingerprint, version}.
- Preview — no spend. A flow has no single ranking (each node routes on its own policy), so preview a node by sending its
policy_ir to POST /x/rank.
- Run it for real.
POST /v1/chat/completions with flow_ir + messages.
- Read how it routed.
x_router.decision_trace carries flow_nodes — one entry per node with its node id, provider / served_by, tokens, latency, and the node's own decision_path (its fallback attempts). That's the per-node debugger: you see which node ran what, where each landed, and any fallback inside a node.
The Σ_pol term, exactly
The operators below are the sigma-pol/v2 signature. The normative
grammar is the core spec (core/docs/SIGMA-POL.md) — this guide mirrors it
for authoring convenience; on a major version bump, regenerate against the
spec. The field vocabulary, by contrast, is injected live from the host
(see Field vocabulary below), so it never drifts from what the host serves.
A policy is a 6-element array — fill the three middle slots, keep the rest as-is:
["policy", <Pred>, <Scorer>, <Selector>, ["id"], ["always", {"action":"next_candidate"}]]
filter score pick xform fail-plan
<Pred> — who qualifies (joined by AND, default-deny)
Always start the filter with the host floor, then AND your conditions:
["and", ["meets_req"], ["not", ["is", "disabled"]], <your conditions...>]
| Want |
Term |
| Numeric threshold |
["cmp", "<field>", "<rel>", <number>] — rel ∈ le lt ge gt eq ne |
| Boolean is true |
["is", "<bool_field>"] |
| Boolean is false |
["not", ["is", "<bool_field>"]] |
| Has a capability |
["has_cap", "supports_tools"] (model serves it; e.g. supports_json_mode) |
| One model family |
["family_eq", "gpt-5.5"] |
| Set of families |
["or", ["family_eq","gpt-5.5"], ["family_eq","kimi-k2.6"]] |
| One provider |
["provider_eq", "openrouter"] — route by who serves; set: provider_in, exclude: not/provider_not_in (e.g. drop a marketplace provider) |
| Tier exactly |
["tier_eq", "partner"] |
| Tier at least |
["min_tier", "marketplace"] (order fallback < marketplace < partner) |
| Specific seller/peer |
["served_by_eq", "<peer-id>"] — executed route (marketplace peer, or provider for a direct route); set: served_by_in, exclude: served_by_not_in |
| In the top N by a benchmark |
["cmp", "<field>_rank", "le", N] (e.g. bench_intelligence_rank) |
| Either of two |
["or", <predA>, <predB>] |
Top-N is a cmp on a _rank field, not a special op — the host
precomputes catalog ranks (1 = best). The intersection of two shortlists
("top-5 on intelligence AND top-5 on coding") is just the and of two cmps.
<Scorer> — rank the survivors (higher wins)
Score on the raw observable fields (the same names the filter gates on; see
Field vocabulary) via ["field", "<name>"], then weight and sum:
["add", ["scale", 0.6, ["normalize", ["field", "bench_coding"]]],
["scale", 0.4, ["neg", ["normalize", ["field", "price_in"]]]]]
["field", "<name>"] — a raw field's value: price_in, price_out,
latency_ms, tok_s, success_rate, context, bench_intelligence, … (any
Num field from the vocabulary).
["normalize", base] — min-max the field across the live population to [0,1]
(mix fields on different scales only after normalizing).
["neg", base] — invert (1 − base), so "lower is better" (cheaper, faster
latency) scores higher.
["scale", <weight>, base] weights a term; ["add", …] sums them.
["lit", <num>] a constant; ["clamp", <lo>, <hi>, base] bounds a score.
- No scoring (pure filter):
["zero"].
- Demote breaker-open instead of excluding: wrap the scorer in
["gate", ["not", ["is", "breaker_open"]], <scorer>].
Score on raw fields, not on composite atoms. The signature also defines
heuristic scorer atoms (cost, speed, quality, partner, free_credit)
that fold fields + request knobs (max_cost_usd, max_latency_ms, token
estimates) into one number with fixed host defaults (spec §5.2). They are
opaque and host-tuned — author with the explicit ["field", …] form above so
the ranking is visible and portable.
<Selector> — pick / cascade
["argmax"] — deterministic best (the default; "subzero converges").
["top_k", N, ["argmax"]] — keep the N best as the failover cascade.
["sample", <temp>] — seeded, reproducible stochastic pick (rank-geometric;
temp=0 ≡ argmax, larger → more uniform). Used for greybox divergence.
["prefer", <Pred>, <Selector>] — strict stable priority: every matching
candidate precedes every non-match, while the inner selector still orders
candidates inside both groups. Nest it for lexicographic provider order; an
outer prefer(not(is("breaker_open")), ...) keeps unhealthy routes last.
Worked examples (copy, adjust the numbers)
Cheapest model that's decent and not over a price ceiling:
["policy",
["and", ["meets_req"], ["not", ["is", "disabled"]],
["cmp", "bench_intelligence", "ge", 0.5], ["cmp", "price_out", "le", 10]],
["neg", ["normalize", ["field", "price_out"]]],
["argmax"], ["id"], ["always", {"action": "next_candidate"}]]
Cheapest in the top-5 on intelligence ∩ top-5 on coding (the host's Σ_pol
example #1):
["policy",
["and", ["meets_req"], ["not", ["is", "disabled"]],
["cmp", "bench_intelligence_rank", "le", 5],
["cmp", "bench_coding_rank", "le", 5]],
["neg", ["normalize", ["field", "price_in"]]],
["argmax"], ["id"], ["always", {"action": "next_candidate"}]]
Top-3 by combined benchmarks, as a cascade (example #2):
["policy",
["and", ["meets_req"], ["not", ["is", "disabled"]]],
["add", ["scale", 1, ["normalize", ["field", "bench_intelligence"]]],
["scale", 1, ["normalize", ["field", "bench_coding"]]],
["scale", 1, ["normalize", ["field", "bench_agentic"]]]],
["top_k", 3, ["argmax"]], ["id"], ["always", {"action": "next_candidate"}]]
Quality-leaning blend, partners only (tier gated in the filter, so the score
is just benchmark vs latency):
["policy",
["and", ["meets_req"], ["not", ["is", "disabled"]], ["tier_eq", "partner"]],
["add", ["scale", 0.7, ["normalize", ["field", "bench_intelligence"]]],
["scale", 0.3, ["neg", ["normalize", ["field", "latency_ms"]]]]],
["argmax"], ["id"], ["always", {"action": "next_candidate"}]]
Field vocabulary
A policy observes a candidate only through named fields (used by cmp,
is, field). The authoritative list — the core vocabulary (core, on every
conforming host) plus this host's registered extensions (host) — is injected
live below from the host's own schema (GET /x/fields), so it always matches
what the host actually serves rather than a copy that can drift.
Categorical attributes, matched by their own ops (not in the table above):
model_family (family_eq) and tier (tier_eq, min_tier; order
fallback < marketplace < partner).
Benchmarks (bench_*, Num in 0–1) each have a _rank companion (1 = best)
for in-top-N gating: a missing benchmark reads as 0 and a missing _rank as
huge, so a family without it is correctly outside every top-N. Marketplace-only
families (no OpenRouter data) have empty benchmarks — gate on price/latency for
those.
Defaults when a field is absent are deliberately conservative (prices +inf
so a missing price fails a ceiling; tok_s/credits 0;
success_rate 1; bools false) — see Rules below.
Σ_flow — composing several calls
A flow is ["flow", { <id>: <node>, ... }] with exactly one input and one
output node; every llm node carries a system prompt, a policy (a full
Σ_pol term), and an inputs list of the node ids it consumes. It is a DAG
(acyclic), each node runs once. Edges are pull-model: b.inputs = ["a"] means
a → b. A node with two inputs is a fusion/synthesizer.
["flow", {
"u": {"kind": "input"},
"a": {"kind": "llm", "system": "Answer concisely.",
"policy": ["policy", ["and", ["meets_req"], ["not", ["is","disabled"]]],
["field","bench_intelligence"], ["argmax"], ["id"], ["always", {"action":"next_candidate"}]],
"inputs": ["u"]},
"b": {"kind": "llm", "system": "Answer rigorously, show steps.",
"policy": ["policy", ["and", ["meets_req"], ["not", ["is","disabled"]]],
["neg",["normalize",["field","price_in"]]], ["argmax"], ["id"], ["always", {"action":"next_candidate"}]],
"inputs": ["u"]},
"f": {"kind": "llm", "system": "Synthesize the single best answer from the drafts.",
"policy": ["policy", ["and", ["meets_req"], ["not", ["is","disabled"]]],
["add",["scale",0.7,["field","bench_intelligence"]],["scale",0.3,["neg",["normalize",["field","price_in"]]]]], ["argmax"], ["id"], ["always", {"action":"next_candidate"}]],
"inputs": ["a", "b"]},
"out": {"kind": "output", "inputs": ["f"]}
}]
POST it as flow_ir. Optional per-node template with $1,$2,… overrides how
a multi-input node joins its predecessors' outputs.
Rules that keep a policy valid
- Defaults are conservative. A candidate with no declared price does not
pass a
price_out ceiling (price_in/out default to +inf). Enforce spend
with a hard cmp ceiling on price_* in the filter — a scorer only ranks
softly, it does not bound anything.
- Score on raw fields, not the composite scorer atoms. Author scores as
["field", "<name>"] (+ normalize/neg/scale/add). Don't use the bare
cost / speed / quality / partner / free_credit scorer atoms: they
bake request knobs and host defaults into one opaque number.
- Always include
["meets_req"] and ["not", ["is", "disabled"]] in the
filter — the host's envelope ANDs its own floor on too, so you can only
narrow what the host allows, never widen it.
- Limits: term depth ≤ 64, ≤ 4096 nodes; flow ≤ 256 nodes, in-degree ≤ 32.
- Numbers must be finite (no NaN/Inf); integers render without a decimal.
- You can target a specific seller —
["served_by_eq", "<peer>"] pins the
executed route (a marketplace peer, or the provider for a direct route), with
served_by_in / served_by_not_in as the set sugar — but prefer gating on
properties over identities: ["cmp", "reputation_score", "ge", 40] or a
success_rate weight keeps working as peers come and go, whereas a pinned peer
id rots. Reach for served_by_eq for a trusted-peer allowlist, not as the
default; for the rest, gate on families and fields.
(The live model/provider catalog is injected here when this file is downloaded
from the host's Catalog tab. Without it, target the field vocabulary above
and confirm families with POST /x/rank.)
1---2name: sigma-policy-author3description: Author Σ_pol policies and Σ_flow flows for this LLM policy host. Load this file into any assistant and it can generate valid `policy_ir` / `flow_ir` terms (as JSON) to POST against the host's OpenAI-compatible endpoint — the live model/provider catalog is embedded below so the policies target models this host actually serves.4---56# Authoring Σ_pol & Σ_flow for this host78This host routes one OpenAI-compatible call to the best provider for it. **You9do not pick a provider or a model name** — you submit a *policy* (a piece of10data) that says how to choose, and the host evaluates it over its live catalog11(prices, benchmarks, latency, breakers) and picks. Two languages:1213- **Σ_pol** (`policy_ir`) — decides *which model serves one call*: a filter14 (who qualifies) → a score (rank the survivors) → a selector (pick / cascade).15- **Σ_flow** (`flow_ir`) — decides *how several calls compose*: a DAG of nodes,16 each node carrying its own Σ_pol policy.1718Both are **data**: serializable JSON arrays, hashable, admitted before they run.19There are no loops, no I/O, no side effects — a term *decides*, it does not *do*.2021## How to send a policy2223`POST /v1/chat/completions` (OpenAI-compatible). Put the term in `policy_ir`24(or `flow_ir`). The `model` field is ignored for selection when `policy_ir` is25present — the policy drives the choice.2627```json28{29 "model": "policy:auto",30 "messages": [{"role": "user", "content": "..."}],31 "policy_ir": ["policy",32 ["and", ["meets_req"], ["not", ["is", "disabled"]]],33 ["add", ["scale", 0.7, ["normalize", ["field", "bench_intelligence"]]],34 ["scale", 0.3, ["neg", ["normalize", ["field", "price_out"]]]]],35 ["argmax"], ["id"], ["always", {"action": "next_candidate"}]]36}37```3839## The feedback loop — author → preview → run → read4041Every step is a request with the **same** `Authorization: Bearer <key>` you used to fetch this guide. You never have to fly blind:42431. **Start from an intent template whenever it fits.** `GET /x/policy/templates`44 lists the blessed choices. Compile one with45 `POST /x/policy/templates/{id}` (for example46 `{"family":"glm-5.2","provider_strategy":"ordered"}` against47 `cheapest-family`, or `agent` for a tool-running autonomous client). Author48 raw `policy_ir` only when the templates cannot49 express the intent. The published `default` template is byte-for-byte50 equivalent after normalization to the policy used when an51 OpenAI-compatible request sends no `policy_ir`.52 The published `agent` template is likewise identical to `profile:agent` and53 owns its first-token/per-attempt budgets, trust gate, and fast-fallback plan;54 callers should select the profile instead of copying its raw term.552. **Admit & identify — no spend.** `POST /x/policy/normalize` `{policy_ir}` → `{policy_ir, fingerprint, version}`. A `400` here pinpoints what's invalid (unknown op, undeclared field, …) so you fix the term before paying.563. **Preview the ranking — no spend.** `POST /x/rank` `{policy_ir}` → `{ranked, rejected}`: the candidates this host would admit and how it orders them, plus the ones it filtered out, each with the `reason` it failed. This is how you see *what your policy does* without a single call.574. **Run it for real.** `POST /v1/chat/completions` with `policy_ir` (or `flow_ir`) + `messages` (the example above). A real call — real spend.585. **Read how it routed.** The response carries **`x_router`** — your debugger for what actually happened:5960 | `x_router` field | what it tells you |61 |---|---|62 | `provider` · `served_model_id` | the model that answered |63 | `served_by` | the *executed route* — the marketplace peer, or the provider for a direct route |64 | `cost_usd` · `price_in` · `price_out` | what the call cost |65 | `policy_fingerprint` | the identity of the policy that ran (matches `/x/policy/normalize`) |66 | `decision_trace` | `ranked` (the candidates considered) **and** `decision_path` (the real fallback attempts — which routes were tried, and `ok` or the error each hit) |67 | `session_acc` | running totals, when the call carries a session |6869 `decision_trace.decision_path` is the fallback story — exactly which routes were tried and why it fell through; `served_by` + `cost_usd` say where it landed and what it cost. Refine the term and loop.7071**The same loop for a Σ_flow** (a DAG of nodes, each with its own `policy_ir` — see *Σ_flow* below):72731. **Author** the `flow_ir`.742. **Admit & identify — no spend.** `POST /x/flow/normalize` `{flow_ir}` → `{flow_ir, fingerprint, version}`.753. **Preview — no spend.** A flow has no single ranking (each node routes on its own policy), so preview a node by sending *its* `policy_ir` to `POST /x/rank`.764. **Run it for real.** `POST /v1/chat/completions` with `flow_ir` + `messages`.775. **Read how it routed.** `x_router.decision_trace` carries **`flow_nodes`** — one entry per node with its `node` id, `provider` / `served_by`, tokens, latency, and the node's own `decision_path` (its fallback attempts). That's the per-node debugger: you see which node ran what, where each landed, and any fallback inside a node.7879## The Σ_pol term, exactly8081> The operators below are the `sigma-pol/v2` signature. The **normative82> grammar is the core spec** (`core/docs/SIGMA-POL.md`) — this guide mirrors it83> for authoring convenience; on a major version bump, regenerate against the84> spec. The **field vocabulary**, by contrast, is injected live from the host85> (see *Field vocabulary* below), so it never drifts from what the host serves.8687A policy is a 6-element array — fill the three middle slots, keep the rest as-is:8889```90["policy", <Pred>, <Scorer>, <Selector>, ["id"], ["always", {"action":"next_candidate"}]]91 filter score pick xform fail-plan92```9394### `<Pred>` — who qualifies (joined by AND, default-deny)9596Always start the filter with the host floor, then AND your conditions:9798```json99["and", ["meets_req"], ["not", ["is", "disabled"]], <your conditions...>]100```101102| Want | Term |103|---|---|104| Numeric threshold | `["cmp", "<field>", "<rel>", <number>]` — rel ∈ `le lt ge gt eq ne` |105| Boolean is true | `["is", "<bool_field>"]` |106| Boolean is false | `["not", ["is", "<bool_field>"]]` |107| Has a capability | `["has_cap", "supports_tools"]` (model serves it; e.g. `supports_json_mode`) |108| One model family | `["family_eq", "gpt-5.5"]` |109| Set of families | `["or", ["family_eq","gpt-5.5"], ["family_eq","kimi-k2.6"]]` |110| One provider | `["provider_eq", "openrouter"]` — route by *who serves*; set: `provider_in`, exclude: `not`/`provider_not_in` (e.g. drop a marketplace provider) |111| Tier exactly | `["tier_eq", "partner"]` |112| Tier at least | `["min_tier", "marketplace"]` (order `fallback < marketplace < partner`) |113| Specific seller/peer | `["served_by_eq", "<peer-id>"]` — executed route (marketplace peer, or provider for a direct route); set: `served_by_in`, exclude: `served_by_not_in` |114| **In the top N by a benchmark** | `["cmp", "<field>_rank", "le", N]` (e.g. `bench_intelligence_rank`) |115| Either of two | `["or", <predA>, <predB>]` |116117> **Top-N is a `cmp` on a `_rank` field**, not a special op — the host118> precomputes catalog ranks (1 = best). The **intersection of two shortlists**119> ("top-5 on intelligence AND top-5 on coding") is just the `and` of two cmps.120121### `<Scorer>` — rank the survivors (higher wins)122123Score on the **raw observable fields** (the same names the filter gates on; see124*Field vocabulary*) via `["field", "<name>"]`, then weight and sum:125126```json127["add", ["scale", 0.6, ["normalize", ["field", "bench_coding"]]],128 ["scale", 0.4, ["neg", ["normalize", ["field", "price_in"]]]]]129```130131- `["field", "<name>"]` — a raw field's value: `price_in`, `price_out`,132 `latency_ms`, `tok_s`, `success_rate`, `context`, `bench_intelligence`, … (any133 Num field from the vocabulary).134- `["normalize", base]` — min-max the field across the live population to [0,1]135 (mix fields on different scales only after normalizing).136- `["neg", base]` — invert (`1 − base`), so "lower is better" (cheaper, faster137 latency) scores higher.138- `["scale", <weight>, base]` weights a term; `["add", …]` sums them.139- `["lit", <num>]` a constant; `["clamp", <lo>, <hi>, base]` bounds a score.140- No scoring (pure filter): `["zero"]`.141- Demote breaker-open instead of excluding: wrap the scorer in142 `["gate", ["not", ["is", "breaker_open"]], <scorer>]`.143144> **Score on raw fields, not on composite atoms.** The signature also defines145> heuristic scorer atoms (`cost`, `speed`, `quality`, `partner`, `free_credit`)146> that fold fields + request knobs (`max_cost_usd`, `max_latency_ms`, token147> estimates) into one number with fixed host defaults (spec §5.2). They are148> opaque and host-tuned — author with the explicit `["field", …]` form above so149> the ranking is visible and portable.150151### `<Selector>` — pick / cascade152153- `["argmax"]` — deterministic best (the default; "subzero converges").154- `["top_k", N, ["argmax"]]` — keep the N best as the failover cascade.155- `["sample", <temp>]` — seeded, reproducible stochastic pick (rank-geometric;156 `temp=0` ≡ argmax, larger → more uniform). Used for greybox divergence.157- `["prefer", <Pred>, <Selector>]` — strict stable priority: every matching158 candidate precedes every non-match, while the inner selector still orders159 candidates inside both groups. Nest it for lexicographic provider order; an160 outer `prefer(not(is("breaker_open")), ...)` keeps unhealthy routes last.161162## Worked examples (copy, adjust the numbers)163164**Cheapest model that's decent and not over a price ceiling:**165```json166["policy",167 ["and", ["meets_req"], ["not", ["is", "disabled"]],168 ["cmp", "bench_intelligence", "ge", 0.5], ["cmp", "price_out", "le", 10]],169 ["neg", ["normalize", ["field", "price_out"]]],170 ["argmax"], ["id"], ["always", {"action": "next_candidate"}]]171```172173**Cheapest in the top-5 on intelligence ∩ top-5 on coding** (the host's Σ_pol174example #1):175```json176["policy",177 ["and", ["meets_req"], ["not", ["is", "disabled"]],178 ["cmp", "bench_intelligence_rank", "le", 5],179 ["cmp", "bench_coding_rank", "le", 5]],180 ["neg", ["normalize", ["field", "price_in"]]],181 ["argmax"], ["id"], ["always", {"action": "next_candidate"}]]182```183184**Top-3 by combined benchmarks, as a cascade** (example #2):185```json186["policy",187 ["and", ["meets_req"], ["not", ["is", "disabled"]]],188 ["add", ["scale", 1, ["normalize", ["field", "bench_intelligence"]]],189 ["scale", 1, ["normalize", ["field", "bench_coding"]]],190 ["scale", 1, ["normalize", ["field", "bench_agentic"]]]],191 ["top_k", 3, ["argmax"]], ["id"], ["always", {"action": "next_candidate"}]]192```193194**Quality-leaning blend, partners only** (tier gated in the filter, so the score195is just benchmark vs latency):196```json197["policy",198 ["and", ["meets_req"], ["not", ["is", "disabled"]], ["tier_eq", "partner"]],199 ["add", ["scale", 0.7, ["normalize", ["field", "bench_intelligence"]]],200 ["scale", 0.3, ["neg", ["normalize", ["field", "latency_ms"]]]]],201 ["argmax"], ["id"], ["always", {"action": "next_candidate"}]]202```203204## Field vocabulary205206A policy observes a candidate only through named **fields** (used by `cmp`,207`is`, `field`). The authoritative list — the core vocabulary (`core`, on every208conforming host) plus this host's registered extensions (`host`) — is injected209live below from the host's own schema (`GET /x/fields`), so it always matches210what the host actually serves rather than a copy that can drift.211212<!-- FIELD_VOCABULARY -->213214**Categorical** attributes, matched by their own ops (not in the table above):215`model_family` (`family_eq`) and `tier` (`tier_eq`, `min_tier`; order216`fallback < marketplace < partner`).217218**Benchmarks** (`bench_*`, Num in 0–1) each have a `_rank` companion (1 = best)219for in-top-N gating: a missing benchmark reads as 0 and a missing `_rank` as220huge, so a family without it is correctly outside every top-N. Marketplace-only221families (no OpenRouter data) have empty benchmarks — gate on price/latency for222those.223224Defaults when a field is absent are deliberately conservative (prices **+inf**225so a missing price fails a ceiling; `tok_s`/`credits` 0;226`success_rate` 1; bools false) — see *Rules* below.227228## Σ_flow — composing several calls229230A flow is `["flow", { <id>: <node>, ... }]` with exactly one `input` and one231`output` node; every `llm` node carries a `system` prompt, a `policy` (a full232Σ_pol term), and an `inputs` list of the node ids it consumes. It is a DAG233(acyclic), each node runs once. Edges are pull-model: `b.inputs = ["a"]` means234`a → b`. A node with two inputs is a fusion/synthesizer.235236```json237["flow", {238 "u": {"kind": "input"},239 "a": {"kind": "llm", "system": "Answer concisely.",240 "policy": ["policy", ["and", ["meets_req"], ["not", ["is","disabled"]]],241 ["field","bench_intelligence"], ["argmax"], ["id"], ["always", {"action":"next_candidate"}]],242 "inputs": ["u"]},243 "b": {"kind": "llm", "system": "Answer rigorously, show steps.",244 "policy": ["policy", ["and", ["meets_req"], ["not", ["is","disabled"]]],245 ["neg",["normalize",["field","price_in"]]], ["argmax"], ["id"], ["always", {"action":"next_candidate"}]],246 "inputs": ["u"]},247 "f": {"kind": "llm", "system": "Synthesize the single best answer from the drafts.",248 "policy": ["policy", ["and", ["meets_req"], ["not", ["is","disabled"]]],249 ["add",["scale",0.7,["field","bench_intelligence"]],["scale",0.3,["neg",["normalize",["field","price_in"]]]]], ["argmax"], ["id"], ["always", {"action":"next_candidate"}]],250 "inputs": ["a", "b"]},251 "out": {"kind": "output", "inputs": ["f"]}252}]253```254255POST it as `flow_ir`. Optional per-node `template` with `$1,$2,…` overrides how256a multi-input node joins its predecessors' outputs.257258## Rules that keep a policy valid259260- **Defaults are conservative.** A candidate with no declared price does *not*261 pass a `price_out` ceiling (`price_in/out` default to +inf). Enforce spend262 with a hard `cmp` ceiling on `price_*` in the filter — a scorer only ranks263 softly, it does not bound anything.264- **Score on raw fields, not the composite scorer atoms.** Author scores as265 `["field", "<name>"]` (+ `normalize`/`neg`/`scale`/`add`). Don't use the bare266 `cost` / `speed` / `quality` / `partner` / `free_credit` scorer atoms: they267 bake request knobs and host defaults into one opaque number.268- **Always include `["meets_req"]` and `["not", ["is", "disabled"]]`** in the269 filter — the host's envelope ANDs its own floor on too, so you can only270 *narrow* what the host allows, never widen it.271- **Limits:** term depth ≤ 64, ≤ 4096 nodes; flow ≤ 256 nodes, in-degree ≤ 32.272- **Numbers** must be finite (no NaN/Inf); integers render without a decimal.273- You *can* target a specific seller — `["served_by_eq", "<peer>"]` pins the274 executed route (a marketplace peer, or the provider for a direct route), with275 `served_by_in` / `served_by_not_in` as the set sugar — but prefer gating on276 *properties* over identities: `["cmp", "reputation_score", "ge", 40]` or a277 `success_rate` weight keeps working as peers come and go, whereas a pinned peer278 id rots. Reach for `served_by_eq` for a trusted-peer allowlist, not as the279 default; for the rest, gate on *families and fields*.280281---282283<!-- LIVE_CATALOG_TABLE -->284*(The live model/provider catalog is injected here when this file is downloaded285from the host's **Catalog** tab. Without it, target the field vocabulary above286and confirm families with `POST /x/rank`.)*