The hash map served a single exact-match lookup that the trie already
answers, at the price of a second container, a second file loader with
its own drift, and a Str-to-UTF-8 conversion on every chain probe. The
files are small (kanji.dict is 7.5k lines), so the trie holds both.
trieopen validates keys and each space-separated value word against Str
and reports path:line; trielookup takes the Str every caller holds and
treats a nil trie as an unloaded map; trienew/trieput exist for tests.
The overflow guards on growth, Trie.root (always 0) and the per-language
init loop written twice are gone.
Avoid reading inactive Str storage when deciding whether to publish a redraw. Keep the focused engine suite compact by retaining one case for each page, navigation, and empty-result invariant.
Add table-driven dictionary miss and language-switch state coverage, regress stale candidates after backspace, and clear them before dictionary refresh. Keep IPC and runtime-created file checks outside the unit runner.