# Clavue v8.13.4 — Resilient global install (auto-clean ENOTEMPTY residue)

Released: 2026-05-20

## Why this exists

A user upgrading to v8.13.3 hit:

```
npm error code ENOTEMPTY
npm error syscall rename
npm error path /opt/homebrew/lib/node_modules/clavue
npm error dest /opt/homebrew/lib/node_modules/.clavue-xOe9qkNa
```

This is a long-standing npm/macOS-APFS friction, not a Clavue regression:
`npm install -g` performs an atomic `rename(pkg_dir, .pkg-<rand>)` before
unpacking the new tarball. On macOS APFS (and occasionally Linux ext4),
that `rename` fails with `ENOTEMPTY` if the existing directory contains
files npm doesn't know about — typical sources:

- A previous install was interrupted partway and left a partial tree.
- An earlier failed upgrade left a `.clavue-<rand>` staging dir behind,
  which then collides with the next attempt.
- Editor temp files / `.DS_Store` / Gatekeeper xattrs.

There is nothing the npm package itself can do at install time (npm
ignores `preinstall` for global installs of the new tarball). The
practical fix lives in `install.sh`, the bootstrapper users run via
`npx`/`curl | bash`. v8.13.4 makes that script self-healing.

## What changed

`install.sh` now:

1. **Detects leftover state** before running `npm install -g`. It looks
   for the package directory (`<prefix>/lib/node_modules/clavue`) and
   any `.clavue-*` staging directories from prior failed runs.
2. **Cleans them** with `rm -rf` (escalates to `sudo` automatically when
   the prefix isn't writable, e.g. `/opt/homebrew` on macOS).
3. **Retries once** if `npm install -g` still fails — runs the cleanup
   again and re-attempts. Most ENOTEMPTY cases resolve on the first
   pass; the retry handles the rare race where npm itself created a
   half-staged directory during the failed attempt.

Both the package directory and the staging directory pattern are scoped
strictly to the package being installed (`clavue`); no other globally
installed packages are touched.

## Files touched

- `install.sh` — added `clean_global_residue` helper invoked before and
  on failure of `npm install -g`. Detects the global prefix via
  `npm prefix -g`, supports both `lib/node_modules` and the rarer flat
  `node_modules` layout, escalates to `sudo` only when needed.

## Workaround for users already stuck on the old install.sh

```bash
sudo rm -rf /opt/homebrew/lib/node_modules/clavue \
            /opt/homebrew/lib/node_modules/.clavue-*
npm install -g clavue@8.13.4
clavue --version   # → 8.13.4 (Clavue)
```

Or use `npx` to bypass the global install entirely:

```bash
npx -y clavue@8.13.4
```

## Verification

- `bash -n install.sh` clean.
- Manual dry-run of the residue probe on a clean homebrew prefix
  reports "no leftovers" and skips the cleanup branch (correct).
- Heap pressure ladder from v8.13.3 unchanged.
- Full test suite: 1537 tests / 1531 pass / 6 skipped / 0 fail.
- `dist/cli.js --version` → `8.13.4 (Clavue)`.

## What changes for users

- Fresh installers running `curl … install.sh | bash` won't hit
  ENOTEMPTY anymore — the script wipes leftover state first.
- Users already stuck after a failed v8.13.3 upgrade: run the two-line
  workaround above, or grab the new install.sh:

  ```
  curl -fsSL https://unpkg.com/clavue@8.13.4/install.sh | bash -s -- 8.13.4
  ```

No application-level behavior changed — heap ladder, image guard, and
the rest of v8.13.3 carry forward unmodified.
