Background
Windows isn't currently CI-tested or formally documented as supported.
This issue proposes promoting Windows from implicitly unsupported to
CI-tested, motivated by PR #633 — which fixed 5 tests (one of which
propagates to a 14-test class via its fixture) that hit
OSError: [WinError 1314] (client does not hold the required privilege)
because they call os.symlink / Path.symlink_to. CI never noticed
because every job in .github/workflows/ci.yml runs on ubuntu-latest.
There's no broken cross-platform promise to fix here — README.md,
packages/memtomem/README.md, and CONTRIBUTING.md don't claim Windows
support today. The relevant prior art is the implicit POSIX-only stance
of the mm: command not found README tip (which mentions ~/.local/bin
on macOS/Linux only). What this issue proposes is a deliberate step
toward supporting Windows by adding the CI signal first.
There are likely other latent Windows-specific issues hiding behind the
ubuntu-only blind spot — see "Likely first-run failures" below.
Proposal
Add windows-latest (and probably macos-latest while we're at it) to
selected jobs in .github/workflows/ci.yml. The repo currently has 6
jobs; per-job recommendations:
| Job |
OS matrix recommendation |
Why |
lint |
ubuntu only |
Ruff output is identical across OSes; matrix would just slow PRs |
typecheck |
ubuntu only |
mypy is platform-agnostic (advisory anyway per CLAUDE.md) |
test |
[ubuntu-latest, windows-latest, macos-latest] |
The actual surface this issue is about |
golden-path (ONNX bge-m3) |
matrix |
Cache key already uses ${{ runner.os }} — prepared for it; ONNX/fastembed has known cross-platform behavior worth pinning |
llm-nightly (optional) |
ubuntu only |
Opt-in nightly, not on PR fast path |
notebooks |
matrix (low priority) |
Jupyter on Windows has its own quirks; defer until base test job is green |
Concretely:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
Notes on cost / latency
Actions minutes are free and unlimited for public repos (this repo
is public), so the 2x/10x billing multipliers for Windows/macOS don't
apply here. The real concern is PR feedback wallclock: Windows
runners take longer to provision and Python runs measurably slower on
Windows. Adding 6 jobs × 3 OSes = 18 jobs may slow PR turnaround even
when the bill is zero. The per-job matrix above keeps the fast-feedback
path (lint + typecheck) on a single OS for that reason.
Developer Mode default
GitHub-hosted windows-latest runners (currently windows-2022) have
Developer Mode enabled by default out of the box — verified against
the actions/runner-images manifest, has been true since 2021. So PR
#633's requires_symlinks marker will run the symlink tests on
Windows CI, not skip them, which is what we want.
Likely first-run failures
First Windows CI run will likely be red on more than just symlinks —
that's the point of adding the runner. Known surfaces to expect:
- Hardcoded POSIX paths in tests.
Path("/tmp/test.md") and
similar appear in 9 test files: test_reranker_fastembed.py,
test_uninstall_cmd.py, test_ask.py, test_indexing_engine.py
(×4 sites), test_runtime_paths.py. Most are metadata-only
(source_file=Path(...) passed without being opened), so some may
pass on Windows by accident — worth triaging real bugs vs. cosmetic.
- POSIX file-mode bits.
tests/test_runtime_paths.py does
mode=0o700 mkdir + mode=0o755 checks (4 sites). NTFS doesn't
translate POSIX modes cleanly; the whole XDG_RUNTIME_DIR test class
may need a Windows skip + a Windows-equivalent test.
mm web PID-file handling. _runtime_paths.py has POSIX-first
XDG fallback to tempfile.gettempdir(), but the Windows fallback
branch isn't exercised on CI today.
- Line endings (CRLF).
git config core.autocrlf true is the
Windows default. Tests doing exact-byte comparison
(e.g. assert (dest / "SKILL.md").read_bytes() == b"# foo skill\n"
in test_install_skill_copies_tree) will break if checkout
normalizes to CRLF. .gitattributes doesn't pin text eol=lf
today — would need either pinning or test rewrite.
uv tool install shim path. README's mm: command not found
troubleshooting tip mentions ~/.local/bin (POSIX-only) — the
Windows shim path differs (%APPDATA%\uv\tools\...). Once Windows
is CI-tested, the user-facing install flow is also worth covering.
- Subprocess invocations that assume a POSIX shell.
Acceptance criteria
References
🤖 Generated with Claude Code
Background
Windows isn't currently CI-tested or formally documented as supported.
This issue proposes promoting Windows from implicitly unsupported to
CI-tested, motivated by PR #633 — which fixed 5 tests (one of which
propagates to a 14-test class via its fixture) that hit
OSError: [WinError 1314] (client does not hold the required privilege)because they call
os.symlink/Path.symlink_to. CI never noticedbecause every job in
.github/workflows/ci.ymlruns onubuntu-latest.There's no broken cross-platform promise to fix here —
README.md,packages/memtomem/README.md, andCONTRIBUTING.mddon't claim Windowssupport today. The relevant prior art is the implicit POSIX-only stance
of the
mm: command not foundREADME tip (which mentions~/.local/binon macOS/Linux only). What this issue proposes is a deliberate step
toward supporting Windows by adding the CI signal first.
There are likely other latent Windows-specific issues hiding behind the
ubuntu-only blind spot — see "Likely first-run failures" below.
Proposal
Add
windows-latest(and probablymacos-latestwhile we're at it) toselected jobs in
.github/workflows/ci.yml. The repo currently has 6jobs; per-job recommendations:
linttypecheckCLAUDE.md)test[ubuntu-latest, windows-latest, macos-latest]golden-path (ONNX bge-m3)${{ runner.os }}— prepared for it; ONNX/fastembed has known cross-platform behavior worth pinningllm-nightly (optional)notebooksConcretely:
Notes on cost / latency
Actions minutes are free and unlimited for public repos (this repo
is public), so the 2x/10x billing multipliers for Windows/macOS don't
apply here. The real concern is PR feedback wallclock: Windows
runners take longer to provision and Python runs measurably slower on
Windows. Adding 6 jobs × 3 OSes = 18 jobs may slow PR turnaround even
when the bill is zero. The per-job matrix above keeps the fast-feedback
path (
lint+typecheck) on a single OS for that reason.Developer Mode default
GitHub-hosted
windows-latestrunners (currentlywindows-2022) haveDeveloper Mode enabled by default out of the box — verified against
the actions/runner-images manifest, has been true since 2021. So PR
#633's
requires_symlinksmarker will run the symlink tests onWindows CI, not skip them, which is what we want.
Likely first-run failures
First Windows CI run will likely be red on more than just symlinks —
that's the point of adding the runner. Known surfaces to expect:
Path("/tmp/test.md")andsimilar appear in 9 test files:
test_reranker_fastembed.py,test_uninstall_cmd.py,test_ask.py,test_indexing_engine.py(×4 sites),
test_runtime_paths.py. Most are metadata-only(
source_file=Path(...)passed without being opened), so some maypass on Windows by accident — worth triaging real bugs vs. cosmetic.
tests/test_runtime_paths.pydoesmode=0o700mkdir +mode=0o755checks (4 sites). NTFS doesn'ttranslate POSIX modes cleanly; the whole
XDG_RUNTIME_DIRtest classmay need a Windows skip + a Windows-equivalent test.
mm webPID-file handling._runtime_paths.pyhas POSIX-firstXDG fallback to
tempfile.gettempdir(), but the Windows fallbackbranch isn't exercised on CI today.
git config core.autocrlf trueis theWindows default. Tests doing exact-byte comparison
(e.g.
assert (dest / "SKILL.md").read_bytes() == b"# foo skill\n"in
test_install_skill_copies_tree) will break if checkoutnormalizes to CRLF.
.gitattributesdoesn't pintext eol=lftoday — would need either pinning or test rewrite.
uv tool installshim path. README'smm: command not foundtroubleshooting tip mentions
~/.local/bin(POSIX-only) — theWindows shim path differs (
%APPDATA%\uv\tools\...). Once Windowsis CI-tested, the user-facing install flow is also worth covering.
Acceptance criteria
.github/workflows/ci.ymlruns thetestjob onwindows-latest(andmacos-latest).marked with the appropriate platform skip + a tracking
sub-issue.
(this is the moment Windows becomes a documented promise).
References
🤖 Generated with Claude Code