Why the generated-tool model changes the failure mode
Your agent never calls a hand-written client through Gecko — it calls tools generated from the spec. Tool generation is a pure function of the surface: same spec in, same tools out. If the source of truth moves, the tools move with it — you re-comprehend the surface instead of hand-editing a client. Gecko knows which surface a tool came from, down to the revision:surface_rev; any edit bumps it. It’s a content fingerprint stamped on
the cached comprehension and on every correctness record in the corpus.

The stay-correct loop
1
Notice — detector shipped, scheduler not built
Re-fetch the spec, or re-simulate a known-good action. Doing this on a cadence is the piece we’re building.
2
Re-fingerprint — shipped
Recompute
surface_rev. Unchanged? Nothing happened, stop.3
Re-ingest — shipped
Run the same comprehension engine on the new spec.
4
Diff — report not built
Old
surface_rev vs new: what params were added, removed, renamed, retyped; what endpoints moved.5
Regenerate — shipped
Emit the new tool defs from the new surface.
The second signal: drift the spec never announced
Providers don’t always tell you. The surface can stay byte-identical while the behaviour underneath moves — a tightened validation, a re-deployed program, a changed default.surface_rev sees none of that.
So Gecko keeps a second, behavioural signal: the categorical corpus.
A run can append its outcome to a series — status class, revert family plus public error
code, compute units, slot, network category, and a values-free recipe hash. Never the
response body, a param value, a pubkey, an amount, a log line, or a token. That’s not a
policy: the writer is a fail-closed allowlist, closed-vocabulary and audited.
Recording is opt-in and the default is record nothing. It takes an explicit
record_to on the run. Gecko does not silently observe your traffic — and it can’t,
because in the normal path your agent calls the API directly and Gecko is not in the data
path at all.