## Diamond Recursion Scan (npm-arborist) — CLEAN

**Scan date:** 2026-03-29
**Pattern:** Recursive cycle/dependency check without visited set (CWE-407 diamond recursion, O(2^D))

### Files examined

- `lib/can-place-dep.js` — peer dependency placement and cycle detection
- `lib/calc-dep-flags.js` — dev/optional/peer flag propagation
- `lib/node.js` — `explain()`, `getBundler()`, `isDescendantOf()`
- `lib/edge.js` — `explain()`
- `lib/gather-dep-set.js` — transitive dep set computation

### Findings

**can-place-dep.js:** Already contains a CWE-407 fix (`npm-0002`, `peerPathSet` as `new Set()`). O(1) membership checks. CLEAN.

**calc-dep-flags.js:** `calcDepFlagsStep` delegates tree traversal to `treeverse.depth()` which manages visited tracking internally. The self-call in the function body handles only `node.isLink` (pointer-following, not a DAG diamond). CLEAN.

**node.js explain():** Uses `this[_explanation]` memoization cache so a node is fully computed only once regardless of how many paths in the DAG reach it. The `seen.includes(this)` (Array.includes) guards against cycle re-entry. The memoization ensures O(N) total work, not O(2^D). CLEAN.

**node.js getBundler():** Traverses the parent chain upward (a tree, not a DAG), then iterates `edgesIn`. Uses a shared `path` array with `path.includes()` as cycle guard. The path array is passed by reference and grows monotonically, acting as an implicit visited set across sibling iterations. Not a diamond recursion defect.

**gather-dep-set.js:** Uses iterative Set-based BFS with `deps.has()` O(1) membership test. CLEAN.

### Verdict: CLEAN — no diamond recursion CWE-407 found (prior CWE-407 defect already patched as npm-0002)
