enabled: false touches: [crates/generation/src/candidate_crate.rs] depends-on: [floors-table] #
cutoff-reporting: separate replies cut off at the token cap from replies that were complete and failed to compile #
A batch's report says how many candidates compiled and drops the rest into one count. Two
different things sit in that count. A reply that ended because the model reached the token
cap (crates/generation/src/model.rs MAX_COMPLETION_TOKENS, 12000, ending with
finish_reason: "length" on the gateway or stop_reason: "max_tokens" on the Claude API)
stops in the middle of a function and is a certain compile failure that says nothing about
the model's answer. A reply that finished and still failed to compile is a wrong or
malformed answer. The cap was raised from 8000 to 12000 for exactly this: in one 32-candidate
batch four replies were cut off and counted as failures. Today the only way to tell the two
apart is to read each candidate's stop_reason in the run's candidate listing by hand
(docs/model-first-cost.md → "Why candidates fail to compile"). A batch that loses half its
candidates to the cap reads the same as one whose model cannot write the function, and an
unattended run then draws the wrong conclusion from a flat batch. This spec counts them
apart.
Where. crates/generation/src/candidate_crate.rs, the one file that holds both
Candidate outcomes and the compile probe. probe_batch is the single call every batch
binary makes after drawing candidates, and it already has the candidates and the probe
outcomes side by side, so the count goes there and no *_real_model_run binary changes.
FailureTally { drawn, compiled, cut_off, complete_failed }, allusize, andfailure_tally(candidates, outcomes) -> BTreeMap<String, FailureTally>, keyed by the candidate'smodel(unknownwhen it has none: a hand-written candidate). Outcomes are matched to candidates by name.- A candidate is cut off when its
stop_reasonislengthormax_tokens(compared without case), the two names the two providers use for the token cap, and it did not compile. A candidate that compiled counts as compiled whatever its stop reason. Any other candidate that did not compile is complete and failed to compile. A candidate with nostop_reason(a CLI completion reports none, asCandidate::stop_reasonsays) is counted as complete, since nothing says otherwise; the report says so in a comment and the caveat below. compiled + cut_off + complete_failed == drawnfor every model, and a unit test asserts it.probe_batchprints one line per model after probing, to stdout with the batch's other progress lines:compile outcome: <model>: <compiled> compiled, <cut_off> cut off at the token cap, <complete_failed> complete and failed to compile, of <drawn>. The wording avoids the shapesbench/ledger.pyreads (<model>: A/B candidate(s) compiled,timing: ...,... received, K completion tokens,... scored, best ...), so the ledger line a batch writes is unchanged.
The token cap, the probe, the compiler messages, write_compile_errors and its
_status.txt layout stay as they are. Nothing here retries a cut-off candidate, raises the
cap or changes what counts as admitted; a cut-off candidate is still dropped from scoring.
The report only stops calling it a model failure.
Tests. Unit tests in the file's existing mod tests, on in-memory Candidate and
ProbeOutcome values with no compiler run: a mixed batch of two models with each of the
four outcomes; both spellings of the cap; a capped candidate that compiled; no stop_reason;
an outcome with no matching candidate name (ignored); an empty batch (an empty map, no
line printed).
Gates. cargo test -p generation then cargo test --workspace, on a build lane
(bench/on-lane.sh --lane-only), never on the development box. This spec runs no model
batch and adds no generated function, so it has no provenance record and no floor.
Left to a hand change. Teaching bench/ledger.py to carry the two counts into a ledger
line, so an announcement can say how many candidates the cap cost, edits bench/, which
bench/land.sh refuses in a track. It is listed in docs/autonomous-loop.md → "Hand
changes still needed".
Caveat. A candidate from the Claude CLI (claude-code) has no stop_reason, so a
cut-off reply from that source is counted as complete and failed. This spec does not fix
that: the CLI reports nothing to read. Recording it here keeps a claude-code batch's
"complete and failed" number from being over-read.
Files. touches is the one source file (its tests are in it). It shares
crates/generation/ with every generated-function spec and with floors-table, so it
follows the latter.