From bc6c542b88daed8ac33ec4d8f9b5c035693217c8 Mon Sep 17 00:00:00 2001 From: Ram Dwivedi Date: Tue, 16 Jun 2026 23:39:01 -0400 Subject: [PATCH] fix(build_context): use forward-slash component paths (cross-platform) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build_context emitted OS-native path separators for relative component paths, producing backslashes on Windows (e.g. `references\guide.md`). These paths are used as dict keys (components / file_cache / component_metadata) and as SARIF physicalLocation URIs, which are meant to be portable forward-slash paths — so on Windows the report locations were non-portable and downstream lookups (and the test suite) mismatched. Use Path.as_posix() so component paths are forward-slash on every OS, in build_context and the handful of test helpers that mirror the same relative_to logic. Behaviour-preserving on Linux/macOS (str() already yields forward slashes). Fixes #86 Co-Authored-By: Claude Opus 4.8 Signed-off-by: Ram Dwivedi --- src/skillspector/nodes/build_context.py | 5 ++++- tests/nodes/analyzers/test_semantic_developer_intent.py | 2 +- tests/nodes/analyzers/test_semantic_security_discovery.py | 2 +- tests/nodes/test_semantic_quality_policy.py | 2 +- tests/test_mcp_least_privilege.py | 2 +- tests/test_mcp_tool_poisoning.py | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/skillspector/nodes/build_context.py b/src/skillspector/nodes/build_context.py index 694a048..225c26d 100644 --- a/src/skillspector/nodes/build_context.py +++ b/src/skillspector/nodes/build_context.py @@ -87,7 +87,10 @@ def _walk_skill_files(skill_dir: Path) -> list[str]: continue try: rel = item.relative_to(skill_dir) - paths.append(str(rel)) + # Use forward slashes on every OS: these relative paths are dict keys + # and SARIF/URI locations, so they must be portable (not OS-specific + # backslashes on Windows). + paths.append(rel.as_posix()) except ValueError: logger.debug("Skipping path (not under skill_dir): %s", item) continue diff --git a/tests/nodes/analyzers/test_semantic_developer_intent.py b/tests/nodes/analyzers/test_semantic_developer_intent.py index 0ddc704..408daa9 100644 --- a/tests/nodes/analyzers/test_semantic_developer_intent.py +++ b/tests/nodes/analyzers/test_semantic_developer_intent.py @@ -342,7 +342,7 @@ def _build_file_cache(skill_dir: Path) -> dict[str, str]: for item in sorted(skill_dir.rglob("*")): if not item.is_file(): continue - rel = str(item.relative_to(skill_dir)) + rel = item.relative_to(skill_dir).as_posix() # forward slashes on every OS try: cache[rel] = item.read_text(encoding="utf-8", errors="replace") except OSError: diff --git a/tests/nodes/analyzers/test_semantic_security_discovery.py b/tests/nodes/analyzers/test_semantic_security_discovery.py index 85209f6..5b0c53b 100644 --- a/tests/nodes/analyzers/test_semantic_security_discovery.py +++ b/tests/nodes/analyzers/test_semantic_security_discovery.py @@ -317,7 +317,7 @@ def _build_file_cache(skill_dir: Path) -> dict[str, str]: for item in sorted(skill_dir.rglob("*")): if not item.is_file(): continue - rel = str(item.relative_to(skill_dir)) + rel = item.relative_to(skill_dir).as_posix() # forward slashes on every OS try: cache[rel] = item.read_text(encoding="utf-8", errors="replace") except OSError: diff --git a/tests/nodes/test_semantic_quality_policy.py b/tests/nodes/test_semantic_quality_policy.py index f80960b..9d52d33 100644 --- a/tests/nodes/test_semantic_quality_policy.py +++ b/tests/nodes/test_semantic_quality_policy.py @@ -289,7 +289,7 @@ def _build_file_cache(skill_dir: Path) -> dict[str, str]: for item in sorted(skill_dir.rglob("*")): if not item.is_file(): continue - rel = str(item.relative_to(skill_dir)) + rel = item.relative_to(skill_dir).as_posix() # forward slashes on every OS try: cache[rel] = item.read_text(encoding="utf-8", errors="replace") except OSError: diff --git a/tests/test_mcp_least_privilege.py b/tests/test_mcp_least_privilege.py index 9e7852e..8195986 100644 --- a/tests/test_mcp_least_privilege.py +++ b/tests/test_mcp_least_privilege.py @@ -98,7 +98,7 @@ def _make_state(fixture_name: str) -> dict: continue if item.name.startswith(".") and not item.name.startswith(".claude"): continue - rel = str(item.relative_to(fixture_dir)) + rel = item.relative_to(fixture_dir).as_posix() # forward slashes on every OS components.append(rel) components.sort() diff --git a/tests/test_mcp_tool_poisoning.py b/tests/test_mcp_tool_poisoning.py index 7d5b524..428897c 100644 --- a/tests/test_mcp_tool_poisoning.py +++ b/tests/test_mcp_tool_poisoning.py @@ -121,7 +121,7 @@ def _make_state( continue if item.name.startswith(".") and not item.name.startswith(".claude"): continue - rel = str(item.relative_to(fixture_dir)) + rel = item.relative_to(fixture_dir).as_posix() # forward slashes on every OS components.append(rel) components.sort()