| name | reproduce-issue |
| description | The single skill for reproducing an nx issue. Given a GitHub issue number (human entry) OR explicit repro parameters (agent entry), it runs the reproduction ENTIRELY inside an isolated Docker sandbox โ gVisor on Linux, the Docker VM on macOS โ so the untrusted repro's install scripts and commands never execute on the host, then reports whether it reproduces. Called by humans via "/reproduce-issue |
| allowed-tools | Read, Grep, Glob, Bash(uname *), Bash(gh issue view *), Bash(gh issue list *), Bash(docker run *), Bash(docker cp *), Bash(docker rm *), Bash(docker info *), Bash(docker pull *) |
Reproduce an issue (sandboxed)
Reproduce an nx bug entirely inside an isolated container and report the outcome. The untrusted repro โ its install (arbitrary postinstall scripts) and its repro command โ runs only in the sandbox, never on the host. --rm destroys everything on exit; nothing touches the host filesystem.
This is the one reproduction engine in the repo. It has two front doors:
Entry A โ a GitHub issue (human: /reproduce-issue <N>)
- Fetch the issue:
gh issue view <N> --repo nrwl/nx --json number,title,body,comments,labels
- Extract from the body: the repro repo URL (or
create-nx-workspace steps), the exact command(s) that show the bug, the reported vs expected behavior, and the Nx Report (nx version + Node version).
- Fill the parameters below and run the sandbox (default
nx-version = whatever the issue reports / the repo pins; default registry = public npm).
Entry B โ explicit parameters (agent: reproduce-verifier Level 2)
The caller passes these directly:
repro โ repo:<git-url> (clone a public repo) OR create:"<create-nx-workspace args>".
nx-version:<version> โ install this published nx and rewrite the repro's nx / @nx/* / @nrwl/* deps to it. For reproducing against a released version.
nx-build:<git-ref> (PR-verification mode) โ instead of a published version, build nx from this nrwl/nx commit inside the sandbox and reproduce against it. Uses the nx-review-sandbox image; the skill derives the version and serves it from a localhost verdaccio in the same container. Mutually exclusive with nx-version.
nx-registry:<url> (optional, nx-version mode only) โ registry to install from. Default public npm.
command:"<repro-cmd>" โ the command whose output/exit code decides the verdict.
node-image:<img> (optional) โ base image matching the issue's Node (default node:22; public images are multi-arch โ native on Apple Silicon).
expect:<reported symptom> (optional), setup:"<files/steps>" (optional) โ files to create in the workspace first.
Platform (where the sandbox boundary comes from)
Run uname -s once:
- Linux โ add
--runtime=runsc to docker run (gVisor is the sandbox).
- macOS (
Darwin) โ omit --runtime=runsc (the Docker VM is the sandbox). Verify docker info works; if not, tell the user to colima start (or start Docker Desktop / OrbStack).
The command below shows the Linux form โ on macOS drop --runtime=runsc, keep the rest.
Preflight โ check the environment, fail with a FIX (not a mystery)
Before running anything, verify prerequisites in order and stop at the first miss, printing the one-line fix. Most misses point at the setup-review-sandbox skill, which installs/builds everything.
-
Docker is up:
docker info >/dev/null 2>&1 && echo up || echo MISSING
Miss โ Linux: sudo systemctl start docker. macOS: colima start (or open Docker Desktop). Or run setup-review-sandbox.
-
Container networking works (the check that would have caught the veth breakage):
docker run --rm --network none alpine true
docker run --rm alpine true
If A passes but B fails with veth ... operation not supported โ networking is broken (usually a kernel update left veth unloadable). Fix: sudo modprobe veth; if that errors with a BTF/version mismatch, reboot (the running kernel no longer matches its modules).
-
Isolation runtime (platform-specific):
-
(PR-build mode ONLY) the toolchain image exists:
docker image inspect nx-review-sandbox:latest >/dev/null 2>&1 && echo ok || echo MISSING
Miss โ run setup-review-sandbox (builds it from tools/review-sandbox/Dockerfile). Skip this check when reproducing against a published nx version โ that path needs only steps 1โ3 and a public node image.
If all needed checks pass, proceed.
Safety rails (do NOT break these)
- The untrusted repro runs only in the container. Never
-v a host path in. nx comes from a registry (or docker cp-ed tarballs), never a mount.
- Always pass:
--cap-drop ALL, --security-opt no-new-privileges, --memory 4g --cpus 4 --pids-limit 2048, --rm; plus --runtime=runsc on Linux.
- Network is ON (clone + install need it). gVisor still protects the host kernel; on macOS the VM protects the host.
- One
docker command per Bash call. (Chaining inside the container's bash -c '...' is one host command, which is fine.)
Run
Detect platform, then a single host command does clone/create โ dep-rewrite โ install โ repro, all inside the sandbox:
docker run --rm $RUNTIME \
--cap-drop ALL --security-opt no-new-privileges \
--memory 4g --cpus 4 --pids-limit 2048 \
node:22 bash -c '
set -e
git clone --depth 1 <GIT_URL> /repro # repo: form
# -- or -- npx --yes create-nx-workspace <ARGS> --directory /repro # create: form
cd /repro
node -e '"'"'
const fs=require("fs"),p=JSON.parse(fs.readFileSync("package.json","utf8")),v=process.argv[1];
for (const s of ["dependencies","devDependencies"]) for (const n of Object.keys(p[s]||{}))
if (n==="nx"||n.startsWith("@nx/")||n.startsWith("@nrwl/")) p[s][n]=v;
fs.writeFileSync("package.json", JSON.stringify(p,null,2)+"\n");
'"'"' <NX_VERSION>
rm -f package-lock.json pnpm-lock.yaml yarn.lock
PM=npm; test -f pnpm-workspace.yaml && PM=pnpm
npm i -g pnpm@11 >/dev/null 2>&1 || true
npm_config_registry=<NX_REGISTRY> $PM install
( timeout 300 <REPRO_COMMAND> ); echo "REPRO_EXIT=$?"
echo "kernel: $(uname -r)"
'
Substitute <GIT_URL>/<ARGS>, <NX_VERSION>, <NX_REGISTRY> (default https://registry.npmjs.org), and <REPRO_COMMAND>.
Classify + report
Compare output and REPRO_EXIT against the reported symptom, and return this block (verdicts match the reproduce-verifier's Level 2 vocabulary):
repro: <repo-url | create-nx-workspace ...>
nx-version: <version> (registry: <url>)
command: <verbatim>
exit code: <N>
verdict: <PR_REPRO_PASSES | PR_REPRO_FAILS | PR_REPRO_FAILS_DIFFERENT | PR_REPRO_INCONCLUSIVE | SETUP_FAILED>
output (tail ~20 lines):
<...>
- succeeded (matches the claimed fix) โ
PR_REPRO_PASSES
- failed with the reported error โ
PR_REPRO_FAILS
- failed with a different error โ
PR_REPRO_FAILS_DIFFERENT (flag for human)
- unclear โ
PR_REPRO_INCONCLUSIVE
- clone/create/install broke before the repro ran โ
SETUP_FAILED (say which step + tail)
(For a human /reproduce-issue run against a released version, "reproduced" vs "did not reproduce" is the plain-language answer; the verdict vocab above is for the agent.)
PR-build mode โ build nx from source in the sandbox (nx-build)
When nx-build:<git-ref> is given, do everything in one nx-review-sandbox container (it carries the mise toolchain incl. java + dotnet, required by nx's @nx/dotnet/@nx/gradle graph plugins). One container, localhost throughout โ no host build, no host verdaccio, no host.docker.internal, no listen-address change:
docker run --rm $RUNTIME \
--cap-drop ALL --security-opt no-new-privileges \
--memory 20g --cpus 6 --pids-limit 8192 --tmpfs /work:rw,exec,size=16g \
-e CI=true -e NX_DAEMON=false \
nx-review-sandbox:latest bash -c '
set -e
# 1. build nx from the PR commit
cd /work
git clone --filter=blob:none https://github.com/nrwl/nx nx && cd nx
git checkout <GIT_REF>
mise install && pnpm install --frozen-lockfile
PORT=4873
pnpm nx local-registry @nx/nx-source --port=$PORT >/tmp/verdaccio.log 2>&1 &
for i in $(seq 1 60); do curl -sf http://localhost:$PORT/-/ping >/dev/null 2>&1 && break; sleep 1; done
NX_LOCAL_REGISTRY_PORT=$PORT pnpm nx populate-local-registry-storage @nx/nx-source
NXV=$(node -p "require(\"/work/nx/dist/packages/nx/package.json\").version")
# 2. reproduce against that build โ same container, localhost registry
cd /work
git clone --depth 1 <GIT_URL> repro # or: npx --yes create-nx-workspace <ARGS> --directory repro
cd repro
# rewrite nx/@nx/@nrwl deps to "$NXV" (same node one-liner as the Run section)
rm -f package-lock.json pnpm-lock.yaml yarn.lock
npm_config_registry=http://localhost:$PORT pnpm install
( timeout 300 <REPRO_COMMAND> ); echo "REPRO_EXIT=$?"
echo "kernel: $(uname -r)"
'
Because verdaccio and the repro live in the same container, the registry is plain localhost โ the reachability/listen-address problems a host verdaccio would create simply don't exist. Classify the result exactly as in "Classify + report".
Prerequisite: the nx-review-sandbox image (setup-review-sandbox). The nx build is heavy (~several min + several GB) โ RAM-backed via the tmpfs above so it stays off the host disk.
Cleanup
--rm destroys the container and everything in it on exit. Nothing persists on the host. Stray sandbox containers/images: /sandbox-prune.