Skip to content

fix(#125): stop walking ancestors in ensure_folder_exists (flickering cmd.exe windows)#14

Merged
YedPool merged 1 commit into
mainfrom
fix/issue-125-ancestor-walk
May 10, 2026
Merged

fix(#125): stop walking ancestors in ensure_folder_exists (flickering cmd.exe windows)#14
YedPool merged 1 commit into
mainfrom
fix/issue-125-ancestor-walk

Conversation

@YedPool
Copy link
Copy Markdown
Owner

@YedPool YedPool commented May 10, 2026

Summary

Fixes MLFlexer#125 — flickering cmd.exe (or wslhost.exe for WSL-default-domain users) pop-up windows on every WezTerm launch and every Ctrl+Shift+R config reload.

Root cause

ensure_folder_exists walked every ancestor of the target path and probe-wrote each one to test existence. dir_is_accessible is a probe-write — it returns false for directories that exist but aren'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 flicker.

change_state_save_dir calls ensure_folder_exists 4× per init (workspace/window/tab/instances) and setup_claude_session_hooks adds 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.rename with 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> and mkdir -p <path> already create intermediate directories automatically. The walk was pure overhead — and the source of the bug. This PR removes it:

  • Drop parse_root and mkdir_if_missing (dead after the walk goes).
  • Keep a small drive-relative normaliser (C:fooC:\foo) so the documented behaviour is preserved (and the existing spec test still passes).
  • Probe-write only the leaf target — leaf dirs always live in user-writable areas (AppData, ~/.claude), so the false-negative cannot occur.
plugin/resurrect/utils.lua | 111 +++++++++++----------------------------------
1 file changed, 26 insertions(+), 85 deletions(-)

Why not wezterm.run_child_process?

I tried it. During plugin init() it errors with "attempt to yield across a C-call boundary" — same for wezterm.read_dir and wezterm.glob. All three are async APIs that can't run from init context. os.execute is 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 instrumenting shell_mkdir to log every invocation, then running wezterm show-keys repeatedly (same config-load path as a real launch / config reload):

Run Pre-existing leaves shell_mkdir calls
Launch 1 none (fresh install) 2 — state/instances, .claude/pane-sessions (legitimate)
Launch 2 both exist 0
Launch 3 both exist 0

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

🤖 Generated with Claude Code

`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
@YedPool YedPool merged commit d32e3d6 into main May 10, 2026
@YedPool YedPool deleted the fix/issue-125-ancestor-walk branch May 10, 2026 06:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flickering pop-up windows on launch and config reload

1 participant