fix(windows): real toolchain probing, HOME/TEMP env, and gate portability

code_run.py: shutil.which() finding a compiler executable on PATH doesn't
mean it's a usable toolchain on Windows -- rustc's MSVC target also needs
Microsoft's linker, and an MSYS2 gcc/clang driver can remain resolvable after
one of its runtime DLLs has broken. Both cases silently turned every C/C++/
Rust snippet into a compile error while the capability check said "ready".
_compiled_tool() now actually compiles+links a trivial known-good program
per candidate (Windows only; POSIX keeps the cheap which(1) check since
release hosts install compiler packages atomically) and caches the result.

Also fixes the run/compile child environment: HOME/TMPDIR don't control
Windows' real temp/profile resolution (expanduser() reaches the actual user
profile, GetTempPath() falls back to the Windows directory), letting a
snippet escape the scratch directory or fail outright. _child_env() now also
sets TEMP/TMP/USERPROFILE on Windows.

tests/conftest.py (new): isolates curry_store's SQLite singleton into a
per-run temp directory via NEXUS_CURRY_DB before any test module imports
synapse, and cleans it up at session end -- the release gate no longer
writes test constants into the checkout's live data/curry.db. .gitignore
picks up /data/curry.db for whatever still lands there locally.

bin/check.sh: falls back to Promethean/Scripts/python.exe when
Promethean/bin/python doesn't exist, so the gate actually runs on a Windows
venv instead of immediately exiting "no Promethean venv".

Verified independently: 254 passed, 0 failed, 8 skipped (tests + management)
-- the 12 C/C++/Rust toolchain failures present all session are gone. Full
bin/check.sh run end-to-end on this Windows checkout: pytest, eslint,
frontend node:test (57/57), PowerShell/shell parse, and the wheel/sdist
packaging + twine + content checks all report OK.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-26 01:37:52 -05:00
co-authored by Claude Sonnet 5
parent cac6e636fc
commit dd3ce09feb
5 changed files with 128 additions and 14 deletions
+23
View File
@@ -0,0 +1,23 @@
"""Test-process isolation for persistent runtime state."""
from __future__ import annotations
import os
import shutil
import sys
import tempfile
from pathlib import Path
# curry_store constructs its SQLite singleton during test collection. Point it
# at a per-run directory before any test module imports synapse, so the release
# gate is repeatable and never writes test constants into the checkout's live
# data/curry.db.
_TEST_STATE = Path(tempfile.mkdtemp(prefix="nexus-pytest-"))
os.environ["NEXUS_CURRY_DB"] = str(_TEST_STATE / "curry.db")
def pytest_sessionfinish(session, exitstatus):
module = sys.modules.get("synapse.curry_store")
if module is not None:
module.curry_db.close()
shutil.rmtree(_TEST_STATE, ignore_errors=True)
+30
View File
@@ -232,10 +232,40 @@ def test_home_points_at_the_scratch_dir():
assert "nexus-run-" in out["stdout"]
def test_temp_points_at_the_scratch_dir():
"""Compilers and snippets must not fall back to a host temp directory."""
out = code_run.run("python", "import tempfile\nprint(tempfile.gettempdir())")
assert "nexus-run-" in out["stdout"]
# ---------------------------------------------------------------------------
# Toolchains
# ---------------------------------------------------------------------------
def test_windows_compiler_resolver_skips_broken_candidates(monkeypatch):
"""An executable on PATH is not enough when its linker/runtime is broken."""
code_run._compiled_tool.cache_clear()
monkeypatch.setattr(code_run.sys, "platform", "win32")
monkeypatch.setattr(
code_run.shutil, "which", lambda name: f"C:\\tools\\{name}.exe"
)
probes = []
def probe(lang, tool):
probes.append((lang, tool))
return tool.endswith("clang.exe")
monkeypatch.setattr(code_run, "_probe_compiled_tool", probe)
try:
assert code_run._compiled_tool("c", ("gcc", "clang")) == "C:\\tools\\clang.exe"
assert probes == [
("c", "C:\\tools\\gcc.exe"),
("c", "C:\\tools\\clang.exe"),
]
finally:
code_run._compiled_tool.cache_clear()
def test_a_missing_toolchain_explains_itself(monkeypatch):
"""The model has to be able to tell 'you cannot run this here' from 'your
code is wrong' — otherwise it rewrites a correct program repeatedly."""