The eight recommendation types
What each detector looks for, the floors a slice has to clear, and which two of the eight can be applied as a configuration change.
On this page
Eight detectors run on each scan. Each looks for one shape of waste, over one grouping, with its own volume floor beneath which it declines to say anything.
The entries below follow one shape so they can be compared. The registry is closed: a type not listed here does not exist, and a detector that named an unregistered method or guard would fail at process startup rather than at the first scan.
Recommendations is the explanation these entries support.
At a glance
| Type | Grouped by | Apply? | Quality method | Guards |
|---|---|---|---|---|
routing-downgrade |
routing rule | actionable | shadow-grade-wilson-v1 |
cache-trajectory, retry-inflation |
cache-opportunity |
model | actionable | mechanism-byte-identical-v1 |
— |
prompt-caching |
model + system-prompt size | advisory | mechanism-byte-identical-v1 |
— |
provider-arbitrage |
model | advisory | shadow-grade-wilson-v1 |
— |
batch-eligibility |
model + feature | advisory | shadow-grade-wilson-v1 |
— |
output-cap |
routing rule | advisory | shadow-grade-wilson-v1 |
truncation-rate |
reasoning-effort |
routing rule | advisory | shadow-grade-wilson-v1 |
— |
dedup |
routing rule | advisory | mechanism-byte-identical-v1 |
— |
Actionable means the card carries a configuration candidate you can apply through the API.
Advisory means it carries a recipe instead, because the change lives outside this config —
in your provider’s console, your prompts, or your call sites. Asking to apply an advisory card
returns 422 with the recipe.
routing-downgrade
Traffic on a routing rule is concentrating in your most expensive tier while graded evidence says a cheaper tier answers it well enough.
Grouped by decision.rule.
Floors. At least 50 rows on the slice, and at least 60% of the slice’s spend on the top tier.
Impact method. shadow-measured-delta-v1: measured per-row deltas extrapolated by volume,
so ESTIMATED at best.
Guards. cache-trajectory blocks a change that would destroy cache hits the saving depends
on; retry-inflation blocks a cheaper tier whose retry rate would eat the saving.
What it costs first. Graded shadow rows, which means running
shadow duplication and paying for the duplicate
calls.
How it declines. With no single candidate tier. If a slice’s graded rows ran their shadow
duplicates at more than one tier, the pass rate is a blend and licenses nothing, and the card
says so.
cache-opportunity
A model’s traffic repeats often enough that a response cache rule would pay.
Grouped by decision.model_served. The model is the grouping because it is the lever’s
shape. A cache rule matches on endpoint, model, dialect, tier and dimensions, so a slice you
could not express as a rule is not a candidate however interesting its repeat rate.
Floors. At least 50 rows on the slice, at least 10 repeated requests among the keyable ones,
and an avoided spend of at least 2% of the slice or $0.01, whichever binds.
Impact method. cache-shadow-would-hit-v1, a counterfactual over rows that would have hit.
What it costs first. Nothing to detect. Turning caching on has its own trade-offs, in
caching.
prompt-caching
A stable prompt prefix is being re-sent often enough that the provider’s own prompt cache would pay for its write premium.
Grouped by model and system-prompt size. Grouping on size is what makes prefix stability structural rather than asserted: the ledger records how long a system prompt was, never what it said, and equal length is the strongest stability evidence it can offer. Floor. At least 20 rows. Advisory, because enrolling a prompt in a provider’s cache is a change to your requests. How it declines. Most often on the price table rather than on your traffic: if the snapshot records a model’s cache-write rate as zero, that cannot be distinguished from the rate being absent, and the detector says so instead of assuming.
provider-arbitrage
The same model is available from a provider your price table prices lower.
Grouped by model. Floor. At least 20 rows. Advisory, because adding a provider is a deployment change. How it declines. By pricing your recorded usage at each alternative and comparing. When the alternatives price identically, it says the switch would save about nothing rather than proposing it. Note that a price-table comparison is not a latency, availability or quality comparison.
batch-eligibility
A model-and-feature pair does enough non-streaming volume that a provider’s batch tier would apply.
Grouped by model and dim.feature. Feature is the grouping because it is the unit an
operator can actually answer about: nobody can confirm that “all non-streaming traffic”
tolerates a day’s delay, but they can answer for nightly-batch.
Floor. At least 20 non-streaming requests.
Advisory. Batch enrolment happens at the provider.
How it declines. Frequently with UNKNOWN rather than zero, and for a reason worth
understanding: a batch discount is provider policy, and the vendored catalog carries no batch
rate for any model. Rather than multiply by a number a marketing page quotes, the figure is
withheld and the reason names the fix: add an override keyed for the batch variant carrying
your provider’s rates, and the figure becomes computable.
output-cap
A routing rule’s completions are far shorter than the cap it allows, so a tighter max_tokens
would cut the tail without touching normal answers.
Grouped by decision.rule.
Floor. At least 30 rows.
Guard. truncation-rate blocks any proposal whose projected truncation share exceeds 10%,
with the arithmetic quoted. It blocks and never reprices: a saving bought by clipping answers is
a different product, not a smaller saving.
Advisory. The cap belongs at your call site.
How it declines. Including on data that cannot be right. If a rule’s rows record output
lengths above their own max_tokens without finishing on finish_reason=length, which no
provider produces, no cap is proposed from that distribution at all.
reasoning-effort
A rule’s output-side spend is mostly reasoning tokens, so a lower effort setting is worth considering.
Grouped by decision.rule.
Floors. At least 20 requests, and reasoning tokens at 25% or more of output-side tokens.
Advisory.
What to watch. Reasoning tokens are billed separately from output tokens and are a disjoint
bucket, so this detector is reading its own line item rather than a share of one.
dedup
The same question is being asked more than once inside a short window.
Grouped by decision.rule.
Floors. At least 20 rows on the slice, and at least 5 collapsible duplicates within a
10-minute window.
Impact method. evidence-scoped-v1.
Advisory, because the fix is in the caller rather than in the gateway.
What it rests on. Prompt hashes recorded on rows. A ledger with prompt redaction set to
none records no hash, and this detector has nothing to compare.
Why six of eight are advisory, and why that is not a gap
The two actionable types both change something this configuration owns: a routing rule and a cache rule. The other six change a provider enrolment, a prompt, or a parameter at your call site.
A card could have shipped an apply button for those anyway and failed at the end. Instead the
type declares itself advisory at registration, the card carries a recipe, and apply returns
422 with the recipe attached. The refusal arrives before you have made any plans around it.