# A clarify head, and a confidence that is not a hardcode

Measured 2026-08-08 on workpc, the same day and the same fixtures as
`2026-08-08-routing-heads-two-head.md` and `2026-08-08-slot-head-three-head.md`.
New script `gen_clarify.py`, new fixture `eval_fixture_clarify.jsonl`.

## A softmax has no clarify class

That sentence closed the two-head measurement. It is why the head's fixture was
88 cases and not 96. The eight `want_clarify` cases sat outside every number
measured, and the head had no way to produce the answer they wanted.

A fourth head is the answer. Clarify is not a value of intent. It is a second
question asked of the same pooled vector: can Maven act on this at all.

## The corpus had one class

Every row in `train_heads_slots.jsonl` was generated FOR an intent or a
destination. So every row is answerable by construction. A head trained on that
alone sees one class and learns to say yes.

`gen_clarify.py` makes the other class. Five shapes, ten topics. The shapes are
the gate's own reasons in `gateLLMDecision` plus the two the fixture carries:
bare noun, bare verb, demonstrative, deictic time, dangling reference.

**The agreement filter that worked for destination cannot work here.**
`routeGrammar` has no clarify value. So the router always names an intent, and
any generated line always agrees with itself. The second pass is a judge
instead. Gemma is asked, without seeing the label, whether Maven would have to
ask a question back.

## The first judge was worthless and the second was measured

The first judge said "needs clarify" on 24 of 40 plainly answerable corpus
rows. It flagged `запиши что я пообедал` and `Покажи расписание поездов на
вечер`. It was judging against a generic assistant, one that asks "where?"
about lunch. Maven writes that note.

Rewriting it to state what she can already do took false positives to 16 of 60.
It also catches all eight fixture clarifies. So the judge discriminates.

On the generated pile it removed 7 of 306, a 97.7% keep rate. That is not the
judge failing. The generator is aimed at underspecified lines, so there is
little for a filter to catch. The 27% false-positive rate is the number to
quote, and it is label noise on the positive class.

**Both passes are gemma-4-12b.** Generation and judging. So the corpus is
gemma's opinion of what is underspecified, and the head distills that opinion.
What keeps it honest is the fixture. Those eight cases were written by the owner
and gemma never saw them.

299 rows kept, against 3604 answerable. The positive class carries `intent:
null`, so it costs the intent head nothing.

## Result

Three seeds, 24 epochs, epoch still chosen on the intent dev slice.

| | two heads | three heads | four heads |
|---|---|---|---|
| intent mean | 93.6% | 92.8% | 91.7% |
| destination mean | 80.8% | 82.8% | 79.8% |
| slot span F1 mean | — | 72.4% | 68.3% |
| clarify caught | — | — | 7.0 of 8 |
| false clarifies | — | — | 2.3 of 88 |

**The fourth head is not free the way the third was.** Intent, destination and
slot F1 all move down. The drop is one to four points, and the seed spread is
wide enough to contain it. Seed 2 scores intent 94.3% and destination 84.8%, both above
every three-head seed. Read the drop as unproven rather than as absent.

Accuracy is the wrong number for this head and is reported for completeness at
95.8% to 96.9%. Eight of ninety-six cases are positive, so a head that never
asks scores 91.7%. Recall on those eight is the number.

Compare it to what ships. The cascade today misses 1 clarify and produces 2
false ones. The head catches 7 of 8 and produces 2.3 false ones. That is
parity, from a 118M encoder with no rules in front of it.

The saved checkpoint is seed 2 at epoch 10. Intent 94.3%, destination 28/33,
slot F1 73.6%, clarify 7 of 8 with 3 false. `heads.pt` carries four state dicts.

## What it gets wrong is consistent across seeds

`поужинал` is a false clarify on all three seeds. That utterance is already
recorded as a real defect. `thinSingleToken` was narrowed on 2026-08-01 to spare
a token carrying a Russian verb ending. One word is routinely a whole sentence
in Russian. The head relearned the mistake the rule was narrowed to
fix.

`что дальше?` is a false clarify on two seeds. That one is a disagreement rather
than an error. The utterance is underspecified, and V-498 decided stage 0 claims
it for the calendar on purpose.

`ну это` is missed on two seeds. `amb-003` is the shortest case in the fixture
and the generated demonstratives are longer.

## Confidence

`Confidence: 1.0` was a hardcode in `llmrouter.go`, so a correct low confidence
could not exist. Max softmax over the intent head is the replacement. It is only worth reading
if it is lower where the head is wrong.

It is. Mean 0.851 where the head is right against 0.604 where it is wrong. It
ranks a right case above a wrong one in 83.4% of pairs.

So there are two signals now and they are not the same signal. Confidence says
the head is unsure which intent this is. The clarify head says the utterance
does not carry enough to act on. A confident wrong route and an honest "I cannot
tell" are different failures, and one number cannot report both.

## What this does not measure

The same gap as every head run. **Nothing of this runs in Go.** Four heads
instead of three does not change that.

There is no threshold. Both signals are reported as raw numbers. Turning either
into a gate needs a decision about where to cut, and that trades false clarifies
against wrong acts. The fixture has 8 positives, which is too few to fit a
threshold on.

The 299 generated rows have no held-out slice of their own. Clarify is scored on
the fixture alone.
