Over an API you cannot touch logits, so everyone enforces structured output with a retry loop: emit four thousand tokens, parse, fail, throw all of it away, pay again.
A model writing "esc into a field whose schema allows only open, closed or pending has already failed. Everything after that character is certain waste, and the retry loop pays for all of it before noticing.
Ordinary validation asks whether a finished value conforms. This asks whether any completion of a half-written value could — reasoning about a set of futures rather than a single present. One asymmetry governs the whole design: a wrong "keep going" costs a few tokens, a wrong "abort" kills a generation that was fine.
Chunks land mid-string, mid-escape, two of four hex digits into a \u sequence. Every partial state is representable.
No permitted enum value starts with what has been written? Provably dead, three characters in.
01, +1, 1. and .5 are rejected. Passing a document JSON.parse then throws on is the worst failure available.
Every no-false-positive case is streamed at every chunk size from 1 to full length.
Full prefix matching needs Brzozowski derivatives. It extracts an anchored literal run and gives up otherwise.
Mid-number bound checks are opt-in, because 150e-1 is 15 and no prefix provably exceeds a maximum.
From examples/benchmark.ts, on a realistic extraction schema:
No $ref, $defs, if/then/else or not — resolve references before handing the schema over. And two of the benchmark cases save nothing: a missing required property is not provable until the closing brace. Early abort only helps when the evidence arrives early. It usually does.
The README goes deeper, and the tests are the honest documentation. Happy to walk through the trade-offs.