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>
render_preview validates markup and hands it to the browser, which renders it
in an opaque-origin sandboxed iframe. Nothing executes server-side. That model
fits HTML/SVG/JSX and cannot fit C, Rust or Erlang, which need a real
toolchain - so those get a second tool instead of a widened first one.
The split is the feature: the model picks a track by picking a tool, rather
than picking a `lang` value from an enum where half the entries run
server-side and half do not.
synapse/code_run.py compiles and runs one file in a throwaway directory and
returns a ```nexus-run fence carrying the source and its captured output
together, so a model cannot paste output without the code that produced it.
Backticks in the source are re-encoded as ` - still valid JSON, and it
cannot close the fence early.
It is not a sandbox, and the module docstring says so up front. What it gives
is containment by layers: consent (an action tool, gated by
action_tool_policy, per-call Approve/Deny on "ask"), static screening, a
scrubbed environment in a temp dir, wall-clock and POSIX rlimits, and a
network namespace on Linux where unprivileged userns are available. Screening
is a tripwire against a model reaching for `requests` out of habit, not a
boundary against an adversary; layers 1 and 3-5 are the load-bearing ones.
Backend RUN_LANGS and frontend run-langs.js are separate registries because
the two sides need different things - one executes, one labels - and neither
should depend on the other at runtime. tests/test_tools.py asserts the key
sets and the fence tag stay equal, so drift fails the gate instead of
rendering a run result under the wrong language.
tests/snippet_probes/ is a data catalog rather than inlined cases, so adding a
language is a data change and the meta-tests can assert every RUN_LANGS key
has both a smoke probe and a screening probe. Probes skip cleanly on hosts
without the toolchain.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>