Outcomes and reasons
Two outcomes, a closed set of reasons, and recovery on an axis of its own.
Two outcomes, not four
useful · not_useful
The response either gave the buyer what they wrote down before paying, or it did not.
Earlier releases had four labels — used, retried, discarded, failed — and three of them were answering a different question. Whether you retried, went elsewhere or gave up is what happened after a failure, not a third kind of outcome. Collapsing that back into two is what makes the arithmetic work: cost per useful result is spend divided by the count of useful, and everything spent reaching not_useful is waste, however it was recovered.
unlabeled is a third state but not an outcome. It is the state before a judgment, kept distinct so an unjudged call is never counted as a judgment either way.
Sending an old label is refused by name
The wire schema went from 1 to 2 at this change. A submission carrying used or discarded is told what replaced it rather than handed a bare "invalid option", and a "schema": 1 body is refused outright rather than read under the new vocabulary — the old labels do not map onto the new ones without inventing a reason nobody recorded.
Reasons
no_response · empty · malformed · wrong · stale · insufficient
Required on not_useful. Refused on useful — "it worked" is not a finding about anything, and storing one would be noise.
A closed set, so reasons aggregate across thousands of calls instead of each describing one.
| Reason | The response… |
|---|---|
no_response | never arrived |
empty | arrived with nothing in it |
malformed | was not the shape it promised |
wrong | was well-formed and incorrect |
stale | was correct once |
insufficient | was correct and not enough to use |
wrong, empty and malformed are kept apart deliberately. The difference between an endpoint that is broken and one that is lying is a different fact about a seller, and a single failure rate throws it away.
Recovery
none · retried_same · went_elsewhere · abandoned
What the buyer did next. Optional everywhere, and never an outcome: retrying and going elsewhere are both recovery from the same failure, and both cost money.
Keeping this on its own axis is what lets you ask the question that actually matters — not just how often an endpoint fails, but what its failures cost you in second attempts.
Notes
note is free text, up to 500 characters, alongside the code rather than instead of it. It carries the specifics without widening the closed set.
`note` and `taskClass` are published verbatim
Both are sent as written when review posting is on. Keep them generic; never put a customer name, an internal identifier, a prompt or a key in either. See Privacy.