Skip to content

Windows compat pass: utf-8 everywhere, hardened guards, windows-latest CI - #8

Merged
danielrmay merged 3 commits into
mainfrom
windows-compat
Jun 10, 2026
Merged

danielrmay merged 3 commits into
mainfrom
windows-compat

Conversation

@danielrmay

Copy link
Copy Markdown
Owner

Closes #6.

No Windows machine was available, so this pass has two legs: fix what a static audit found, and convert as much as possible from "static squint" into empirical evidence via CI.

Fixed (static audit)

  • encoding="utf-8" on all file I/O in mailbox.py, protocol_status.py, protocol_init.py, upstream_audit.py (16 user-facing call sites; mcp_server.py was already compliant). Windows' cp1252 locale default corrupts unicode markdown — em-dashes, emoji, CJK. Docstring-noted per the vendored contract; listed as an upstream-candidate bugfix since upstream omits it too. PYTHONUTF8=1 joins .mcp.json env as defense in depth.
  • Traversal guards → Path.is_relative_to in the MCP server (3 sites): the upstream string-prefix comparison is defeated by a sibling directory named .clarity-protocol-evil on any OS, and NTFS case-insensitivity weakens it further. New sibling-prefix regression test; upstream candidate.
  • .gitattributes pins LF so core.autocrlf=true checkouts don't mangle shell scripts or the byte-compared vendored files.

CI-verified on Windows (the empirical leg)

Tier 1 now runs a [ubuntu-latest, windows-latest] matrix. The Windows job is real evidence, not a smoke wave: the MCP protocol tests spawn the actual server over stdio, and the new tests/test_unicode_roundtrip.py pushes unicode through every recording path under Windows' cp1252 default (PYTHONUTF8 deliberately stays out of CI — it would mask exactly these bugs). The POSIX-only e2e harness tests skip on win32 with a documented reason.

Not validated (needs a real Windows machine)

Claude Code natively running the plugin on Windows: the Bash tool needs Git for Windows, and .mcp.json invokes python3, which python.org installers don't register (python/py only; the Microsoft Store build does). Switching to python would break stock macOS, so python3 stays and the README prerequisites now document the requirement and remedies. This residual is stated in README/TESTING.md rather than claimed.

Verification

261 Tier 1 tests + audit + plugin validate locally; 10-record e2e passes on macOS (no runtime regression from the encoding/guard edits); windows-latest job green on this PR.

🤖 Generated with Claude Code

Static audit fixes plus an empirical leg:

- encoding="utf-8" on all file I/O in the protocol scripts (mailbox.py,
  protocol_status.py, protocol_init.py, upstream_audit.py; mcp_server.py
  was already compliant) — Windows' cp1252 locale default corrupts
  unicode markdown. Docstring-noted per the vendored contract and listed
  as an upstream-candidate bugfix (upstream omits it too). PYTHONUTF8=1
  added to .mcp.json env as defense in depth for the server process.
- Path-traversal guards in mcp_server.py move from string-prefix
  comparison to Path.is_relative_to: a sibling dir named
  .clarity-protocol-evil passes a startswith check on any OS, and NTFS
  case-insensitivity weakens it further (upstream candidate; new
  sibling-prefix regression test).
- .gitattributes pins LF so autocrlf checkouts don't mangle shell
  scripts or byte-compared vendored files.
- CI: Tier 1 now runs a [ubuntu-latest, windows-latest] matrix — the
  Windows job is the empirical evidence for the Python layer (the MCP
  protocol tests spawn the real server; the new unicode round-trip
  tests run under Windows' cp1252 default, so PYTHONUTF8 stays OUT of
  CI). The POSIX-only e2e harness tests skip on win32.
- Docs (README/TESTING/CLAUDE.md) state the honest boundary: Python
  layer CI-verified on Windows; Claude Code natively spawning the
  plugin's python3 MCP server on Windows remains unvalidated (python.org
  installers don't register python3 — remedies noted).

261 Tier 1 tests, audit, plugin validate, and a 10-record e2e pass on
macOS.
First windows-latest run caught five failures, all in
test_plugin_structure.py itself (the product layer passed): bare
read_text() hitting cp1252 on the em-dash-rich vendored markdown, and
relative_to() emitting backslashes against the forward-slash fixture
manifests. encoding=utf-8 + as_posix(). Other bare read_text() calls in
the vendored tests read their own ASCII content and stay untouched
(closer to upstream; fix-on-real-failure per the plan).
Round 2 on windows-latest: test_vendored_files_all_watched compared
backslash relative paths against the forward-slash watch set and prefix
exclusions. as_posix() at the comparison sites.
@danielrmay

Copy link
Copy Markdown
Owner Author

windows-latest is green as of 8c7c103. For the record, the first real Windows runs caught two genuine portability classes beyond the static audit — both in the test suite itself, none in the product layer:

  1. Bare read_text() in the structural tests hitting cp1252 on the em-dash-rich vendored markdown (the same class the audit fixed in scripts/).
  2. relative_to() emitting backslashes against forward-slash manifests and watch-set comparisons (as_posix() at the comparison sites).

Final state: 251 passing + 10 skipped (the POSIX-only harness tests) on Windows; 261 passing on ubuntu/macOS.

@danielrmay

Copy link
Copy Markdown
Owner Author

Consciously deferred, for the record (reviewed before calling the pass done):

  • Tier 0 on Windows: plugin-validate stays ubuntu-only. It would prove CLI-side plugin loading on Windows, but manifest parsing is platform-neutral JSON and it would not exercise the MCP spawn — modest value for the CI maintenance cost. Revisit if a Windows user reports a loading issue.
  • Reserved device filenames (CON/NUL/AUX…): a non-issue by construction — every generated filename carries a timestamp or decision-NN- prefix, and Windows' reserved-name rules only apply to the bare stem. Slugs are also a-z0-9 only. No code needed.
  • Long paths (MAX_PATH): protocol trees are shallow; the windows-latest suite ran clean. No action.
  • The real unknown remains hardware-bound: Claude Code natively spawning the server via python3 — documented in README prerequisites, needs an actual Windows machine.

@danielrmay
danielrmay merged commit f953364 into main Jun 10, 2026
3 checks passed
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.

Windows compat pass

1 participant