fix: handle Windows path separators in the TS layer - #17
Merged
SuperLogicAI merged 1 commit intoSep 6, 2026
Merged
Conversation
Two spots assumed POSIX paths and a `/` filesystem root. The Accomplished panel built its filename by splitting on `/`, so a Windows `file_path` was never shortened and the row rendered the whole `C:\Users\x\dev\proj\main.rs` instead of `main.rs`. The basename moves out of listToolEvents into an exported `basename()` — it had to be exported to be testable, which matches how dedupeKey, scopeBySession and latestPerTether are already covered. The one that matters is the root guard in bindSession. It refuses `/` because an untethered session with a rootless cwd would otherwise bind to an arbitrary tab and overwrite its cwd with a key that matches nothing, blanking the panel. A Windows drive root is the identical hazard and walked straight through. The replacement is anchored and exact rather than a prefix test, on purpose: over-rejecting here would silently stop every real Windows session from binding, which is a worse bug and a far quieter one than the drive root the guard exists to catch. So bind-check now covers both directions — five rootless keys that must be refused, and two real Windows project paths that must still bind. Both new cases were run against the old code first, to be sure they could fail: bind-check with "a rootless session (C:\) hijacked the active tab", scope-check with "backslash path not shortened". scope-check was the nearest existing home for the basename cases, so its header widens from "panel session scoping" to "panel row shaping" rather than adding a tenth check script. Out of scope, still Mac-only, tracked in ROADMAP § "Windows port": hook_command's `sh -c`, pty_spawn's `$SHELL`, clipboard.rs's pbpaste, and the Rust-side $HOME lookups (SuperLogicAI#7). npx tsc --noEmit clean; all nine check scripts pass. Closes SuperLogicAI#8 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WyeecwdT2gD3fxeWkJpTKa
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.
Closes #8.
Two spots assumed POSIX paths and a
/filesystem root.1. Accomplished panel filename —
src/lib/repo.tsBuilt its filename by splitting on
/, so a Windowsfile_pathwas never shortened and the row rendered the wholeC:\Users\x\dev\proj\main.rsinstead ofmain.rs.The basename moves out of
listToolEventsinto an exportedbasename(). It had to be exported to be testable, which matches howdedupeKey,scopeBySessionandlatestPerTetherare already covered.2. The root guard —
src/lib/ingest.tsThis is the half that matters.
bindSessionrefuses/because an untethered session with a rootless cwd would otherwise bind to an arbitrary tab and overwrite its cwd with a key that matches nothing, blanking the panel. A Windows drive root is the identical hazard and walked straight through.The replacement is anchored and exact rather than a prefix test, deliberately: over-rejecting here would silently stop every real Windows session from binding — a worse bug, and a far quieter one, than the drive root the guard exists to catch.
So
bind-checknow covers both directions:C:\,C:/,C:,\,d:\)The second group is the one I'd actually want reviewed.
Both new cases were run against the old code first
Tests that can't fail aren't worth adding, so I downgraded both lines to the pre-fix logic and confirmed the new assertions catch the real bugs:
bind-check→AssertionError: a rootless session (C:\) hijacked the active tabscope-check→AssertionError: backslash path not shortenedRestored; both green.
A judgement call worth flagging
The issue says where the guard's case belongs (
bind-check, not a new harness) but not where the basename case goes.scope-checkwas the nearest existing home, so its header widens from "panel session scoping" to "panel row shaping" rather than adding a tenth check script. Happy to move it if you'd rather it lived elsewhere.Explicitly out of scope
Still Mac-only, tracked in
docs/ROADMAP.md§ "Windows port":hook_command'ssh -c,pty_spawn's$SHELL,clipboard.rs'spbpaste, and the Rust-side$HOMElookups (#7).Verification
npx tsc --noEmitclean; all nine check scripts pass on this branch.🤖 Generated with Claude Code
https://claude.ai/code/session_01WyeecwdT2gD3fxeWkJpTKa