# Pilar 6: El volante de aprendizaje de IA — cómo hacer que tu agente sea permanentemente más inteligente

**Línea de apertura (opcional, para el feed):** Llevas tres meses usando Claude en tu proyecto Java. Los mismos errores de hace tres meses siguen apareciendo. `==` para comparar `BigDecimal`. [`LocalDate.now`](http://LocalDate.now)`()` directo en el dominio. `javax.*` en lugar de `jakarta.*`. El modelo no aprendió porque tú no le enseñaste. Este es el último pilar: convertir cada bug en inteligencia permanente.

*Parte de la serie "Los 6 pilares del desarrollo asistido por IA" — basado en el material de Thor Henning Hetland (Totto) / Ægis. Adaptado a proyectos Java.*

* * *

Los cinco pilares anteriores construyen la base: contexto inteligente (Pilar 1), delegación estratégica (Pilar 2), verificación sistemática (Pilar 3), agencia técnica (Pilar 4) y guardrails de proceso (Pilar 5). El Pilar 6 responde la pregunta que todos estos dejan abierta:

**¿Cómo hace el sistema para volverse más inteligente con el tiempo, no solo más rápido?**

La respuesta no es confiar en que el modelo aprenda solo. Los modelos no aprenden de tus sesiones. Lo que aprendiste tú al arreglar ese bug el martes desaparece en el siguiente chat si no lo documentas. El Pilar 6 es el mecanismo para convertir cada bug resuelto, cada patrón descubierto y cada edge case encontrado en inteligencia permanente del sistema.

* * *

## **El problema del contexto estático**

Hay dos tipos de equipos que usan IA:

![](https://cdn.hashnode.com/uploads/covers/64a79aba336591d2a1481aae/b5ff193a-bdcf-471d-961b-cf1601495cca.png align="center")

![](https://cdn.hashnode.com/uploads/covers/64a79aba336591d2a1481aae/d9faf747-7a27-4a4c-97a5-d14ef38a1ea8.png align="center")

La diferencia no está en el modelo. Está en si el equipo actualiza el contexto o no.

Con el enfoque estático, el [CLAUDE.md](http://CLAUDE.md) del Pilar 1 se queda exactamente igual que el día que se escribió. La IA no aprende que `BigDecimal` se compara con `.compareTo()`, no aprende que el endpoint `/pagos` tiene validaciones especiales antes del publish a Kafka, no aprende que `Optional.empty()` en `PedidoService.findById()` puede significar "cancelado" además de "no existe".

Con el enfoque continuo, cada bug que se arregla se convierte en un anti-patrón documentado. Cada patrón que funciona bien se convierte en una skill. Cada edge case descubierto se convierte en un requisito de test. La IA del mes 3 es cualitativamente diferente a la del día 1.

## **El volante: cómo funciona el mecanismo**

![](https://cdn.hashnode.com/uploads/covers/64a79aba336591d2a1481aae/27ded2c9-eed6-4f3f-ad19-d71401447f25.png align="center")

El volante gira con tres artefactos:

1.  [`LEARNINGS.md`](http://LEARNINGS.md) — captura diaria de bajo rozamiento (5 min)
    
2.  [`IMPLEMENTATION-HISTORY.md`](http://IMPLEMENTATION-HISTORY.md) — registro de features implementadas
    
3.  **Skills como módulos vivos** — no documentos estáticos, sino módulos con ciclo de vida
    

## **El artefacto central:** [**LEARNINGS.md**](http://LEARNINGS.md)

Una entrada de [`LEARNINGS.md`](http://LEARNINGS.md) en un proyecto Java real tarda 5 minutos en escribir. Esos 5 minutos previenen que el mismo bug ocurra en los próximos 8 endpoints.

**Formato estándar (bajo rozamiento):**

```markdown
## LEARNINGS.md

### 2026-07-29 — BigDecimal comparación incorrecta
**Contexto:** Revisión del PR de PedidoService.calcularTotal()
**Bug:** La IA generó `if (monto == BigDecimal.ZERO)` para validar monto vacío.
  Pasó todos los unit tests porque los tests usaban el mismo literal.
  Falló en producción con valores calculados (0.00 != 0 por referencia).

**Insight capturado:**
- Comparar BigDecimal con `==` es siempre incorrecto.
- Usar siempre `monto.compareTo(BigDecimal.ZERO) == 0` o `BigDecimal.ZERO.equals(monto)`.

**Acciones:**
- [x] Añadido a CLAUDE.md sección Anti-patrones (#8)
- [x] Actualizado skill domain-model.md con sección "Comparaciones monetarias"
- [x] Añadido test de propiedad con jqwik: comparación con valores calculados

**Resultado:** Cero bugs de comparación BigDecimal en los siguientes 6 servicios.
```

```markdown
### 2026-07-31 — LocalDate.now() en dominio no es testeable
**Contexto:** Debugging de PedidoService.estaVencido() — tests no deterministas
**Bug:** La IA usó `LocalDate.now()` directamente en el método de dominio.
  Los tests fallaban aleatoriamente dependiendo de la fecha de ejecución.

**Insight:**
- Nunca usar `LocalDate.now()` o `Instant.now()` directamente en clases de dominio.
- Inyectar `Clock` como dependencia y usar `LocalDate.now(clock)`.
- Permite fijar el tiempo en tests: `Clock.fixed(Instant.parse("2026-01-15T00:00:00Z"), ZoneOffset.UTC)`

**Acciones:**
- [x] Añadido a CLAUDE.md Anti-patrones (#9)
- [x] Creado skill testing.md — sección "Time-dependent testing con Clock"
- [ ] Refactorizar PedidoService para inyectar Clock (backlog JIRA-789)
```

## **Los cuatro tipos de insight que vale la pena capturar**

![](https://cdn.hashnode.com/uploads/covers/64a79aba336591d2a1481aae/45df1286-e605-4c1d-9b98-79de208f8764.png align="center")

### **El ejemplo del micro-retorno**

Un bug en un parser de Spring Boot que asumió que un campo de versión era de longitud fija. La IA generó código basado en la especificación v6 sin saber que v7+ cambió el tamaño del campo.

**El problema (5 minutos de análisis):**

```java
// Lo que generó Claude — asumió versión fija
int version = buffer.readByte();  // correcto hasta v6
// v7+ usa 2 bytes — parseó el byte de datos como parte de la versión
```

**La solución permanente (10 minutos de documentación):**

```markdown
### 2026-07-15 — Campos de versión de longitud variable
**Anti-patrón:** Nunca asumir tamaño fijo de campos sin verificar la versión del protocolo.
**Regla:** Leer siempre la versión primero. Campos de longitud variable según versión
  requieren dispatch explícito: v1-6 = 1 byte, v7+ = 2 bytes (little-endian).
**Acciones:**
- [x] CLAUDE.md Anti-patrones: "Verificar versión de protocolo antes de parsear campos"
- [x] Skill creada: .claude/skills/parsing-versioning.md
```

**Resultado:** Cero bugs de parsing de versión en los siguientes 5 parsers generados.

* * *

## **El ciclo de disparadores: no confíes en tu memoria**

El error más común es decir "lo documento después". Después nunca llega. La solución es reemplazar la intención por **disparadores situacionales inmediatos**:

![](https://cdn.hashnode.com/uploads/covers/64a79aba336591d2a1481aae/26032fe6-770f-4bfa-bbab-2da38508500e.png align="center")

El mecanismo más efectivo es **integrar los disparadores directamente en el PR template**. El volante gira automáticamente con cada merge:

```markdown
<!-- .github/pull_request_template.md — sección de aprendizaje -->

## Documentación del aprendizaje

- [ ] `IMPLEMENTATION-HISTORY.md` actualizado (si fue una feature significativa)
- [ ] `LEARNINGS.md` tiene una entrada (si hubo nuevos insights o bugs encontrados)
- [ ] Skills relevantes actualizados en `.claude/skills/`
- [ ] `CLAUDE.md` actualizado si hay nuevos patrones o anti-patrones
```

Cuando esta sección está en el template, el equipo la ve en cada PR. No hace falta recordar. El proceso captura el aprendizaje automáticamente.

## **Los skills como módulos vivos**

Los skills del Pilar 1 no son documentos que se escriben una vez. Tienen un ciclo de vida:

![](https://cdn.hashnode.com/uploads/covers/64a79aba336591d2a1481aae/1c247406-0816-4f99-bb7c-955008b005f6.png align="center")

**Ejemplos de evolución en un proyecto Java:**

*   [`spring-boot.md`](http://spring-boot.md) se divide en [`spring-boot-controllers.md`](http://spring-boot-controllers.md) y [`spring-boot-config.md`](http://spring-boot-config.md) cuando supera las 400 líneas
    
*   [`javax-patterns.md`](http://javax-patterns.md) se archiva después de completar la migración a Jakarta EE
    
*   [`jpa-patterns.md`](http://jpa-patterns.md) y [`hibernate-config.md`](http://hibernate-config.md) se fusionan en [`persistence.md`](http://persistence.md) cuando el equipo unifica criterios
    

## **El retorno exponencial: los números reales**

El volante produce retorno compuesto, no lineal:

| Momento | Contexto | Automatización | Tiempo por feature |
| --- | --- | --- | --- |
| **Día 1** | 200 líneas / 5 skills | 10% | 4 horas |
| **Mes 1** | 767 líneas / 20 skills | 40% | 1.5 horas |
| **Mes 3** | 1.200+ líneas / 51 skills | 75% | 30 minutos |

El multiplicador de 8× no viene de un modelo más caro. Viene de un contexto más rico que permite a Haiku 4.5 hacer lo que antes requería Sonnet 5, y a Sonnet 5 hacer lo que antes requería intervención humana.

* * *

## **Las métricas del volante**

Cuatro KPIs que miden si el sistema está aprendiendo o estancado:

| Métrica | Target | Cómo medirla en Java |
| --- | --- | --- |
| **Tasa de captura** | \>80% de sesiones documentadas | Entradas en [LEARNINGS.md](http://LEARNINGS.md) / días de desarrollo activo |
| **Reducción de errores repetidos** | 90% de caída en 3 meses | Bugs del mismo tipo en JIRA: mes 1 vs. mes 3 |
| **Crecimiento del contexto** | +50 líneas/semana en los primeros 3 meses | `wc -l` [`CLAUDE.md`](http://CLAUDE.md) + total líneas en `.claude/skills/` |
| **Precisión al primer intento** | De 3-4 iteraciones a 1 | Rondas de revisión promedio antes del merge |

* * *

## **La rutina de mantenimiento**

El volante no requiere grandes inversiones de tiempo. Requiere consistencia:

![](https://cdn.hashnode.com/uploads/covers/64a79aba336591d2a1481aae/08bcf470-9bb8-421e-ae2a-12c909e617ce.png align="center")

## **Empieza hoy: tres acciones en 30 minutos**

**1\. Crea** [`LEARNINGS.md`](http://LEARNINGS.md) **en la raíz del proyecto** (5 min):

```markdown
# LEARNINGS.md — Registro de aprendizaje del equipo

## Formato de entrada
- **Fecha:** YYYY-MM-DD
- **Contexto:** ¿En qué estabas trabajando?
- **Insight:** ¿Qué aprendiste?
- **Acciones:** ¿Qué actualizaste? (CLAUDE.md / skill / test)
- **Resultado:** ¿Qué evitó este aprendizaje?

---
<!-- Primera entrada: usa el bug más reciente que arreglaste -->
```

2\. Añade la sección de aprendizaje al PR template (10 min): Copia el bloque de la sección anterior a .github/pull\_request\_template.md. A partir de ese momento, cada PR activa el ciclo.

3.Añade la primera entrada basada en el último bug de Java (15 min): No esperes al próximo bug. El último que arreglaste esta semana — el NullPointerException en el servicio de pagos, la excepción de Hibernate inesperada, el test que fallaba aleatoriamente — ya tiene un insight que vale la pena capturar.

## **El cierre de la serie**

Seis pilares. Una arquitectura de trabajo que se autorefuerza:

![](https://cdn.hashnode.com/uploads/covers/64a79aba336591d2a1481aae/bf55f63d-154d-4a13-8215-03ee5730575e.png align="center")

El Pilar 6 cierra el ciclo: los aprendizajes capturados enriquecen el [CLAUDE.md](http://CLAUDE.md) y los skills del Pilar 1, que mejoran la delegación del Pilar 2, que hacen más efectiva la verificación del Pilar 3, que fortalecen la agencia del Pilar 4, que se integran en los guardrails del Pilar 5, que generan más aprendizajes para capturar.

Cada bug resuelto hace al sistema más inteligente. Cada patrón documentado hace al equipo más rápido. Cada insight capturado compone.

> Haz que tu IA sea permanentemente más inteligente con cada bug resuelto y patrón descubierto. Tus prompts de sistema son un motor de aprendizaje activo, no archivos estáticos.

* * *

*¿Tienes algún* [`LEARNINGS.md`](http://LEARNINGS.md) *en tu proyecto Java? ¿O esos insights viven solo en tu memoria hasta que se olvidan? El próximo bug que arregles es el mejor momento para empezar.*

*Enjoy!*

*Joe*
