Repository navigation
bug: Korean-compatible shifted vowel forces 말할까 to raw English akfgkfRK #12
Description
Summary
Typing 말할까 can intermittently select raw English akfgkfRK, although pressing Esc selects the correct 말할까. Similar behavior was observed with other words ending in 까.
The last uppercase K is not necessarily intentional English capitalization. Two-set Hangul treats both k and K as ㅏ, but the language classifier treats K as decisive English evidence. This makes a valid Korean key sequence sensitive to Shift overlap when typing the preceding doubled consonant.
Version and scope
- Observed with the official macOS v0.10.6 release, Japanese-enabled edition, language order
ko,en,ja. - Deterministically reproduced with the installed official CLI and a release-matched v0.10.6 FFI build using temporary settings and no user learning data.
- The relevant macOS adapter, Hangul composer, heuristic, mixed-language, and preview code is unchanged between v0.10.6 and v0.10.8. v0.10.8 was not installed or runtime-tested for this report.
- The classifier is shared core logic. Physical keyboard behavior on Windows/Linux/Android was not tested.
Reproduction
Compare these exact key streams in a fresh session:
akfgkfRk: lowercase finalk.akfgkfRK: uppercase finalK.
Both have the same valid two-set Korean reading, 말할까:
R→ㄲ.kandK→ㅏ.
Expected: a Korean-compatible shifted vowel following the Korean composition should not categorically force the entire word to English solely because of that uppercase character. Genuine English capitalization must remain available and raw key case must be preserved.
Actual: the first stream selects Korean; the second selects raw English. Esc recovers the correct Korean reading from the same stored keys.
Candidate physical reproduction
Type akfgkf, hold Shift to type R (ㄲ), then press the K key before releasing Shift. Compare with releasing Shift before the final vowel.
This is a plausible way to generate the failing stream, not a verified trace of the user's physical Shift timing. The user did not intend a capital K. Caps Lock, layout/event-source differences, and physical overlap were not individually captured during the original incident.
Reproduction evidence
Official CLI:
$ tacit -v "akfgkfRk akfgkfRK"
말할까 akfgkfRK
akfgkfRk → 말할까 [ko 1.00] ko=-1.10 en=-4.36 margin=+3.26
akfgkfRK → akfgkfRK [en 0.99] uppercase outside QWERTOP
$ printf 'akfgkfRk\nakfgkfRK\n' | tacit --compose
akfgkfRk 말할까
akfgkfRK 말할까
Release-matched FFI replay, with English/Korean/Japanese enabled and no learned data (M = marked text, I = insertion):
Control, akfgkfRk:
after R: 1 | M말할ㄲ
after k: 1 | M말할까
Space: 0 | I말할까
Failing stream, akfgkfRK:
after R: 1 | M말할ㄲ
after K: 1 | MakfgkfRK
Space: 0 | IakfgkfRK
Same failing stream, then Esc:
after K: 1 | MakfgkfRK
Esc: 1 | M말할까
Space: 0 | I말할까
Enter was also replayed and commits the corresponding Korean/English result, consumed with returnConfirmsOnly=true.
An isolated English/Korean decision probe confirms that this case is not specifically a boundary-time reclassification:
akfgkfRk, partial=true and partial=false:
Korean, output=말할까, confidence=0.9973167156760228
akfgkfRK, partial=true and partial=false:
English, output=akfgkfRK, confidence=0.99
reason: uppercase outside QWERTOP
In this deterministic reproduction, the preview changes immediately on K, before Space/Enter.
Root-cause analysis
platforms/macos/Controller.swift,handle(): forwards the first scalar ofevent.charactersunchanged. Shift is not excluded with Command/Control/Option. This path does not independently turn a lowercasekintoK, and does not track Shift-release timing.crates/core/src/lang/hangul.rs: the composer mapsRtoㄲand mapsKthrough its lowercase fallback toㅏ, so both streams compose correctly. However,is_korean_shift_key('K')is false because Shift does not change this vowel.crates/core/src/decide/engine.rs: when English is enabled, an uppercase character outside the Korean-specific shifted keysQWERTOPtakes an early English decision with confidence0.99and explanationuppercase outside QWERTOP.- The resulting high-confidence English choice can replace the prior Korean preview. Esc explicitly selects a Korean reading, bypassing the automatic language choice while preserving the original raw keys.
The problem is the inference that an uppercase character with no distinct Korean shifted meaning must indicate English intent. That inference is too strong for a valid Korean composition after a doubled consonant.
Suggested fix boundary
Narrow the categorical uppercase-English rule using Korean composition/preview/context evidence. Preserve genuine English capitalization and the original key stream for cycling back to English.
Do not lowercase all native events or all stored keys: that would lose English case and break meaningful Korean shifted keys such as R → ㄲ. Avoid word-specific exceptions for 말할까 or the suffix 까.
A focused regression can compare akfgkfRk and akfgkfRK, assert correct Hangul availability and appropriate automatic selection, and retain genuine English-capitalization controls. This is separate from confirmation replacing the last visible composition: the failing stream here is already English before confirmation.
Verification limits
The synthetic key replay proves the deterministic response to uppercase K, not why a particular native key event carried that case. No physical key timing instrumentation or user keystroke logging was performed. The installed app, settings, and learning data were left unchanged.
Activity
helloiamnotth3devnicetomeetyou commentedon Oct 22, 2026
Thanks for the reproduction and root-cause analysis. I confirmed that
akfgkfRkandakfgkfRKcompose to the same Hangul reading, but the final uppercaseKcauses the latter to be classified as English. Esc recovers the Korean reading.I'll adjust the uppercase classification rule to account for Korean composition when Shift overlaps the following vowel, while preserving the original key stream, genuine English capitalization, and meaningful Korean shifted keys. This will be handled separately from #11.
snowykr commentedon Oct 22, 2026
Thanks for confirming the reproduction. I investigated a conservative fix and then tested its generalization more aggressively. Posting the results here because the remaining failures are not limited to invented words, and the broader alternatives I tried introduce a real English-preservation tradeoff.
Conclusion: my candidate provides a limited improvement, but I am not proposing it as a general solution to this issue. The issue remains reproducible. These experiments were based on
82add6290e7d6b1dc450af551277bed7153585b0; they are not an assessment of any subsequently implemented upstream fix.1. Candidate and initial checks
The candidate relaxes the uppercase-English shortcut only when:
Q/W/E/R/T), with lowercase letters elsewhere in the token;This only permits score competition; it does not force Korean. Original keys and capitalization are preserved, meaningful shifted vowels/consonants are not flattened, and the exception does not admit an unrelated Japanese reading. There are no word-specific exceptions.
The implementation and evaluator are available for inspection on my fork:
It fixes the clean-profile reproductions
akfgkfRK → 말할까anddlfWLr → 일찍.The initial evaluation covered 145,344 rows / 435,948 decision or session-output surfaces across enko/enkoja and development/holdout groups, with 5,292 improved surfaces and no newly wrong sampled surfaces. The evaluator used independently specified expected outputs, composition-preserving augmentation, canonical-key grouping before splitting, exact-case English identifier controls, and real InputSession replay for contextless rows. New wrong outputs were counted separately from improvements, rather than hidden by a net score.
Important limitation: these are repeated surfaces/contexts/profiles, not that many independent words. Key-group holdout does not make the corpus independent of the dictionaries used by the engine. The favorable result did not establish generalization to unfamiliar words or productive forms.
A local installed macOS build also completed a final 69-case OS-key smoke matrix: textarea 23, contenteditable 23, and fresh NSTextView clients 23. The driver posted virtual-key events, including holding Shift across adjacent uppercase keys; it did not insert expected Unicode text for the IME cases. Marked and committed text were checked. That build also included the separate #13 guard. These were selected smoke cases, not human keyboard-timing measurements, an all-app claim, or a matched-profile before/after study. Their success should not be mistaken for unseen-word coverage.
There is also a measured cost, not a speed improvement: focused partial-decision benchmarks on affected paths added roughly 1.1–1.8 microseconds in the tested configurations. This is not end-to-end keyboard latency.
2. Ordinary forms still fail
Further manual exploration included
늘찍/smfWLras a deliberately possibly-nonstandard input. I then constructed a new challenge set containing 85 ordinary expressions/forms and generated single-overlap variants. Each generated variant was checked to compose to the same independently specified Hangul as its canonical keys.With no learned data, no previous-language context, and final decisions:
The results were identical in enko and enkoja. The current candidate also remained at 58/119 with previous English or Korean context.
Examples with the candidate:
smfWLrsmfWLrsjfWLrgkeksjfWLrgkeksmfTLsgkrpsmfTLsgkrpTKfTkfgodyTKfTkfgodyRKRdkTdjdyRKR았어요The corresponding canonical inputs produce the intended Hangul. The ordinary-form rows show that this is not just a question of whether an invented word deserves automatic conversion.
This is a handcrafted diagnostic set, with related forms/stems, not a representative error-rate estimate or a newly blind holdout. Its purpose is to expose counterexamples to the generalization claim.
I also cross-checked difficult examples through the actual C FFI using the previous #13-only build and the combined candidate, enko/enkoja, Enter/Space, and isolated clean, generic-shape, or explicit-word learning states: 288 recorded replays, including failures, not 288 successful Korean corrections. The failures above reproduce in clean sessions; explicit word overrides remain effective.
3. Why these failures occur
For
smfWLr, the underlying raw scores favor Korean (approximatelyko=-2.42,en=-5.09). The composer produces늘찍correctly.However, the candidate's lexical predicate is false, so
HeuristicEngine::decide()returns the early English decision withconfidence=0.99,runner_up=None, anduppercase outside QWERTOPbefore normal score comparison. That 0.99 is a rule-assigned value, not measured classification accuracy. Removing the overlap (smfWlr) permits normal scoring and selects Korean.There is a second boundary: mixed-language arbitration. For example,
RKRdkTdjdybecomesRKR았어요. Relaxing the whole-word heuristic alone does not ensure that it wins over an earlier mixed segmentation. This implicates consistency between the heuristic and mixed-language paths, not just one uppercase check. It does not establish that one particular lattice penalty is solely responsible.4. Broader rules tested in isolated copies — not installed
I tried two further eligibility rules while retaining the adjacency restriction, original keys, and exclusion of unrelated Japanese readings:
a/e/i/o/u/y) and entirely composed Hangul syllablesBoth introduce new false conversions on synthetic identifier controls that the narrow candidate preserves:
These are explicitly labeled identifier intentions, not ordinary English dictionary words; their real-world frequency was not measured. They nevertheless demonstrate why “no English vowel” or “valid Hangul syllables” cannot by themselves establish safe automatic conversion. There were six new wrong identifier controls in the initial control set for each expansion. An existing
RT → ㄲㅆresult was kept separate and not counted as a new regression.Simply raising a confidence cutoff does not separate all the matching cases either. In additional deliberately constructed stress controls, the erroneous
dmlWL → 의찌received about0.99775, higher than the desiredsmfWLr → 늘찍rescue at about0.99523. Those controls were added after initial inspection, not treated as held-out validation, and the candidates were not retuned to them.The scoring-only experiment still leaves 11/119 ordinary variants wrong through mixed-language selection, even when the whole-word heuristic selects the intended Korean. Examples include
RKR았어요andRHaRha하다instead of깎았어요and꼼꼼하다.I do not consider either expansion safe to adopt in its current form. This is not proof that every broader solution is impossible.
5. Personal learning is an escape hatch, not passive adaptation
In isolated real-FFI sessions:
smfWLrwithout correcting it left the learning store empty and the output unchanged.늘찍automatically.smfWLrgkek → 늘찍하다, but an unrelated unknown form such assjfWLrgkekstill failed.Thus deliberate repeated correction can help a token and supported stem/ending combinations. Merely waiting while typing does not teach these decisions. Generic shape preference also cannot overcome this hard English decision: even without the additional #13 eligibility restriction, its maximum bias of 1.6 is below the margin implied by 0.99 (
ln(99)/2 ≈ 2.298).Status
I am pausing my candidate as a general fix rather than claiming this issue is solved. Its narrow gains are real, but its lexical boundary misses ordinary forms, and the tested broadenings trade better Korean recall for identifier corruption.
A promising investigation would need coherent evidence across capitalization, Korean word/form plausibility, English identifier preservation, and mixed segmentation, with independently labeled negative cases—not word exceptions, global lowercasing, or a confidence threshold justified only by these examples.
The further experiments were confined to isolated source copies and synthetic fixtures. No private learning records, unrelated application input, or machine-specific diagnostics are included here. I am leaving the issue open; these findings are additional reproduction/validation material for the planned fix, not a claim that it cannot be fixed.
helloiamnotth3devnicetomeetyou commentedon Oct 22, 2026
Fixed in #15, now merged into
main.Both
akfgkfRkandakfgkfRKnow select말할까. A shifted vowel immediately after a Korean doubled-consonant key in an otherwise lowercase key stream uses normal language scoring rather than forcing English. The original key stream, English capitalization and abbreviations, and explicit cycling back to English are preserved.Real-model, session, and C FFI regressions passed, along with repository CI. This fix is prepared for 0.10.9; that release has not been published yet. The tests verify replayed key streams, not the physical Shift timing that originally produced them.
fix: preserve confirmed readings and handle shifted Korean vowels