--- 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 #N", "reproduce this bug", "does this reproduce", and by the reproduce-verifier agent (Level 2). Nothing lands on the host. 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 `) 1. Fetch the issue: ```bash gh issue view --repo nrwl/nx --json number,title,body,comments,labels ``` 2. 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). 3. 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:` (clone a public repo) OR `create:""`. - **`nx-version:`** — install this **published** nx and rewrite the repro's `nx` / `@nx/*` / `@nrwl/*` deps to it. For reproducing against a released version. - **`nx-build:`** (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:`** (optional, `nx-version` mode only) — registry to install from. Default public npm. - **`command:""`** — the command whose output/exit code decides the verdict. - **`node-image:`** (optional) — base image matching the issue's Node (default `node:22`; public images are multi-arch → native on Apple Silicon). - **`expect:`** (optional), **`setup:""`** (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. 1. **Docker is up:** ```bash 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`. 2. **Container networking works** (the check that would have caught the `veth` breakage): ```bash docker run --rm --network none alpine true # A: is the sandbox itself OK? docker run --rm alpine true # B: is networking OK? ``` 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). 3. **Isolation runtime (platform-specific):** - **Linux** — gVisor registered as a Docker runtime? ```bash docker info --format '{{range $k,$v := .Runtimes}}{{$k}} {{end}}' | grep -q runsc && echo ok || echo MISSING ``` Miss → run `setup-review-sandbox` (installs + registers `runsc`). - **macOS** — the Docker VM (Colima / Docker Desktop) _is_ the sandbox; step 1 already covered it. No `runsc`. 4. **(PR-build mode ONLY) the toolchain image exists:** ```bash 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: ```bash # RUNTIME="--runtime=runsc" on Linux # RUNTIME="" on macOS 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 /repro # repo: form # -- or -- npx --yes create-nx-workspace --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"); '"'"' 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= $PM install ( timeout 300 ); echo "REPRO_EXIT=$?" echo "kernel: $(uname -r)" ' ``` Substitute ``/``, ``, `` (default `https://registry.npmjs.org`), and ``. ## 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: nx-version: (registry: ) command: exit code: verdict: 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:` 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: ```bash # RUNTIME="--runtime=runsc" on Linux, "" on macOS 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 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 repro # or: npx --yes create-nx-workspace --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 ); 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`.