From 65c5058bc6bafbe33efa1ce2e1cca23e16543a25 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 2 Aug 2026 00:32:22 -0700 Subject: [PATCH] docs(ime): name the derived traces honestly and cite the Escape spec basis Every trace in ime-recorded-composition-traces was `derived`; the only hardware recording in the suite is the ibus-hangul one it re-exports. The name asserted evidence the file does not have, so it is now ime-derived-composition-traces with a header saying so outright. The Escape exemption in isImeCompositionKeyDown was justified in prose as an assumption. It is stronger than that: UI Events defines `isComposing` as true between compositionstart and compositionend, so an unmarked Escape is the browser contradicting our derived flag, and the browser is authoritative. Recorded the reasoning and the residual risk that a browser under-reports the bit. Co-authored-by: Orca --- .../src/lib/ime-composition-keyboard-event.ts | 15 ++++++++++----- .../src/lib/ime-composition-trace.test.ts | 2 +- ...e-derived-composition-traces.test-fixtures.ts} | 4 ++++ 3 files changed, 15 insertions(+), 6 deletions(-) rename src/renderer/src/lib/{ime-recorded-composition-traces.test-fixtures.ts => ime-derived-composition-traces.test-fixtures.ts} (97%) diff --git a/src/renderer/src/lib/ime-composition-keyboard-event.ts b/src/renderer/src/lib/ime-composition-keyboard-event.ts index 4d64bddefef..a5cebfa3a96 100644 --- a/src/renderer/src/lib/ime-composition-keyboard-event.ts +++ b/src/renderer/src/lib/ime-composition-keyboard-event.ts @@ -101,10 +101,15 @@ export function isImeCompositionKeyDown(event: AnyKeyboardEvent): boolean { if (keybindingInputIsImeOwned(nativeEvent)) { return true } - // Why: an Escape the IME owns is marked above, so an unmarked one means this - // derived flag has drifted. compositionend is not guaranteed, so swallowing it - // would strand every cancel path behind a flag nothing is going to clear. Same - // rule as TERMINAL_IME_OWNED_KEYS in xterm-bypass-policy.ts, which omits Escape - // for exactly this reason. + // Why: UI Events defines `isComposing` as true for any key event dispatched between + // compositionstart and compositionend, so an Escape the IME owns is marked above by + // definition. An unmarked one is the browser stating we are not between those two + // events — it disagrees with this derived flag, and the browser is the authority. + // Escape is the one key where trusting the flag instead is unrecoverable: compositionend + // is not guaranteed, so swallowing it strands every cancel path behind a flag nothing + // will clear. Same rule as TERMINAL_IME_OWNED_KEYS in xterm-bypass-policy.ts. + // + // Spec-backed, not engine-verified: CANDIDATE_ESCAPE_DISMISSAL_TRACE is derived, and a + // browser that under-reports the bit would contradict this. Recording it settles it. return nativeEvent.key !== 'Escape' && isDocumentImeCompositionActive() } diff --git a/src/renderer/src/lib/ime-composition-trace.test.ts b/src/renderer/src/lib/ime-composition-trace.test.ts index 0298def6dc1..c5ae31b6950 100644 --- a/src/renderer/src/lib/ime-composition-trace.test.ts +++ b/src/renderer/src/lib/ime-composition-trace.test.ts @@ -17,7 +17,7 @@ import { IBUS_HANGUL_RETAINED_COMMIT_TRACE, IBUS_HANGUL_TERMINAL_JAMO_COMMIT_TRACE, IME_COMPOSITION_TRACES -} from './ime-recorded-composition-traces.test-fixtures' +} from './ime-derived-composition-traces.test-fixtures' const KEY_EVENT_WITHOUT_REPEAT: ImeTraceKeyEvent = { code: 'KeyE', diff --git a/src/renderer/src/lib/ime-recorded-composition-traces.test-fixtures.ts b/src/renderer/src/lib/ime-derived-composition-traces.test-fixtures.ts similarity index 97% rename from src/renderer/src/lib/ime-recorded-composition-traces.test-fixtures.ts rename to src/renderer/src/lib/ime-derived-composition-traces.test-fixtures.ts index 50e096af549..263ea517135 100644 --- a/src/renderer/src/lib/ime-recorded-composition-traces.test-fixtures.ts +++ b/src/renderer/src/lib/ime-derived-composition-traces.test-fixtures.ts @@ -2,6 +2,10 @@ // // Adding a trace: prefer capturing one from a real engine and mark it `recorded`. // A `derived` trace pins current behavior but proves nothing about the engine. +// +// Everything defined below is `derived`. The only hardware recording in the suite is the +// ibus-hangul trace re-exported here. Read a derived trace as a written-down assumption +// waiting to be falsified by `pnpm ime:record`, not as evidence about any engine. import type { ImeCompositionTrace,