fix(#125): stop walking ancestors in ensure_folder_exists (flickering cmd.exe windows)#14
Merged
Merged
Conversation
`cmd /c mkdir <path>` and `mkdir -p <path>` already create intermediate directories automatically, so the per-component walk in `ensure_folder_exists` was pure overhead — and it was the root cause of the cmd.exe window flashes reported in MLFlexer#125. The walk called `dir_is_accessible(ancestor)` on each path component. `dir_is_accessible` is a probe-write check: it returns false for a directory that **exists but isn't writable to the current user** (e.g. `C:\Users` on a non-admin Windows account). For each such ancestor, `shell_mkdir` then ran `os.execute('cmd /c mkdir ...')`, which on Windows spawns a visible `cmd.exe` console window — the "flickering pop-up" users described. Because `change_state_save_dir` calls `ensure_folder_exists` 4× per init (workspace/window/tab/instances) and `setup_claude_session_hooks` adds a 5th, and `init()` runs on every WezTerm launch and every config reload, affected users saw 4–N flashes per launch / per reload. The previous fix (MLFlexer#127-style probe-write replacing `os.rename`) only fixed one false-negative; it left the read-only-ancestor false-negative intact. This PR removes the walk entirely: - Drop `parse_root` and `mkdir_if_missing` (dead after the walk goes). - Keep a small drive-relative normaliser (C:foo → C:\foo) so the documented behaviour is preserved. - Trust `cmd /c mkdir` and `mkdir -p` to create intermediates. After the change: 0 shell_mkdir calls per launch in steady state. First install still creates the leaf state dirs (1 call per missing leaf, ≤5 total) and is then quiet forever. Verified locally on wezterm 20240203-110809-5046fc22 / Win 11 by instrumenting shell_mkdir and counting invocations across 3 consecutive `wezterm show-keys` runs (which exercises the same config-load path as a real launch and a Ctrl+Shift+R reload): - Launch 1 (fresh): 2 calls (state/instances + .claude/pane-sessions) - Launch 2: 0 calls - Launch 3: 0 calls Closes MLFlexer#125
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes MLFlexer#125 — flickering
cmd.exe(orwslhost.exefor WSL-default-domain users) pop-up windows on every WezTerm launch and every Ctrl+Shift+R config reload.Root cause
ensure_folder_existswalked every ancestor of the target path and probe-wrote each one to test existence.dir_is_accessibleis a probe-write — it returns false for directories that exist but aren't writable to the current user, e.g.C:\Userson a non-admin Windows account. For each such ancestor,shell_mkdirthen ranos.execute('cmd /c mkdir ...'), which on Windows spawns a visiblecmd.execonsole window — the flicker.change_state_save_dircallsensure_folder_exists4× per init (workspace/window/tab/instances) andsetup_claude_session_hooksadds a 5th.init()runs on every launch and every config reload, so affected users saw 4–N flashes per event.The earlier MLFlexer#125 fix attempt (replacing
os.renamewith probe-write) addressed one false-negative but left the read-only-ancestor false-negative in place, which is why @pszlendak (2026-03-19) and @pine2D (2026-05-10) reported the bug still reproduces on this fork.Fix
cmd /c mkdir <path>andmkdir -p <path>already create intermediate directories automatically. The walk was pure overhead — and the source of the bug. This PR removes it:parse_rootandmkdir_if_missing(dead after the walk goes).C:foo→C:\foo) so the documented behaviour is preserved (and the existing spec test still passes).Why not
wezterm.run_child_process?I tried it. During plugin
init()it errors with"attempt to yield across a C-call boundary"— same forwezterm.read_dirandwezterm.glob. All three are async APIs that can't run from init context.os.executeis the only viable option for the rare miss case (genuinely-missing leaf dir on first install), and the new code only invokes it then.Test plan
Verified on
wezterm 20240203-110809-5046fc22/ Windows 11 by instrumentingshell_mkdirto log every invocation, then runningwezterm show-keysrepeatedly (same config-load path as a real launch / config reload):state/instances,.claude/pane-sessions(legitimate)Existing spec coverage still applies (nested mkdir, idempotent re-entry, spaces in path, file-as-obstacle, relative paths, drive-letter abs, drive-relative normalisation).
Notes
dev.weztermkeyword mismatch silently aborts plugin load on fresh install). fix: narrow init keywords to { "YedPool" } so both URL spellings load #13 must merge for this fix to take effect for new users.🤖 Generated with Claude Code