src/icons.rs:12 holds render state in a process-global mutable:
static USE_NERD_FONTS: AtomicBool = AtomicBool::new(true);
pub fn set_nerd_fonts(val: bool) { USE_NERD_FONTS.store(val, Ordering::Relaxed); }
Icon::s() / Icon::c() read it on every call to choose between the nerd glyph and the ASCII fallback, so it directly determines rendered output — and rendered width, since the two variants are not the same width.
It is written from several production paths, all driven by settings.use_nerd_fonts:
src/tui_main/mod.rs:675
src/tui_main/shell_app.rs:187
src/core/settings.rs:1087, :1599 (the :set nerdfonts / :set nonerdfonts command path)
src/gtk/mod.rs:1395
Why this matters now
No test currently calls set_nerd_fonts, so this is latent, not live — verified during the #615 investigation. But a Rust test binary runs its tests in parallel threads in one process, so a single future test that flips this flag would non-deterministically corrupt every snapshot test running concurrently, with failures that depend on scheduling and therefore on core count. That is a nightmare to diagnose.
We just spent a multi-round investigation on #615, whose failure mode was exactly this shape: a render that depends on process-wide ambient state, passing everywhere locally and failing only in CI. #615 turned out to be a different mechanism (~/.config/vimcode/session.json leaking into Engine::new()), and USE_NERD_FONTS was audited and cleared as a candidate — but the audit is what surfaced it as a landmine.
Proposal
Move the flag off process-global state and onto something scoped — per-Engine, per-Backend, or thread-local. src/render.rs:788 already does b.set_nerd_fonts(engine.settings.use_nerd_fonts) on the backend, and src/render.rs:16653 has a comment asserting that resolving icons is "the backend's job (Backend::set_nerd_fonts)". So the intended design may already be backend-scoped and the global is vestigial — worth checking whether the icons:: global can simply be deleted in favour of the backend path.
If the global must stay for now, at minimum add a test-only guard (a mutex or #[serial]) so no test can flip it while snapshot tests run.
Not urgent
Nothing is broken today. This is a "fix it before it costs a week" issue — file it, size it, schedule it. Found during the #615 investigation.
src/icons.rs:12holds render state in a process-global mutable:Icon::s()/Icon::c()read it on every call to choose between the nerd glyph and the ASCII fallback, so it directly determines rendered output — and rendered width, since the two variants are not the same width.It is written from several production paths, all driven by
settings.use_nerd_fonts:src/tui_main/mod.rs:675src/tui_main/shell_app.rs:187src/core/settings.rs:1087,:1599(the:set nerdfonts/:set nonerdfontscommand path)src/gtk/mod.rs:1395Why this matters now
No test currently calls
set_nerd_fonts, so this is latent, not live — verified during the #615 investigation. But a Rust test binary runs its tests in parallel threads in one process, so a single future test that flips this flag would non-deterministically corrupt every snapshot test running concurrently, with failures that depend on scheduling and therefore on core count. That is a nightmare to diagnose.We just spent a multi-round investigation on #615, whose failure mode was exactly this shape: a render that depends on process-wide ambient state, passing everywhere locally and failing only in CI. #615 turned out to be a different mechanism (
~/.config/vimcode/session.jsonleaking intoEngine::new()), andUSE_NERD_FONTSwas audited and cleared as a candidate — but the audit is what surfaced it as a landmine.Proposal
Move the flag off process-global state and onto something scoped — per-
Engine, per-Backend, or thread-local.src/render.rs:788already doesb.set_nerd_fonts(engine.settings.use_nerd_fonts)on the backend, andsrc/render.rs:16653has a comment asserting that resolving icons is "the backend's job (Backend::set_nerd_fonts)". So the intended design may already be backend-scoped and the global is vestigial — worth checking whether theicons::global can simply be deleted in favour of the backend path.If the global must stay for now, at minimum add a test-only guard (a mutex or
#[serial]) so no test can flip it while snapshot tests run.Not urgent
Nothing is broken today. This is a "fix it before it costs a week" issue — file it, size it, schedule it. Found during the #615 investigation.