Vosk grammar constraints require dynamic-graph models
I raised the model tiers for both wake and command recognition. Vosk went from small to full (vosk-model-en-us-0.22), and faster-whisper from base to small. The Whisper self-test passed identically on both tiers because clean Kokoro TTS does not discriminate between them. The test proved non-regression, not a gain.
The full Vosk model loaded in 16.05 seconds and added 5.07 GB resident memory.
Grammar-constrained recognition
Live testing with the full model scored around 1/10. The partials showed the language model was preferring ordinary English over the proper noun: pay for me, the have, hey format. A grammar removes the competition. The decoder may emit only listed phrases or [unk].
Only a dynamic-graph model can take a runtime grammar. The 1.8 GB full model has a static graph and cannot be constrained.
I made lgraph the default. It loads in 0.83 seconds at +352 MB resident, 19× faster and 14× smaller than the full model. I kept the open-vocabulary shape-matching path as the fallback for full, so selecting wake_model: full still works instead of silently breaking. Three tiers are now selectable via a new wake_model setting.
Name audition harness
Grammar generation became style-aware. From a name, six styles (bare, hey, okay, good morning, good evening, are you there) are crossed with every expected spelling. 24 phrases per finalist.
python foreman.py audition <name> drives the real detector on a candidate, counts wakes, and attributes each one to the entry that caught it. A clean hit on the name is distinguishable from a rescue by a variant spelling.
Critical finding
The bare-name style plus the incumbent name measured 4/6 false wakes on ordinary trade speech. With odin it measured 1/6.
It fired on "the foreman said the job was done", "we are forming a new crew tomorrow", "what is the format of that file". The style correctly reported that the wake name is a common noun in this user's own working vocabulary.
Production wake is degraded until the rename lands or the bare style row is deleted from _WAKE_STYLES.
Matching rule
Matching is complete-entry tail-anchored, never "any non-[unk] result fires". A grammar constrains vocabulary but not length, so partials are prefixes of legal phrases. A bare "hey" or "good morning" would fire under the literal rule.
[unk] is not stripped before matching. "The site had a foreman on it" returns [unk] hey foreman [unk] and stripping would leave a clean phrase that wakes her mid-sentence.
Audition dry run
On synthesized speech all three finalists woke 5/5 across all six styles with zero silence or noise wakes. Apollo resolved to its canonical spelling every time. Odin and Janus were carried entirely by the variants oden and janice.