<!-- Node backend verification is ENABLED for this project. The Stop hook
     enforces a node cycle in addition to the browser cycle whenever an
     edited file matches `node.verifyPatterns`. -->

## ⚠️ CRITICAL: when NOT to use node-devtools

**`node-devtools` is ONLY for Node.js backends.** It is a V8 inspector wrapper (`node --inspect` attach via PID / inspector port / Docker container). It does **NOT** work with any other runtime. Do **NOT** call `ndt_*` tools for projects whose backend is:
- Java (Spring, Quarkus, Micronaut, …) — use the JVM debugger, not supported by IronBee yet
- Python (FastAPI, Django, Flask, …) — not supported yet
- Go, Rust, Ruby, .NET, PHP, Elixir, Kotlin/JVM, Swift — not supported yet

**How to tell whether the backend is Node.js:**
- `package.json` is the main project manifest, with `"type": "module"` or `"engines": { "node": ... }`, or scripts that run `node`/`tsx`/`ts-node`.
- The dev server command is `npm run dev`, `next dev`, `nest start`, `nodemon`, etc.

If you see `pom.xml`, `build.gradle`, `requirements.txt`, `pyproject.toml`, `go.mod`, `Cargo.toml`, `Gemfile`, `composer.json`, `*.csproj` — the backend is NOT Node.js. Do NOT call any `ndt_*` tools.

**Misconfiguration recovery.** If you read this section it means the operator enabled the node cycle for this project. If the backend isn't actually Node.js, this was a mistake — the Stop hook will keep blocking the gate (with `incomplete_tools` for the node cycle) until you call `ndt_debug_connect` (which will fail) or `maxRetries` is exhausted. Don't attempt the connection. Stop and tell the user clearly: the project's backend is not Node.js; ask them to run `ironbee node disable` to unblock the gate. Continue with browser-cycle verification only in the meantime.

## Node flow

1. **Identify the running Node process** — note its PID, container name (`docker compose ps`), or inspector port.
2. **Connect**: `mcp__node-devtools__ndt_debug_connect` with one of `pid` / `processName` / `containerId` / `containerName` / `inspectorPort` / `wsUrl`. SIGUSR1 is auto-sent if the inspector isn't on; for Docker, it goes through `docker exec`.
3. **Pick an evidence path** per changed code path:
   - **Probe path** (proves the code path executed):
     - Set a probe at the changed location: `ndt_debug_put-tracepoint` (checkpoint), `ndt_debug_put-logpoint` (logged expression), or `ndt_debug_put-exceptionpoint` (caught/uncaught exception).
     - **Exercise the path** (e.g. send a request from the browser, run a CLI command, post to a queue) — without this the probe never fires.
     - Read collected snapshots: `ndt_debug_get-probe-snapshots`. At least one probe must come back with `triggered: true`.
   - **Log path** (proves no errors during execution):
     - Exercise the path.
     - Read errors: `ndt_debug_get-logs` with the error-level filter.
4. **Disconnect** (optional): `ndt_debug_disconnect`.

**Batch (speed):** connect (step 2) is standalone discovery. Batch consecutive `ndt_*` calls in one `mcp__node-devtools__ndt_execute` — set several probes together, then later read snapshots/logs together. The exercise step is ALWAYS separate: whatever triggers the code path (a browser/backend call on another server, a CLI command, the user) can't share an `ndt_*` batch — so node runs as set probes (batch) → exercise (separate) → read snapshots (batch).

### Verdict fields
The verdict is platform-agnostic — you submit only semantic judgment:

```json
{
  "session_id": "<sid>",
  "status": "pass",
  "checks": ["POST /api/orders returned 201", "tracepoint at handler.ts:42 fired once"]
}
```

Node-cycle pass criteria:
- If probes were set, at least one must have triggered (proves the code path executed).
- If only logs were used, no ERROR-level entries.
- If both forms were used, both conditions must hold.

## Multi-cycle (browser + node simultaneously)

Common case: in the same task you edit a `.tsx` component (browser-cycle) and a `server/api/*.ts` handler (node-cycle). Both cycles activate. **Single** `verification-start`, **single** `verdict.json`, **single** retry counter cover both. The verdict shape doesn't change — one verdict regardless of how many cycles ran:

```json
{
  "session_id": "<sid>",
  "status": "pass",
  "checks": ["checkout submits", "POST /api/orders returned 201", "no console errors"]
}
```

For a multi-cycle `pass`, BOTH cycles' pass criteria must hold.
