fix: green Windows CI — POSIX display paths after ~/, and home isolation that holds on Windows - #296
Merged
Brian Krabach (bkrabach) merged 1 commit intoSep 3, 2026
Conversation
…lation that holds on Windows Windows CI has failed on main for every recent run (8 tests; e.g. run 33706023624 @ b85867c). Two independent root causes, neither a Windows-only product defect in routing behaviour -- but one IS a real display bug. 1. `routing list`/`show` rendered mixed separators (PRODUCT, 4 tests) `_display_path` abbreviated a home-relative path as `f"~/{path.relative_to(Path.home())}"`. On Windows the remainder stringifies with backslashes, so users saw the separator switch mid-path: ~/.amplifier\routing\openai.yaml ~/.amplifier\cache\amplifier-bundle-routing-matrix-test\routing\balanced.yaml in the terminal AND in the JSON `matrix_file` field -- while the CLI's own help text spells the same location `~/.amplifier/routing/...`. `~/` is a POSIX-style abbreviation; the remainder now renders via `as_posix()` on every platform. A path outside the home directory is not abbreviated and is left in its native form, unchanged. tests/test_routing_shadowing.py:249, :405 tests/test_routing_winner_selection.py:323, :400 2. Two `matrix_path` assertions were POSIX-specific (TEST, 2 tests) `matrix_path` is a native absolute path -- correctly; a user or tool may open it. `.endswith("/.amplifier/routing/openai.yaml")` can never hold against `C:\...\.amplifier\routing\openai.yaml`. Compare as a Path. tests/test_routing_shadowing.py:304 tests/test_routing_winner_selection.py:346 3. `monkeypatch.setenv("HOME", tmp_path)` isolates nothing on Windows (TEST, 2 failing tests -- and 47 sites in 5 files with the same latent hazard) `Path.home()` is `os.path.expanduser("~")`. On Windows, `ntpath.expanduser` reads USERPROFILE (then HOMEDRIVE+HOMEPATH) and never consults HOME. So every SessionStore-touching test in these files wrote real records into the runner's -- or a Windows developer's -- ACTUAL `~/.amplifier/projects/<slug>/sessions/`, and the two tests asserting `not SessionStore().exists(SUB_SESSION_ID)` found the record a previous checkpointing test had left there: tests/test_timedout_session_resumable.py:393, :670 Fixed once: `tests/conftest.py` gains `isolated_home` (sets HOME AND USERPROFILE, returns tmp_path). Each affected module opts in with a 3-line autouse fixture, and its per-test HOME lines are removed so there is one mechanism, not two: test_timedout_session_resumable.py 12 sites test_session_spawner.py 22 sites test_resume_preserves_provider_promotion.py 8 sites test_resume_system_prompt.py 4 sites test_skills_cli.py 1 site (custom path; USERPROFILE added beside it) The three files that were not failing had the identical defect -- their assertions merely never depended on the isolation holding. VERIFICATION No Windows host here, so each fix was checked against the EXACT values the CI log recorded, with Windows path semantics simulated via PureWindowsPath and ntpath.expanduser: _display_path old '~/.amplifier\\routing\\openai.yaml' new '~/.amplifier/routing/openai.yaml' (both CI values; outside-home path unchanged) matrix_path old .endswith(...)=False against the CI value; new Path == : True (and still True on POSIX) home HOME only -> ntpath.expanduser('~') = C:\Users\runneradmin HOME+USERPROFILE -> C:\Users\runneradmin\...\tmp_path Linux: 1647 pass (unchanged count), deterministic and random order. Two of the touched test files were not ruff-format clean on main; they were edited surgically (per-test line removals + the fixture) rather than reformatted, so the diff is the change and nothing else.
Brian Krabach (bkrabach)
deleted the
fix/windows-ci-path-and-home-isolation
branch
September 3, 2026 07:28
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.
Why
Windows CI has failed on
mainfor every recent run — 8 tests (e.g. run 33706023624 @b85867c). Two independent root causes. One is a real display bug; the rest are POSIX-only assumptions in tests, one of which is also a hygiene hazard for anyone running the suite on a Windows machine.1.
routing list/showrendered mixed separators — product bug, 4 tests_display_pathabbreviated a home-relative path asf"~/{path.relative_to(Path.home())}". On Windows the remainder stringifies with backslashes, so the separator switched mid-path — in the terminal and in the JSONmatrix_filefield:…while the CLI's own help text spells that location
~/.amplifier/routing/....~/is a POSIX-style abbreviation, so the remainder now renders viaas_posix()on every platform. A path outside the home directory isn't abbreviated and stays in native form — unchanged.test_routing_shadowing.py:249, :405·test_routing_winner_selection.py:323, :4002. Two
matrix_pathassertions were POSIX-specific — test bug, 2 testsmatrix_pathis a native absolute path — correctly; a user or tool may open it..endswith("/.amplifier/routing/openai.yaml")can never hold againstC:\...\.amplifier\routing\openai.yaml. Now compared as aPath.test_routing_shadowing.py:304·test_routing_winner_selection.py:3463.
monkeypatch.setenv("HOME", tmp_path)isolates nothing on Windows — test bug, 2 failing tests; 47 sites in 5 files with the same latent hazardPath.home()isos.path.expanduser("~"). On Windows,ntpath.expanduserreadsUSERPROFILE(thenHOMEDRIVE+HOMEPATH) and never consultsHOME. So everySessionStore-touching test in these files wrote real records into the runner's — or a Windows developer's — actual~/.amplifier/projects/<slug>/sessions/, and the two tests assertingnot SessionStore().exists(SUB_SESSION_ID)found the record a previous checkpointing test had left there.Fixed once:
tests/conftest.pygainsisolated_home(setsHOMEandUSERPROFILE, returnstmp_path). Each affected module opts in with a 3-line autouse fixture; its per-testHOMElines are removed so there is one mechanism, not two.test_timedout_session_resumable.py(the 2 failing)test_session_spawner.pytest_resume_preserves_provider_promotion.pytest_resume_system_prompt.pytest_skills_cli.py(custom path;USERPROFILEadded beside it)The three non-failing files had the identical defect — their assertions merely never depended on the isolation holding.
Verification
No Windows host available, so each fix was checked against the exact values the CI log recorded, with Windows path semantics simulated via
PureWindowsPath/ntpath.expanduser:_display_path~/.amplifier\\routing\\openai.yaml~/.amplifier/routing/openai.yaml(both CI values; outside-home unchanged)matrix_pathassert.endswith(...)→Falseon the CI valuePath ==→True(Windows and POSIX)HOMEonly →expanduser('~')=C:\Users\runneradminHOME+USERPROFILE→ the tmp pathLinux: 1647 pass (count unchanged), deterministic and random order. Two touched test files weren't
ruff format-clean onmain; they were edited surgically (per-test line removals + the fixture) rather than reformatted, so the diff is the change and nothing else.ruff checkclean on all 9 touched files; the 5 remaining repo-wide lint findings are pre-existing onmain.This PR should be merged only when the Windows jobs are actually green — that's the point of it.