Skip to content

fix(core): stop instruction walk at case-variant Windows project roots - #53349

Merged
neriousy merged 1 commit into
v2from
instruction-drive-case
Oct 5, 2026
Merged

neriousy merged 1 commit into
v2from
instruction-drive-case

Conversation

@neriousy

@neriousy neriousy commented Oct 5, 2026

Copy link
Copy Markdown
Member

Issue for this PR

Closes #52874
Closes #50296
Closes #50750
Closes #50857

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

On Windows, a location's directory keeps whatever spelling the client's CWD had, while the project root comes from git rev-parse --show-toplevel. Starting opencode from cmd after cd /d c:\repo, from a process spawned with a c:\ cwd (IDE terminals, scripts), or via opencode c:\repo gives c:\repo vs C:\repo. PowerShell and Git Bash normalize the drive, which is why this is intermittent across users.

FSUtil.contains(root, start) accepts the pair (path.relative is case-insensitive on win32), but ancestorDirectories compared with ===, never reached stop, and recursed at the drive root (dirname("c:\\") === "c:\\") until the stack overflowed. The plugin's activation guard then marked core/instructions unavailable, so every prompt in that location failed with InitializationBlocked. Non-git directories aren't affected because both sides come from the same fs.resolve.

The fix compares the stop boundary with the existing sameDirectory helper from @opencode/util/path (case-insensitive for Windows drive/UNC paths, unchanged on POSIX):

  • ancestorDirectories in the instruction plugin, which fixes the crash.
  • FSUtil.up / globUp. Without this, the instruction source's fs.up walk also misses stop and reads AGENTS.md files above the project root, up to c:\. The same up boundary is used by the formatter config lookup (findUp(target, directory, worktree)).

Compared to #50544, which bounds the walk at the filesystem root: that also stops the overflow, but the walk then continues past the project root, so instruction watchers and AGENTS.md reads extend to the drive root.

This doesn't address the duplicate-Location side of the casing problem (#47839). The server still keys c:\repo and C:\repo as separate locations.

How did you verify your code works?

  • Reproduced on Windows 11 / Bun 1.4.2 with a regression test in instruction-discovery.test.ts (location c:\...\repo, project C:\...\repo, plus an AGENTS.md above the project). It runs only on win32, which the unit CI matrix covers.
    • Without the fix: fails with Instructions.InitializationBlocked: ... core/instructions.
    • With only the ancestorDirectories change: fails because the AGENTS.md above the project root is included.
    • With both changes: passes.
  • bun test for instruction, project, formatter, git, read-tool, config and skill-discovery tests in packages/core, and bun test in packages/util. The only failures (Config > excludes home-level claude..., Config > deduplicates global ecosystem..., global > ... canonicalizes it) fail identically on v2 without this change on this Windows machine.
  • bun run check passes, and prettier reports the changed files as formatted.

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

A location directory keeps the client's spelling (a CWD such as c:\repo
from cmd or an IDE-spawned process) while the project root comes from
git as C:\repo. FSUtil.contains accepts the pair because path.relative
is case-insensitive on Windows, but ancestorDirectories compared with
===, never reached stop, and recursed at the drive root until the stack
overflowed. Instruction activation then failed and every prompt in that
location was blocked by InitializationBlocked.

Compare the stop boundary with sameDirectory in ancestorDirectories and
in FSUtil.up/globUp, so the walk ends at the project root instead of
continuing to the drive root and reading AGENTS.md files outside it.
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