Files
NexusOS/tests/snippet_probes/catalog.py
T
Athena KaminskyandClaude Opus 5 affba1805c feat(chat): add run_snippet, an execution track beside the render track
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>
2026-08-20 14:30:03 -05:00

275 lines
7.9 KiB
Python

"""Catalog of code-snippet probes for the execution track.
Each probe is a small, self-contained program (or deliberate reject) that the
suite in tests/test_snippet_probes.py runs automatically. Keeping the corpus
here — not inlined in the test file — means adding a language or a case is a
data change, and the meta-tests can assert every RUN_LANGS key is covered.
"""
from __future__ import annotations
from dataclasses import dataclass, field
@dataclass(frozen=True)
class Probe:
"""One automatic snippet probe.
kind:
run — critique must be clean, then code_run.run (skip if no toolchain)
screen — critique must report at least one issue matching needle;
never executed
tool — same as run, but also through tools.dispatch("run_snippet")
"""
id: str
lang: str
source: str
kind: str = "run"
expect_stdout: str | None = None # exact strip() match when set
expect_stdout_contains: tuple[str, ...] = ()
expect_stderr_contains: tuple[str, ...] = ()
expect_exit: int | None = 0 # None = don't care; run-ok can be nonzero
expect_ok: bool = True # False => stage failure (compile/timeout/…)
expect_stage: str | None = None
screen_needle: str | None = None # required substring in critique issues
stdin: str = ""
tags: tuple[str, ...] = field(default_factory=tuple)
# ---------------------------------------------------------------------------
# Corpus — keep each source short; the suite runs every probe on every check.
# ---------------------------------------------------------------------------
PROBES: tuple[Probe, ...] = (
# --- python (always available) -----------------------------------------
Probe(
id="py-hello",
lang="python",
source="print('probe-ok', 6 * 7)",
expect_stdout="probe-ok 42",
tags=("smoke", "python"),
),
Probe(
id="py-stdin",
lang="python",
source="import sys\nprint(sys.stdin.read().strip().upper())",
stdin="nexus\n",
expect_stdout="NEXUS",
tags=("stdin", "python"),
),
Probe(
id="py-nonzero-exit",
lang="python",
source="import sys\nprint('before')\nsys.exit(3)",
expect_stdout="before",
expect_exit=3,
tags=("exit", "python"),
),
Probe(
id="py-traceback",
lang="python",
source="print(1 / 0)",
expect_exit=None, # nonzero, exact code is interpreter-dependent enough
expect_stderr_contains=("ZeroDivisionError",),
tags=("stderr", "python"),
),
Probe(
id="py-alias-py",
lang="py",
source="print('alias')",
expect_stdout="alias",
tags=("alias", "python"),
),
Probe(
id="py-tool-envelope",
lang="python",
source="print('via-tool', 2 + 2)",
kind="tool",
expect_stdout="via-tool 4",
tags=("tool", "python"),
),
Probe(
id="py-screen-network",
lang="python",
source="import socket\nprint(socket.gethostname())",
kind="screen",
screen_needle="network",
tags=("screen", "python"),
),
Probe(
id="py-screen-subprocess",
lang="python",
source="import subprocess\nsubprocess.run(['true'])",
kind="screen",
screen_needle="processes",
tags=("screen", "python"),
),
# --- c -----------------------------------------------------------------
Probe(
id="c-hello",
lang="c",
source=(
"#include <stdio.h>\n"
"int main(void) {\n"
' printf("c-probe %d\\n", 6 * 7);\n'
" return 0;\n"
"}\n"
),
expect_stdout="c-probe 42",
tags=("smoke", "c"),
),
Probe(
id="c-math",
lang="c",
source=(
"#include <stdio.h>\n"
"#include <math.h>\n"
"int main(void) {\n"
' printf("%.0f\\n", sqrt(144.0));\n'
" return 0;\n"
"}\n"
),
expect_stdout="12",
tags=("libm", "c"),
),
Probe(
id="c-compile-error",
lang="c",
source="int main(void) { return nope; }\n",
expect_ok=False,
expect_stage="compile",
expect_exit=None,
expect_stderr_contains=("nope",),
tags=("compile", "c"),
),
Probe(
id="c-screen-system",
lang="c",
source='#include <stdlib.h>\nint main(void){ system("true"); return 0; }\n',
kind="screen",
screen_needle="processes",
tags=("screen", "c"),
),
Probe(
id="c-screen-no-main",
lang="c",
source="int add(int a){ return a + 1; }\n",
kind="screen",
screen_needle="main()",
tags=("screen", "c"),
),
# --- cpp ---------------------------------------------------------------
Probe(
id="cpp-hello",
lang="cpp",
source=(
"#include <iostream>\n"
"int main() {\n"
' std::cout << "cpp-probe " << (6 * 7) << "\\n";\n'
" return 0;\n"
"}\n"
),
expect_stdout="cpp-probe 42",
tags=("smoke", "cpp"),
),
Probe(
id="cpp-alias",
lang="c++",
source=(
"#include <iostream>\n"
"int main(){ std::cout << 9 << \"\\n\"; }\n"
),
expect_stdout="9",
tags=("alias", "cpp"),
),
Probe(
id="cpp-screen-socket",
lang="cpp",
source="#include <sys/socket.h>\nint main(){ return 0; }\n",
kind="screen",
screen_needle="network",
tags=("screen", "cpp"),
),
# --- rust --------------------------------------------------------------
Probe(
id="rust-hello",
lang="rust",
source='fn main() { println!("rust-probe {}", (1..=10).sum::<i32>()); }\n',
expect_stdout="rust-probe 55",
tags=("smoke", "rust"),
),
Probe(
id="rust-alias-rs",
lang="rs",
source='fn main() { println!("rs"); }\n',
expect_stdout="rs",
tags=("alias", "rust"),
),
Probe(
id="rust-compile-error",
lang="rust",
source="fn main() { let x: i32 = \"nope\"; }\n",
expect_ok=False,
expect_stage="compile",
expect_exit=None,
tags=("compile", "rust"),
),
Probe(
id="rust-screen-command",
lang="rust",
source='fn main() { let _ = std::process::Command::new("true"); }\n',
kind="screen",
screen_needle="processes",
tags=("screen", "rust"),
),
# --- erlang ------------------------------------------------------------
Probe(
id="erl-escript",
lang="erlang",
source='main(_) -> io:format("erl-probe ~p~n", [lists:sum(lists:seq(1, 10))]).\n',
expect_stdout="erl-probe 55",
tags=("smoke", "erlang"),
),
Probe(
id="erl-module",
lang="erlang",
source=(
"-module(main).\n"
"-export([main/1]).\n"
'main(_) -> io:format("~p~n", [7 * 6]).\n'
),
expect_stdout="42",
tags=("module", "erlang"),
),
Probe(
id="erl-alias",
lang="erl",
source='main(_) -> io:format("alias~n").\n',
expect_stdout="alias",
tags=("alias", "erlang"),
),
Probe(
id="erl-screen-os-cmd",
lang="erlang",
source='main(_) -> os:cmd("true").\n',
kind="screen",
screen_needle="processes",
tags=("screen", "erlang"),
),
)
def probes_by_tag(*tags: str) -> tuple[Probe, ...]:
wanted = set(tags)
return tuple(p for p in PROBES if wanted.intersection(p.tags))
def covered_langs() -> set[str]:
"""Canonical RUN_LANGS keys touched by at least one probe (aliases resolved)."""
from synapse import code_run
return {code_run.resolve_lang(p.lang) for p in PROBES}