You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Two app instances saving at once silently lose each other's profiles and commands: the data-directory lock only serializes within one process #236
That lock only works inside one process. SaveProfileAsync and UpdateCommandsAsync each load the file, merge in memory and write the result back. When two processes do this over the same directory at the same time, the last writer replaces the other's changes. The class remarks and the README scope the guarantee to "in the process", so this is a gap left by the #148/#195 fixes rather than a regression. But two instances of a desktop app sharing one keybinding directory is a common setup, for example two editor windows. In that setup a user's new profile or rebind can disappear with no error.
Hold an OS-level lock for the whole load-merge-write, alongside the in-process semaphore. For example, open <dir>/.lock with FileShare.None and retry with backoff on IOException.
Cover SaveProfileAsync, UpdateCommandsAsync, DeleteProfileAsync and SaveActiveProfileAsync.
Add a test that runs N concurrent savers which can't share the static lock (separate processes, or separate AssemblyLoadContexts), and assert that all N profiles and commands survive.
Update the class remarks and the README to say saves are serialized across processes.
Category: Bug. It is labelled enhancement, but the result is silent data loss in a common setup: two app instances sharing one data directory.
Priority: High. The repro lost data in every run, and there is no error or warning.
Area / suggested assignment:JsonKeybindingRepository: the static per-directory semaphore, and the load-merge-write in SaveProfileAsync, UpdateCommandsAsync, DeleteProfileAsync and SaveActiveProfileAsync.
Activity
matt-edmondson commentedon Nov 8, 2026
Triage
enhancement, but the result is silent data loss in a common setup: two app instances sharing one data directory.JsonKeybindingRepository: the static per-directory semaphore, and the load-merge-write inSaveProfileAsync,UpdateCommandsAsync,DeleteProfileAsyncandSaveActiveProfileAsync.WriteFileUnlockedAsyncreplace path) both touch this code, so expect conflicts. Delegate profile/command JSON persistence to ktsu.AppDataStorage #93, delegating persistence toktsu.AppDataStorage, would move this code entirely, so decide whether this fix goes in first.<dir>/.lock,FileShare.None, retry with backoff). Key it on the resolved directory from Resolve a relative data directory once, for both the files and the lock [patch] #220. Add a multi-process test and update the remarks and README.Generated by Claude Code