0019. Declared capabilities remove targets that cannot serve a request¶
- Status: accepted
- Date: 2026-10-15
- Supersedes: none
- Change:
openspec/changes/add-capability-eligibility
Context¶
A model has limits the gateway does not know: a context window, and whether it supports tools or images.
Today a request that exceeds a window is sent anyway, rejected by the provider, and moved on by the
overflow fallback, which costs a round trip and a recorded failure. A request that uses tools, sent to a
model without tool support, may fail or be answered without the tools. The planning step (ADR 0008, 0017,
0018) already removes targets before the algorithm runs; a target that cannot serve the request belongs
there. Two things make it delicate: the gateway's prompt estimate errs high on purpose, so using it to
exclude targets would wrongly remove ones that fit, and declared data can be stale.
Decision¶
An endpoint may declare context_window, tools and vision; undeclared means capable. In the planning
step, after classes and policy and before health, a target is removed for a request when none of its
endpoints can serve it. A removal is made only on certainty: a declared false, or a prompt estimate that
errs low (not high) and still exceeds every declared window. When nothing can serve the request, it is
refused with 400 no_capable_target and a named reason before any upstream call. Capability only
removes, is recorded on the decision and counted, and capability proposals from public data are only ever
suggestions (ADR 0014).
Alternatives considered: relying on failure and the overflow fallback (the status quo; it costs a failed
call and cannot catch missing tool support); the existing high estimate (it excludes targets that fit,
which is the costly direction); probing providers for limits at runtime (extra network on the serving
path, against ADR 0014's spirit); learning limits from failures (nondeterministic and slow to correct);
failing open when nothing is capable (it sends a request known to fail upstream, unlike health, where the
signal is soft).
Consequences¶
Doomed requests stop reaching targets that cannot take them, silent degradation from dropped tools becomes
a clear error, and the decision log says which target was removed and why. A stale or wrong declaration
refuses requests until corrected, so the message names it and the importer reports drift. Borderline
prompts still cost one failed call, which is the price of never removing a target that could have served.
Endpoint-level skipping inside a target remains a later step. Decisions gain a stored incapable_targets
field (ledger schema v6) holding configuration names only.