---
name: mapear-codebase
description: Análisis profundo de un codebase existente. Genera árbol de directorios anotado, identifica puntos de entrada, tecnologías usadas, estándares del proyecto, deuda técnica visible y zonas de riesgo. Produce un documento estructurado que sirve como mapa de navegación para el equipo.
version: "1.1.0"
evolved: true
evolved-from: "1.0.0"
evolved-at: "2026-04-24"
evolved-by: "aprender"
evolved-note: "Sección nueva: validar parser jerárquico contra árbol real antes de congelar dataclass"
herramientasPermitidas: [Read, Write, Edit, Bash, Glob, Grep]
exclusiones:
  - "No cargar cuando el codebase ya tiene un `CODEBASE-MAP.md` generado en los últimos 30 días y no ha habido cambios arquitectónicos — ejecutar este skill produce un mapa redundante con costo de análisis innecesario."
  - "No cargar para análisis de seguridad del código — el mapa identifica zonas de riesgo estructural, no vulnerabilidades; para auditoría de seguridad cargar `reglas/seguridad.md` o invocar `revisor-seguridad-swl`."
  - "No cargar para entender el codebase de un proyecto SWL propio (este mismo repo) — usar `.planning/ESTADO.md`, `INVENTARIO.md` y `ARQUITECTURA.md` que ya documentan la estructura interna."
  - "No cargar para búsquedas puntuales de una función o patrón específico — `Grep` directo es suficiente y no requiere generar un CODEBASE-MAP.md completo."
evolvable: true  # default para skill estandar
---
# Habilidad: Mapear Codebase Existente

## Propósito

Antes de modificar cualquier sistema heredado o desconocido, el equipo necesita
un mapa preciso. Esta habilidad genera ese mapa de forma sistemática y sin
suposiciones. El output es un documento que cualquier desarrollador puede leer
para orientarse en menos de 15 minutos.

## Cuándo activar

- Se incorpora un desarrollador nuevo al proyecto
- Se va a añadir una feature a un proyecto no familiar
- Se detectan inconsistencias entre documentación y código
- El planificador necesita entender el estado actual antes de planear

---

## Paso 1 — Reconocimiento inicial (5 minutos de exploración)

Ejecutar los siguientes comandos de exploración antes de generar el documento:

```bash
# Árbol de directorios (excluir node_modules, .git, __pycache__, dist)
find . -type f | grep -v node_modules | grep -v .git | grep -v __pycache__ \
  | grep -v dist | grep -v .egg-info | sort | head -200

# Conteo de archivos por extensión
find . -type f | grep -v node_modules | grep -v .git \
  | sed 's/.*\.//' | sort | uniq -c | sort -rn | head -20

# Archivos de configuración en raíz
ls -la . | grep -E "\.(toml|json|yaml|yml|env|cfg|ini|lock)$"

# Tamaño de los módulos principales
du -sh */ 2>/dev/null | sort -rh | head -20
```

---

## Paso 2 — Identificación de tecnologías

Para cada uno de los siguientes indicadores, verificar si existe en el proyecto:

### Backend
- `requirements.txt` / `pyproject.toml` / `Pipfile` → Python
- `pom.xml` / `build.gradle` → Java/Kotlin
- `package.json` en raíz sin framework frontend → Node.js
- `go.mod` → Go
- `Cargo.toml` → Rust
- `Gemfile` → Ruby

### Frontend
- `angular.json` → Angular
- `next.config.*` → Next.js
- `vite.config.*` → Vite (React/Vue/Svelte)
- `nuxt.config.*` → Nuxt
- Presencia de `tailwind.config.*`
- Presencia de `material` en package.json

### Infraestructura
- `Dockerfile` / `docker-compose.yml` → Docker
- `terraform/` o `*.tf` → Terraform
- `.github/workflows/` → GitHub Actions
- `Makefile` → Build automation
- `alembic/` → Migraciones SQLAlchemy

---

## Paso 3 — Identificar puntos de entrada

Buscar explícitamente:

| Tecnología | Punto de entrada típico |
|-----------|------------------------|
| FastAPI | `app = FastAPI()` en `main.py` o `app/main.py` |
| Django | `manage.py`, `wsgi.py`, `asgi.py` |
| Express | `app.listen` en `index.js` / `server.js` |
| Angular | `main.ts` → `bootstrapApplication` o `AppModule` |
| Next.js | `pages/_app.tsx` o `app/layout.tsx` |
| Go | `func main()` en `cmd/*/main.go` |
| Rust | `fn main()` en `src/main.rs` |

Anotar: archivo exacto, función, puerto si es visible en config.

---

## Paso 4 — Detectar estándares del proyecto

Verificar presencia y contenido de:

- **Linting**: `.eslintrc*`, `ruff.toml`, `.pylintrc`, `flake8`
- **Formateo**: `.prettierrc`, `black` en pyproject.toml, `editorconfig`
- **Tests**: `pytest.ini`, `jest.config.*`, carpeta `tests/` o `__tests__/`
- **Commits**: `.commitlintrc`, `CHANGELOG.md`, convenciones en README
- **Branching**: documentado en README o CONTRIBUTING.md
- **Tipo de imports**: absolutos vs relativos, alias configurados

---

## Paso 5 — Identificar deuda técnica visible

Buscar activamente:

```bash
# TODOs y FIXMEs
grep -r "TODO\|FIXME\|HACK\|XXX\|DEPRECATED" --include="*.py" \
  --include="*.ts" --include="*.js" -n | head -50

# Print/console.log de debug
grep -r "console\.log\|print(" --include="*.py" --include="*.ts" \
  --include="*.js" -n | grep -v "test\|spec\|logger" | head -30

# Código comentado (señal de deuda)
grep -r "^[[:space:]]*#.*def \|^[[:space:]]*//" --include="*.py" \
  --include="*.ts" -n | head -20
```

Categorizar deuda encontrada:
- **Deuda de código**: TODOs, prints, código comentado
- **Deuda de test**: módulos sin cobertura visible
- **Deuda de docs**: funciones públicas sin docstring/JSDoc
- **Deuda de dependencias**: packages con versiones `*` o muy antiguas

---

## Estructura del documento de salida: `CODEBASE-MAP.md`

```markdown
# CODEBASE-MAP.md — [Nombre del Proyecto]
**Generado**: [fecha]  **Analizado por**: [agente/persona]

## Resumen ejecutivo
[2-3 oraciones: qué hace el sistema, en qué estado está, nivel de madurez]

## Stack tecnológico confirmado
| Capa | Tecnología | Versión | Notas |
|------|-----------|---------|-------|
|      |           |         |       |

## Árbol de directorios (anotado)
```
proyecto/
├── src/              # Código fuente principal
│   ├── api/          # Endpoints REST
│   ├── models/       # Modelos ORM
│   └── services/     # Lógica de negocio
├── tests/            # Suite de tests
├── migrations/       # Migraciones de BD
└── .planning/        # Documentación del proyecto
```

## Puntos de entrada
| Tipo | Archivo | Función/Clase | Puerto/URL |
|------|---------|--------------|-----------|
|      |         |              |           |

## Módulos principales
| Módulo | Responsabilidad | Dependencias internas | LOC aprox. |
|--------|----------------|----------------------|-----------|
|        |                |                      |           |

## Convenciones detectadas
- Estilo de código: [PEP8 / Airbnb / Google / sin estándar detectado]
- Tests: [pytest / jest / sin tests detectados]
- Commits: [conventional commits / libre]
- Branching: [gitflow / trunk / no documentado]

## Deuda técnica
| Tipo | Descripción | Archivo(s) | Severidad |
|------|-------------|-----------|----------|
|      |             |           | Alta/Media/Baja |

## Zonas de riesgo
- [ ] [Módulo X]: sin tests, alta complejidad ciclomática
- [ ] [Módulo Y]: dependencia no mantenida (última versión: 20XX)

## Decisiones de arquitectura detectadas (implícitas)
1.
2.

## Preguntas sin respuesta (para el equipo)
- ¿Por qué se usa X en lugar de Y?
- ¿Existe documentación de la API externa Z?
```

---

## Reglas de calidad del mapa

- NUNCA escribir "no encontrado" sin haber buscado activamente
- Cada zona de riesgo debe tener evidencia (archivo y línea)
- El árbol NO es el output de `tree` — es el árbol anotado con semántica
- Si el codebase supera 50k LOC, mapear solo los módulos de mayor impacto

## Cuándo NO cargar

- El `CODEBASE-MAP.md` fue generado hace menos de 30 días y el proyecto no tuvo cambios arquitectónicos desde entonces — el análisis es caro en tool calls y el mapa existente es válido.
- El objetivo es detectar vulnerabilidades de seguridad o código inseguro — este skill mapea estructura, no analiza patrones de seguridad. Para eso cargar `reglas/seguridad.md` o invocar `revisor-seguridad-swl`.
- Se trabaja en el propio repositorio SWL — `INVENTARIO.md`, `.planning/ESTADO.md` y `ARQUITECTURA.md` ya documentan la estructura; generar un CODEBASE-MAP redundante fragmenta la documentación.
- La necesidad es encontrar dónde está una función o clase específica — `Grep` directo con la firma de la función es suficiente y no requiere el análisis completo de los 5 pasos.

## Gotchas / Errores comunes no obvios

- **Árbol de directorios generado con `tree` sin anotar**: el CODEBASE-MAP.md tiene el árbol de directorios crudo sin explicar qué contiene cada carpeta. Causa: copiar el output de `tree` directamente en lugar de recorrer cada directorio y agregar semántica. Solución: el árbol en CODEBASE-MAP.md es obligatoriamente anotado — cada directorio no obvio debe tener un comentario breve (`# Lógica de negocio`, `# Endpoints REST`, etc.); sin anotaciones no es un mapa, es un listado.
- **Deuda técnica marcada sin evidencia de archivo y línea**: el reporte dice "hay código comentado" sin indicar dónde. Causa: los `grep` de deuda se ejecutan pero el resultado no se procesa para extraer los archivos y líneas específicos. Solución: cada entrada en la tabla de deuda técnica debe incluir el path del archivo y el número de línea aproximado — una deuda sin ubicación no puede priorizarse ni corregirse.
- **Zonas de riesgo inferidas sin verificación**: el mapa marca `api/auth.py` como "sin tests" sin haber verificado si existe `tests/test_auth.py` en otro directorio. Causa: buscar tests solo en el directorio adyacente al módulo. Solución: el Paso 4 requiere detectar la convención de organización de tests del proyecto (adyacente, directorio `tests/` raíz, o ambos) antes de marcar un módulo como sin cobertura.
- **Preguntas sin respuesta omitidas porque "parecen obvias"**: el mapa no registra por qué se usa una dependencia inusual o por qué un módulo tiene una estructura atípica. Causa: el agente asume conocimiento del equipo. Solución: toda decisión implícita no documentada en el codebase debe aparecer en "Preguntas sin respuesta" — el propósito es exactamente capturar lo que no es obvio para alguien externo al proyecto.

## Validar parser jerárquico contra el árbol REAL antes de congelar el dataclass

### SIEMPRE: ejecutar `find <raíz> -type d | sort` sobre datos reales antes de diseñar

**Cuándo aplicar**: al diseñar un `dataclass`, parser o modelo de estructura jerárquica (árbol de archivos, taxonomía de categorías, segmentos de ruta con numerales anidados) cuya forma exacta se desconoce a priori. Aplica especialmente cuando la jerarquía es heterogénea entre ramas.

**Problema que previene**: asumir una profundidad fija (p.ej. "5 niveles") o una gramática regular (p.ej. "siempre letra + número + letra") basándose en una muestra pequeña o en la documentación del dominio. La realidad de los datos suele tener:
- Profundidad variable entre ramas (5 niveles en una, 7 en otra).
- Numerales compuestos (árabe + romano + inciso + subinciso).
- Fusiones donde un nodo combina inciso con descripción en un solo segmento.
- Saltos de niveles porque el dato fuente los omitió.

Si el dataclass se congela sin ver estos casos, el parser requiere re-diseño completo al encontrar la primera rama atípica. Las 15 minutos de exploración ahorran horas de refactor.

**Regla operativa**:

1. Antes de escribir la primera línea del dataclass/parser, ejecutar:
   ```bash
   find <raíz-del-dataset> -type d -mindepth 1 | sort | head -200
   # o para dataset enorme:
   find <raíz-del-dataset> -type d -mindepth 1 | awk -F/ '{print NF-1}' | sort | uniq -c
   ```
2. Inspeccionar a ojo 3-5 ramas distintas. Anotar variaciones en profundidad, tipos de numeral, presencia/ausencia de segmentos esperados.
3. Diseñar el dataclass para la variedad observada, no para el caso modal.
4. Si después se descubre un caso no cubierto, el fix es extender el parser — no colapsar el caso a un molde que no lo acomoda.

### Ejemplo genérico

```python
# MAL — dataclass con profundidad fija asumida
@dataclass
class RutaJerarquica:
    nivel_1: str           # asume siempre letra mayúscula
    nivel_2: int           # asume siempre número
    nivel_3: str           # asume siempre inciso
    nivel_4: str | None    # ocasionalmente un 4to nivel

# Cuando llega una rama "A.7 d) I 1/" el parser explota o pierde información.

# BIEN — dataclass con estructura variable comprobada contra datos reales
@dataclass(frozen=True)
class RutaJerarquica:
    segmentos: tuple[str, ...]                   # profundidad variable
    numeral_romano: str | None = None            # presente solo en algunas ramas
    numeral_arabe_secundario: int | None = None  # caso observado en rama B
    inciso_fusionado: bool = False               # caso donde inciso+desc vienen juntos
```

**Relacionado**: ver también `testing-python` (tests multi-formato ANTES de refactorizar un parser) y `patrones-python` (normalizadores al formato canónico del proyecto).
