---
paths:
  - "**/tests/**"
---
# Regla: Cleanup obligatorio de directorios temporales en tests

Esta regla es **OBLIGATORIA** y aplica a todo test Node.js del sistema SWL
que cree directorios temporales bajo `os.tmpdir()` (típicamente
`C:\Users\<usuario>\AppData\Local\Temp` en Windows, `/tmp` en POSIX).

Promueve a regla obligatoria el aprendizaje L1 #19 + entrada extensa de
APRENDIZAJES.md (2026-05-17 "Tests sin cleanup acumularon 5,800+ carpetas
en %TEMP%") tras reincidencia documentada el 2026-05-18 en
`tests/hooks/validar-intent-spec.test.js` (192 residuos acumulados).

---

## Principio

> Todo test que necesite un directorio temporal DEBE usar
> `setupSandboxes(prefix)` de `tests/_helpers/sandbox.js`. NUNCA
> `fs.mkdtempSync(path.join(os.tmpdir(), ...))` directo.

El helper registra automáticamente `after()` del módulo `node:test` para
limpiar al final del archivo. Sin él, cada corrida de `npm test:all` deja
N carpetas residuales (N = número de tests × archivos sin cleanup).

---

## Patrón obligatorio

### MAL — `mkdtempSync` directo sin cleanup

```javascript
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { test } = require('node:test');

function crearSandbox() {
  return fs.mkdtempSync(path.join(os.tmpdir(), 'mi-test-'));
  // ✗ Nunca se limpia. Acumula residuos en %TEMP%.
}

test('hace algo', () => {
  const dir = crearSandbox();
  // ... usar dir ...
  // ✗ Sin try/finally con rmSync.
});
```

### BIEN — `setupSandboxes` con cleanup automático

```javascript
const { test } = require('node:test');
const { setupSandboxes } = require('../_helpers/sandbox');

const sandboxes = setupSandboxes('mi-test-');
// ✓ setupSandboxes registra after() global del archivo.

test('hace algo', () => {
  const dir = sandboxes.create();
  // ... usar dir ...
  // ✓ Cleanup automático al terminar el archivo de tests.
});
```

---

## Cuándo aplicar

OBLIGATORIO en:

- Cualquier archivo `tests/**/*.test.js` que cree dirs temporales para
  aislar el test del CWD del proyecto.
- Tests de hooks que necesitan sandbox con `.planning/`, `agentes/`, etc.
- Tests de scripts que escriben archivos generados (`generar-*`,
  `auditar-*`).
- Cualquier helper de fixtures que cree filesystem temporal.

NO aplicar (excepciones legítimas):

- Tests que NO crean dirs temporales (unit tests puros con stubs/mocks).
- Tests que usan `process.chdir` sin crear directorio nuevo (raros).

---

## Convención del prefijo

El prefijo pasado a `setupSandboxes(prefix)` DEBE:

1. Empezar con `swl-` para identificar el origen del proyecto.
2. Incluir el nombre del componente bajo test (corto).
3. Terminar con guión final (el helper NO lo añade automáticamente).

Ejemplos válidos:

- `setupSandboxes('swl-mi-hook-')`
- `setupSandboxes('swl-claudemd-')`
- `setupSandboxes('swl-evolucion-')`

Ejemplos inválidos:

- `setupSandboxes('test')` — sin prefijo `swl-`, indistinguible de otros proyectos.
- `setupSandboxes('swl-hook')` — falta guión final, `mkdtemp` añade chars random pegados al nombre.

---

## Casos especiales: `process.chdir` en tests

Si el test hace `process.chdir(sandbox)` en top-level, registrar
**ANTES** de `setupSandboxes()` un `after()` que restaure el CWD:

```javascript
const originalCwd = process.cwd();
const { after } = require('node:test');
after(() => process.chdir(originalCwd));
// ↑ chdir-restore registrado primero
const sandboxes = setupSandboxes('swl-mi-test-');
// ↑ cleanup de sandboxes registrado segundo
// FIFO en node:test: restaurar CWD primero, luego limpiar dirs.
```

Sin esto, Windows EBUSY al intentar `rmSync` un directorio que es el CWD activo.

---

## Verificación

### Test de regresión post-suite

Tras ejecutar `npm test:all`, verificar 0 residuos:

```bash
# Bash
ls "$LOCALAPPDATA/Temp" 2>/dev/null | grep -c "^swl-" || echo "0"

# PowerShell
(Get-ChildItem $env:TEMP -Directory -Filter "swl-*").Count
```

Resultado esperado: `0`.

### Auditoría continua del repo

Para detectar archivos de test que ignoran la regla:

```bash
# Archivos con mkdtempSync directo SIN usar el helper
grep -rlE "mkdtempSync\(.*os\.tmpdir|fs\.mkdtempSync" tests/ \
  | grep -v "_helpers" \
  | xargs -I {} grep -L "setupSandboxes" {}
```

Resultado esperado tras migración completa: `0` archivos.

---

## Anti-patrones explícitos

- **Copiar de un test "legacy" como referencia**: si el patrón a copiar
  usa `mkdtempSync` directo, el nuevo test heredará el bug. Verificar
  que el test fuente use `setupSandboxes` antes de copiar como
  plantilla.
- **`afterEach` con `rmSync` manual cuando ya existe `setupSandboxes`**:
  duplica responsabilidad. Si necesitas cleanup por test (no por
  archivo), el helper expone `sandboxes.cleanup()` invocable.
- **Path construido manualmente** con `path.join(os.tmpdir(),
  \`prefix-${Date.now()}\`)` + `mkdirSync`: escapa al regex de
  auditoría que busca `mkdtempSync`. Si el patrón aparece, refactorizar
  a `setupSandboxes` igual.
- **Ignorar Windows EBUSY**: si en CI Linux pasa pero localmente
  Windows falla con `EBUSY: resource busy or locked`, casi siempre es
  cleanup intentando borrar el CWD. Aplicar el patrón `chdir-restore
  ANTES de setupSandboxes`.

---

## Excepciones documentadas

Si un test legítimamente NO puede usar `setupSandboxes` (caso raro), DEBE:

1. Documentar en comentario al inicio del archivo por qué no aplica.
2. Implementar cleanup manual con `after()` + `try/finally`.
3. Incluir test de regresión: tras el test, `Get-ChildItem $env:TEMP` no debe mostrar el dir.

Sin esos 3 elementos, el test será marcado como violación de la regla
en `/swl:revisar`.

---

## Origen

- **Aprendizaje L1 #19** (2026-05-17): "Test nuevo que necesita
  directorio temporal | Usar `setupSandboxes('prefix-')` de
  `tests/_helpers/sandbox.js`; nunca `fs.mkdtempSync` directo sin
  cleanup".
- **Entrada extensa APRENDIZAJES.md líneas 6793-6852**: el usuario
  descubrió manualmente más de 5,800 carpetas con prefijo `swl-*` en
  `C:\Users\Saul\AppData\Local\Temp` y las limpió con CCleaner.
  Investigación reveló 14 archivos de test sin cleanup en refactor PR #35.
- **Reincidencia 2026-05-18**: tras refactor de 14 archivos quedaron
  32 sin migrar. Yo creé `tests/hooks/validar-intent-spec.test.js`
  copiando patrón de `claudemd-bloat-detector.test.js` (no migrado) →
  192 residuos en 1 sesión. La regla estaba en APRENDIZAJES.md pero NO
  era obligatoria en `reglas/` ni `~/.claude/rules/pruebas.md`.

Esta regla cierra ese gap promoviendo el aprendizaje a regla obligatoria
del proyecto + actualización del skill `tdd-workflow` para enseñar el
patrón correcto.

---

## Relación con otras reglas

- `~/.claude/rules/pruebas.md § Tests deterministas — sin sleep ni delays`
  — esta regla extiende el principio "Limpiar estado en teardown" al
  caso específico de directorios temporales.
- `~/.claude/rules/arreglar-al-detectar.md` — la deuda silenciosa que
  esta regla cierra es exactamente el tipo de deuda que esa regla
  prohíbe acumular.
- `reglas/registro-componentes-nuevos.md` — al crear un test nuevo se
  considera componente nuevo del sistema; la regla aplica como parte
  del registro.
