Skip to content

fix(paths): prefer LOCALAPPDATA over HOME for state_dir on Windows - #130

Open
ziyaad-mallick wants to merge 2 commits into
RunanywhereAI:mainfrom
ziyaad-mallick:fix/state-dir-windows-precedence-v2
Open

ziyaad-mallick wants to merge 2 commits into
RunanywhereAI:mainfrom
ziyaad-mallick:fix/state-dir-windows-precedence-v2

Conversation

@ziyaad-mallick

@ziyaad-mallick ziyaad-mallick commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

state_dir() in src/config/cli_paths.cpp checks HOME before its own
#if defined(_WIN32) block:

    if (std::string home = getenv_utf8("HOME"); !home.empty()) {
        return normalize_dir(std::move(home)) + "/.local/state/runanywhere";
    }
#if defined(_WIN32)
    if (std::string local = getenv_utf8("LOCALAPPDATA"); !local.empty()) {
        return normalize_dir(std::move(local)) + "/RunAnywhere/state";
    }

So whether the Windows branch runs at all depends on whether HOME happens to
be set. On a Windows 11 Pro x64 machine:

shell HOME state_dir() resolves to
PowerShell / cmd.exe unset %LOCALAPPDATA%/RunAnywhere/state (intended)
Git Bash / MSYS2 /c/Users/<user> $HOME/.local/state/runanywhere (POSIX layout on Windows)

Anyone running wally from Git Bash, MSYS2, or a tool launched from one gets a
POSIX-shaped state directory under the user profile, and the LOCALAPPDATA /
USERPROFILE branches are unreachable for them.

Change

Move the _WIN32 block ahead of the HOME check. XDG_STATE_HOME stays first
as an explicit override on every platform. Non-Windows builds are unchanged:
the moved block is compiled out there, so the order is still
XDG_STATE_HOME -> HOME.

A second commit updates the two places that describe the location:
docs/EDITORS.md (where to find shim.log) and the state_dir() comment in
src/config/cli_paths.h. Both named only ~/.local/state/runanywhere, which is
no longer the answer on Windows unless XDG_STATE_HOME is set.

Why CI did not catch this

The Windows case already in test_state_dir() (the backslashed LOCALAPPDATA
case from #58) explicitly unsets HOME before calling state_dir(), so it
reaches the Windows branch regardless of the ordering. This PR adds a second
_WIN32 case with HOME set alongside LOCALAPPDATA and asserts
LOCALAPPDATA wins. It runs on the existing windows-2022 and
windows-11-arm ctest jobs.

Behaviour change for existing users

For a user who has been running from Git Bash / MSYS2, everything kept under
state_dir() moves from ~/.local/state/runanywhere to
%LOCALAPPDATA%/RunAnywhere/state: REPL history (cmd_run), shim.log
(anthropic/messages.cpp), and the seeded Claude Code config dir
(cmd_editors.cpp, state_dir() + "/claude"). Nothing is migrated. The config
dir re-seeds from ~/.claude on the next launch (its first_run path); the old
history and log are simply left behind. If you would rather keep existing
MSYS2 users where they are, say so and I will add a fallback that prefers an
already-existing $HOME/.local/state/runanywhere, but that seemed like more
code than the problem deserves.

Tested

Windows 11 Pro x64, MinGW-w64 g++ 15.2.0 (MSYS2 Rev10). Full ctest was
not run: building the test binary needs the pinned SDK kit (protobuf +
rac/* headers), which is not on this machine. Instead I compiled the real
src/config/cli_paths.cpp against a one-line stub for its SDK include
(rac_desktop_default_base_dir, used only by resolve_home, not by
state_dir), and extracted the EnvVar helper and test_state_dir()
verbatim from tests/test_wally_unit.cpp with sed into a throwaway
harness (main() just runs test_state_dir() and prints the result).

Build (same for each variant, -Wall, no warnings):

g++ -std=c++17 -Wall -I<variant> -Istub <variant>/config/cli_paths.cpp <variant>/harness.cpp -o ...

main's cli_paths.cpp + this PR's test_state_dir() (red):

[FAIL] state_dir HOME should not win over LOCALAPPDATA, got C:/msys-home/.local/state/runanywhere
exit=1

This PR's cli_paths.cpp + this PR's test_state_dir() (green):

[PASS] state_dir
exit=0

Same result with HOME unset in the calling shell (env -u HOME): red on
main, green on the branch. main's own unmodified test_state_dir() also
passes against main's source ([PASS] state_dir, exit 0), confirming the
existing case does not exercise this ordering.

UNVERIFIED:

  • the test under MSVC / the CI Windows jobs (no MSVC or SDK kit here);
  • the non-Windows path (reasoned from the preprocessor, not built or run);
  • wally info output end to end from Git Bash (no full build here).

🤖 Generated with Claude Code


Summary by cubic

Fixes Windows state directory resolution so LOCALAPPDATA is preferred over HOME when both are set (as in MSYS2/Git Bash), and updates the docs and state_dir() comment to name the Windows path.

Bug Fixes

  • On _WIN32, check LOCALAPPDATA/USERPROFILE before HOME so Windows builds no longer fall back to a POSIX-shaped path (~/.local/state/runanywhere); XDG_STATE_HOME still wins first on all platforms.
  • Add a test case with HOME set alongside LOCALAPPDATA to guard this ordering.

Migration

  • For existing MSYS2/Git Bash users, everything under state_dir() (REPL history, shim.log, seeded Claude config dir) moves to %LOCALAPPDATA%\RunAnywhere\state; nothing is migrated, and the config dir re-seeds from ~/.claude on next launch.

Written for commit 66ab335. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Fixed Windows state directory resolution so application state is stored under %LOCALAPPDATA%\RunAnywhere\state, including when HOME is set through environments such as MSYS2 or Git Bash.
    • Preserved existing behavior for custom XDG_STATE_HOME settings and non-Windows platforms.
  • Documentation

    • Updated state directory documentation to include Windows paths and clarify platform-specific resolution behavior.

ziyaad-mallick and others added 2 commits September 22, 2026 20:02
state_dir() tested HOME before its own _WIN32 block, so on any Windows
machine where HOME is set -- MSYS2, Git Bash, and anything launched from
them -- state resolved to %USERPROFILE%/.local/state/runanywhere and the
LOCALAPPDATA branch was unreachable. Native cmd.exe and PowerShell do not
set HOME, so the intended path only ever applied there. The existing
separator case in test_state_dir unsets HOME to reach the Windows branch,
which is why this never showed up in CI.

Moving the Windows block ahead of HOME makes the platform branch win on
the platform it is for. XDG_STATE_HOME stays first as an explicit
override on every platform, and non-Windows builds are unchanged.

For an existing MSYS2 / Git Bash user this moves everything kept under
state_dir() -- REPL history, shim.log and the seeded Claude Code config
dir -- to %LOCALAPPDATA%/RunAnywhere/state. Nothing is migrated; the
config dir re-seeds from ~/.claude on the next launch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
docs/EDITORS.md sends people to ~/.local/state/runanywhere/ for shim.log,
and the state_dir() contract in cli_paths.h says the same. With the
previous commit that is never the answer on Windows unless XDG_STATE_HOME
is set: state lands under %LOCALAPPDATA%\RunAnywhere\state even from Git
Bash, where HOME is set. Say so in both places.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ziyaad-mallick
ziyaad-mallick requested a review from a team as a code owner September 22, 2026 15:06
@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 5991485c-f989-4a92-b982-a19165369df2

📥 Commits

Reviewing files that changed from the base of the PR and between ff54394 and 66ab335.

📒 Files selected for processing (4)
  • docs/EDITORS.md
  • src/config/cli_paths.cpp
  • src/config/cli_paths.h
  • tests/test_wally_unit.cpp

Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

Windows state directory resolution now prioritizes LOCALAPPDATA over HOME. The public documentation and editor documentation describe the Windows path, and a Windows-only test covers the MSYS2/Git Bash environment case.

Changes

Windows state directory resolution

Layer / File(s) Summary
Windows path precedence and regression coverage
src/config/cli_paths.h, src/config/cli_paths.cpp, tests/test_wally_unit.cpp, docs/EDITORS.md
The Windows LOCALAPPDATA and USERPROFILE paths are checked before the HOME fallback. Documentation describes the resulting path, and a Windows-only test verifies that LOCALAPPDATA wins when both variables are set.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: siddhesh2377

Merge Risk: ⚪ Minimal · up to 66ab3

Windows environments with HOME set now use LOCALAPPDATA for state and related logs, matching the documented behavior; no merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 3 files. (1 skipped: 1 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: prioritizing LOCALAPPDATA over HOME for state_dir() on Windows.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 3 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
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.

1 participant