Skip to content

[codex] fix Windows IME abort restore ordering#234

Closed
millionart wants to merge 46 commits into
Open-Less:mainfrom
millionart:codex-fix-ime-abort-restore
Closed

[codex] fix Windows IME abort restore ordering#234
millionart wants to merge 46 commits into
Open-Less:mainfrom
millionart:codex-fix-ime-abort-restore

Conversation

@millionart
Copy link
Copy Markdown
Contributor

@millionart millionart commented May 4, 2026

User description

Summary

  • Keep dictation abort cleanup in its original non-idle session phase until the prepared Windows IME snapshot has been restored.
  • Publish Idle only after restore for the same session, preventing a new session from racing in and causing the old snapshot to be discarded.
  • Add regression coverage for abort restore ordering and for the existing Inserting guard that prevents starting a new dictation during TSF submit.

Root Cause

abort_recording_with_error set state.phase to Idle before calling restore_prepared_windows_ime_session. Since restore is guarded by state.session_id, a new dictation could start in that gap, advance the session id, and make the old prepared IME snapshot get discarded instead of restored.

Validation

  • cargo test --manifest-path openless-all/app/src-tauri/Cargo.toml --lib --no-run
  • cargo check --manifest-path openless-all/app/src-tauri/Cargo.toml

Note: directly running the target test binary in this local Windows/Tauri environment exits before Rust test execution with STATUS_ENTRYPOINT_NOT_FOUND (0xc0000139), so the executable build was verified with --no-run.


PR Type

Enhancement, Bug fix, Tests


Description

  • Add Windows TSF session flow

  • Fix abort restore ordering race

  • Add non-TSF fallback controls

  • Expand smoke and regression tests


Diagram Walkthrough

flowchart LR
  A["Dictation session"] --> B["Prepare Windows IME session"]
  B --> C["Submit text over IPC"]
  C --> D["Restore saved IME profile"]
  C --> E["Non-TSF insertion fallback"]
  A --> F["Session-scoped resources"]
  A --> G["Abort and cancel cleanup"]
  G --> D
  F --> B
Loading

File Walkthrough

Relevant files
Bug fix
1 files
coordinator.rs
Scope session resources and restore ordering                         
+696/-106
Enhancement
13 files
windows_ime_profile.rs
Capture and restore Windows IME profiles                                 
+730/-0 
windows_ime_ipc.rs
Add named-pipe IME submit transport                                           
+430/-0 
windows_ime_session.rs
Prepare, submit, and restore IME sessions                               
+253/-0 
windows_ime_protocol.rs
Define IME pipe messages and naming                                           
+173/-0 
lib.rs
Register Windows IME modules and command                                 
+28/-9   
types.rs
Add IME status types and preference                                           
+42/-2   
insertion.rs
Add Unicode and clipboard fallback insertion                         
+66/-6   
commands.rs
Expose Windows IME status command                                               
+6/-1     
Settings.tsx
Surface Windows IME controls in settings                                 
+48/-0   
en.ts
Add English Windows IME strings                                                   
+12/-0   
zh-CN.ts
Add Chinese Windows IME strings                                                   
+12/-0   
types.ts
Extend frontend types for IME status                                         
+15/-0   
ipc.ts
Wire frontend IPC for IME status                                                 
+13/-0   
Configuration changes
4 files
windows-preflight.ps1
Update Windows packaging preflight checks                               
+63/-1   
windows-package-msvc.cmd
Add MSVC packaging wrapper script                                               
+8/-0     
openless-ime.wxs
Package Windows IME installer artifacts                                   
+54/-0   
OpenLessIme.vcxproj
Define the Windows IME C++ build                                                 
+159/-0 
Tests
1 files
windows-real-asr-insertion-smoke.ps1
Adjust Windows insertion smoke coverage                                   
+76/-7   
Dependencies
1 files
Cargo.toml
Add Windows IME-related Rust dependencies                               
+7/-2     
Additional files
26 files
2026-05-01-windows-temporary-tsf-ime.md +2191/-0
2026-05-01-windows-temporary-tsf-ime-design.md +143/-0 
windows-ime-build.ps1 +82/-0   
windows-ime-register.ps1 +88/-0   
windows-ime-unregister.ps1 +136/-0 
windows-package-msvc.ps1 +316/-0 
windows-package-msvc.test.mjs +114/-0 
windows-smoke-suite.ps1 +2/-2     
hotkey.rs +20/-11 
selection.rs +4/-3     
tauri.conf.json +11/-0   
OpenLessIme.sln +27/-0   
OpenLessIme.def +6/-0     
class_factory.cpp +75/-0   
class_factory.h +20/-0   
dllmain.cpp +54/-0   
edit_session.cpp +125/-0 
edit_session.h +43/-0   
guids.h +21/-0   
ipc_client.cpp +511/-0 
ipc_client.h +37/-0   
registry.cpp +307/-0 
registry.h +6/-0     
resource.rc +33/-0   
text_service.cpp +332/-0 
text_service.h +54/-0   

millionart added 30 commits May 1, 2026 18:05
@millionart millionart closed this May 4, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Restore loss

The prepared IME snapshot is removed from the session slot before restore_session runs. If the restore path hits a transient COM/TSF error, the snapshot is already gone, so later cleanup cannot retry and the user can be left with the wrong IME/profile state.

fn take_matching_prepared_windows_ime_session(
    slots: &mut Vec<PreparedWindowsImeSessionSlot>,
    session_id: u64,
) -> Option<PreparedWindowsImeSession> {
    let index = slots
        .iter()
        .position(|slot| slot.session_id == session_id)?;
    Some(slots.remove(index).prepared)
}

#[cfg(target_os = "windows")]
fn take_current_prepared_windows_ime_session_for_restore(
    slots: &mut Vec<PreparedWindowsImeSessionSlot>,
    session_id: u64,
    current_session_id: u64,
) -> Option<PreparedWindowsImeSession> {
    let prepared = take_matching_prepared_windows_ime_session(slots, session_id)?;
    if current_session_id == session_id {
        Some(prepared)
    } else {
        None
    }
}

#[cfg(target_os = "windows")]
fn restore_prepared_windows_ime_session(inner: &Arc<Inner>, session_id: u64) {
    let state = inner.state.lock();
    let prepared = {
        let mut slot = inner.prepared_windows_ime_session.lock();
        take_current_prepared_windows_ime_session_for_restore(
            &mut slot,
            session_id,
            state.session_id,
        )
    };
    if let Some(prepared) = prepared {
        inner.windows_ime.restore_session(prepared);
    }
Misclassified error

A Windows insertion failure is mapped to windowsImeTsfRequired whenever focus was restorable and non-TSF fallback is disabled, even if the real problem was IPC failure, timeout, or a protocol error. That hides the actual failure mode and makes diagnostics misleading.

fn dictation_error_code(
    status: InsertStatus,
    polish_failed: bool,
    focus_ready_for_paste: bool,
    allow_non_tsf_insertion_fallback: bool,
) -> Option<&'static str> {
    if !focus_ready_for_paste && status == InsertStatus::Failed {
        Some("focusRestoreFailed")
    } else if cfg!(target_os = "windows")
        && focus_ready_for_paste
        && !allow_non_tsf_insertion_fallback
        && status == InsertStatus::Failed
    {
        Some("windowsImeTsfRequired")
    } else if polish_failed {
        Some("polishFailed")
    } else {
        None
    }

@millionart millionart deleted the codex-fix-ime-abort-restore branch May 8, 2026 11:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant