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 <help@stably.ai>
This commit is contained in:
Neil
2026-08-02 00:32:22 -07:00
co-authored by Orca
parent 867f38e9dc
commit 65c5058bc6
3 changed files with 15 additions and 6 deletions
@@ -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()
}
@@ -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',
@@ -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,