@saulwade/swl-ses 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +3 -3
- package/README.md +5 -5
- package/agentes/orquestador-swl.md +89 -1
- package/agentes/revisor-codigo-swl.md +34 -10
- package/agentes/revisor-seguridad-swl.md +7 -0
- package/agentes/tdd-qa-swl.md +23 -2
- package/comandos/swl/autoresearch.md +102 -6
- package/comandos/swl/metricas.md +34 -0
- package/comandos/swl/nemesis.md +42 -1
- package/comandos/swl/planear-fase.md +8 -0
- package/comandos/swl/predecir.md +139 -0
- package/comandos/swl/verificar.md +50 -7
- package/habilidades/angular-moderno/SKILL.md +44 -1
- package/habilidades/autoresearch/SKILL.md +15 -1
- package/habilidades/calidad-mutation-testing/SKILL.md +170 -0
- package/habilidades/changelog-generator/scripts/parse-commits.js +2 -1
- package/habilidades/checklist-seguridad/SKILL.md +29 -1
- package/habilidades/checklist-seguridad/recursos/stride-cobertura.md +60 -0
- package/habilidades/css-moderno/SKILL.md +3 -1
- package/habilidades/fastapi-experto/SKILL.md +56 -5
- package/habilidades/patrones-python/SKILL.md +8 -5
- package/habilidades/proceso-debate-adversarial/SKILL.md +164 -0
- package/habilidades/proceso-debate-adversarial/recursos/personas.md +105 -0
- package/habilidades/proceso-dynamic-workflows/SKILL.md +138 -0
- package/habilidades/proceso-dynamic-workflows/recursos/template-adversarial-verify.js +65 -0
- package/habilidades/proceso-dynamic-workflows/recursos/template-triage.js +65 -0
- package/habilidades/tdd-workflow/SKILL.md +14 -1
- package/habilidades/tdd-workflow/recursos/gherkin-bdd.md +111 -0
- package/hooks/contexto-iteracion.js +144 -0
- package/hooks/lib/loop-telemetry.js +321 -0
- package/hooks/notificacion-telegram.js +11 -3
- package/llms.txt +29 -0
- package/manifiestos/hooks-config.json +10 -1
- package/manifiestos/modulos.json +7 -1
- package/manifiestos/skills-lock.json +45 -24
- package/package.json +4 -3
- package/plugin.json +5 -2
- package/reglas/arquitectura.evolved.json +7 -0
- package/reglas/arquitectura.md +65 -0
- package/reglas/seguridad.evolved.json +7 -0
- package/reglas/seguridad.md +144 -0
- package/scripts/generar-inventario.js +64 -1
- package/scripts/instalador.js +32 -2
- package/scripts/smoke-test.js +24 -2
|
@@ -5,12 +5,12 @@ description: >
|
|
|
5
5
|
testing con httpx. Incluye el anti-patrón crítico MissingGreenlet (lazy loading
|
|
6
6
|
en async). Cargar cuando se implementen endpoints FastAPI, schemas Pydantic v2,
|
|
7
7
|
queries SQLAlchemy async, WebSockets, SSE o tests de integración con httpx.
|
|
8
|
-
version: "1.3.
|
|
8
|
+
version: "1.3.1"
|
|
9
9
|
evolved: true
|
|
10
|
-
evolved-from: "1.
|
|
11
|
-
evolved-at: "2026-
|
|
12
|
-
evolved-by: "
|
|
13
|
-
evolved-note: "
|
|
10
|
+
evolved-from: "1.3.0"
|
|
11
|
+
evolved-at: "2026-06-04"
|
|
12
|
+
evolved-by: "evolucionar"
|
|
13
|
+
evolved-note: "3 patrones nuevos OIC v1.5 2026-06-04: handler logging defensivo con sesión SQLAlchemy desacoplada (PE-001); tests pytest cuelgan si handler abre engine con pool_pre_ping al startup sin BD + receta conftest (PE-002); tests de endpoint sin BD con dependency_overrides[get_db]=lambda:None + monkeypatch del service (PE-008)"
|
|
14
14
|
herramientasPermitidas: [Read]
|
|
15
15
|
exclusiones:
|
|
16
16
|
- "No cargar para proyectos Django o Flask — los patrones de ORM sync, Class-Based Views y middleware difieren fundamentalmente; cargar `django-experto` o el skill del framework correspondiente."
|
|
@@ -267,6 +267,57 @@ Beneficios:
|
|
|
267
267
|
|
|
268
268
|
Regla: para cualquier whitelist usada en `setattr` ciego (PATCH endpoints, factory methods, deserializadores manuales), declarar como `ClassVar[frozenset[str]]` de la clase del service. NUNCA como variable local del método. NUNCA como módulo-level constant fuera de la clase (pierde el namespacing).
|
|
269
269
|
|
|
270
|
+
- **Handler logging que persiste en BD durante manejo de excepciones — SIEMPRE usar sesión SQLAlchemy desacoplada + silenciar fallos** (patrón portable; caso real OIC v1.5 BitacoraErrorHandler 2026-06-04): si un `logging.Handler` custom (audit trail, Sentry-style, métricas async, observabilidad) usa la sesión del request para persistir el evento, durante una excepción HTTP la sesión está en estado roto y `session.add()` falla, perdiendo la excepción original. Patrón correcto:
|
|
271
|
+
```python
|
|
272
|
+
# Engine PROPIO independiente del pool del request (pool=2 suficiente).
|
|
273
|
+
engine = create_engine(settings.DATABASE_URL, pool_pre_ping=True, pool_size=2, max_overflow=2)
|
|
274
|
+
session_factory = sessionmaker(bind=engine, autocommit=False, autoflush=False)
|
|
275
|
+
|
|
276
|
+
class MiHandler(logging.Handler):
|
|
277
|
+
def emit(self, record):
|
|
278
|
+
try:
|
|
279
|
+
self._emit_unsafe(record)
|
|
280
|
+
except Exception: # noqa: BLE001 — silencio defensivo intencional
|
|
281
|
+
# NUNCA loggear con `logging` desde aquí (riesgo de recursión infinita).
|
|
282
|
+
pass
|
|
283
|
+
|
|
284
|
+
def _emit_unsafe(self, record):
|
|
285
|
+
sess = session_factory() # sesión NUEVA, no la del request
|
|
286
|
+
try:
|
|
287
|
+
sess.add(entry); sess.commit()
|
|
288
|
+
finally:
|
|
289
|
+
sess.close()
|
|
290
|
+
```
|
|
291
|
+
Reglas clave: (a) engine propio; (b) `try/except: pass` agresivo en `emit()`; (c) NUNCA emitir a logger propio; (d) filtrar loggers ruidosos por nombre (`sqlalchemy.*`, `uvicorn.access`, `httpx`, `httpcore`, `asyncio`, `urllib3`); (e) idempotente: verificar si ya está en `root.handlers` antes de registrar. Aplicable a Sentry-style integrations, audit handlers, métricas async, alertas.
|
|
292
|
+
|
|
293
|
+
- **Tests pytest se cuelgan al importar `app.main` cuando un handler abre engine BD con `pool_pre_ping=True` al startup** (gotcha crítico OIC v1.5 2026-06-04): si un handler de logging custom se instala en `setup_logging()` y construye un engine con `pool_pre_ping=True`, sin PostgreSQL corriendo SQLAlchemy intenta una query de verificación en `socket.connect()` infinito. El test no falla — se cuelga sin output (ni siquiera el header de pytest). Causa: el bloqueo ocurre en la **importación** del módulo `app.main` (que llama `setup_logging()`), no en el test mismo. Síntoma típico: 3+ invocaciones de `pytest` en background sin output, requiere matar `python.exe` manualmente. Solución obligatoria en `backend/tests/conftest.py`:
|
|
294
|
+
```python
|
|
295
|
+
# ANTES de importar app.main
|
|
296
|
+
os.environ.setdefault("ENV", "test")
|
|
297
|
+
os.environ.setdefault("SKIP_DB_HEALTHCHECK", "true")
|
|
298
|
+
# Por CADA handler del proyecto que abre engine al startup:
|
|
299
|
+
os.environ.setdefault("SWL_BITACORA_ERRORES_ENABLED", "false")
|
|
300
|
+
# (sustituir por la env var real del proyecto)
|
|
301
|
+
```
|
|
302
|
+
Regla: cualquier handler con `pool_pre_ping=True` en su engine debe tener un kill-switch env var, y el conftest debe deshabilitarlo. Sin esto, los tests del proyecto serán inviables sin BD real.
|
|
303
|
+
|
|
304
|
+
- **Tests de endpoint sin BD con `dependency_overrides[get_db]=lambda:None` + monkeypatch del service** (patrón portable validado OIC v1.5 Slice 4 2026-06-04): para tests de endpoint que verifican contrato HTTP (auth, validación de query params, estructura de respuesta, rate-limit, headers de export CSV) sin requerir PostgreSQL. 25 tests en 0.23s.
|
|
305
|
+
```python
|
|
306
|
+
@pytest.fixture
|
|
307
|
+
def client_admin(monkeypatch: pytest.MonkeyPatch) -> TestClient:
|
|
308
|
+
app.dependency_overrides[requiere_admin] = _fake_admin # stub user
|
|
309
|
+
app.dependency_overrides[get_db] = lambda: None # type: ignore[return-value]
|
|
310
|
+
# Mock del service: el endpoint llama service.metodo(db, ...) pero `db` es None;
|
|
311
|
+
# el service no debe ejecutarse — se reemplaza con fake que devuelve schema válido.
|
|
312
|
+
from app.services import mi_service
|
|
313
|
+
monkeypatch.setattr(mi_service, "listar_paginado",
|
|
314
|
+
lambda db, filtros, page, per_page: SchemaPage(items=[], total=0, ...))
|
|
315
|
+
with TestClient(app) as c:
|
|
316
|
+
yield c
|
|
317
|
+
app.dependency_overrides.clear()
|
|
318
|
+
```
|
|
319
|
+
Aplicabilidad: cualquier endpoint que dependa de `get_db` + service mockeable. NO requiere BD real ni fixtures de datos. Tests de integración con BD viven aparte (`@pytest.mark.integration`).
|
|
320
|
+
|
|
270
321
|
## Referencias especializadas
|
|
271
322
|
|
|
272
323
|
| Tema | Archivo |
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: patrones-python
|
|
3
3
|
description: Idiomas pythonicos, PEP 8, type hints modernos, dataclasses, async/await, context managers, decorators y generators. Patrones de código limpio en Python.
|
|
4
|
-
version: "1.
|
|
4
|
+
version: "1.4.2"
|
|
5
5
|
evolved: true
|
|
6
|
-
evolved-from: "1.
|
|
7
|
-
evolved-at: "2026-05
|
|
8
|
-
evolved-by: "
|
|
9
|
-
evolved-note: "+
|
|
6
|
+
evolved-from: "1.4.1"
|
|
7
|
+
evolved-at: "2026-06-05"
|
|
8
|
+
evolved-by: "evolucionar"
|
|
9
|
+
evolved-note: "+gotcha PEP 758: except A,B: sin paréntesis es válido en Python >=3.14, no SyntaxError; cubre efecto colateral de ruff format (target py314) reformateando archivo completo. Origen: falso positivo reproducible en sistema-verificacion-oic (requires-python >=3.14)"
|
|
10
10
|
herramientasPermitidas: [Read, Glob, Grep]
|
|
11
11
|
exclusiones:
|
|
12
12
|
- "No cargar para patrones de un framework específico (FastAPI, Django, Celery) — los idiomas generales de este skill aplican, pero los patrones de framework tienen restricciones adicionales; cargar el skill del framework correspondiente."
|
|
@@ -205,6 +205,9 @@ ver [recursos/referencia-completa.md](recursos/referencia-completa.md).
|
|
|
205
205
|
- **`__slots__` en clase Python produce `TypeError: multiple bases have instance lay-out conflict`** al heredar de otra clase con `__slots__`: las subclases con `__slots__` requieren que todos los ancestros también tengan `__slots__`, o que el ancestro directo sea `object`. Causa: si `ClaseBase` no tiene `__slots__`, tiene un `__dict__` implícito; si `ClaseHija` tiene `__slots__`, hay conflicto de layout de memoria. Solución: o agregar `__slots__ = ()` vacío a la clase base, o eliminar `__slots__` de la subclase — no mezclar clases con y sin `__slots__` en la misma jerarquía.
|
|
206
206
|
- **`property` setter que modifica un campo privado no refleja el cambio en `__repr__` generado por dataclass**: el `@property` en un dataclass crea un campo de clase que conflictúa con el campo de instancia del dataclass. Causa: `@dataclass` genera `__repr__` basado en los campos declarados en `__init__` — si el setter modifica un atributo con nombre diferente (ej: `_valor`), `__repr__` muestra el campo original sin la modificación. Solución: usar `field(init=False, repr=False)` para el campo interno y exponer solo la `property` en la interfaz pública.
|
|
207
207
|
- **`assert` no es guard de invariantes en producción con `PYTHONOPTIMIZE=1` o `python -O`**: el bytecode optimizado **elimina** todos los `assert` del módulo, por lo que `assert x is not None; return x` puede retornar `None` violando el contrato `-> dict` en producción aunque pase tests en desarrollo. El test runner por defecto NO usa `-O`, por lo que el bug es invisible hasta que alguien despliega con `PYTHONOPTIMIZE=1` (configuración común para reducir memoria en imágenes Docker production). Causa: `assert` está documentado en Python como herramienta de **debugging**, no de validación. Solución: para invariantes que DEBEN cumplirse en producción, usar guard explícito con raise: `if x is None: raise HTTPException(500, "Invariante violado")` o `if x is None: raise RuntimeError(...)`. Reservar `assert` solo para tests, scripts, o pre-condiciones triviales en código de desarrollo. Regla rápida: si el assert protege un caso que activa una respuesta del usuario o un side-effect, NO es assert — es validación y debe ser `if/raise`.
|
|
208
|
+
- **Pasar `None` explícito a un parámetro NO activa su valor por defecto en el callee**: si una función declara `def listar(activa: bool = True)` y el caller hace `listar(activa=valor)` donde `valor` puede ser `None` (típico con parámetros opcionales que viajan entre capas: `activa: bool | None = None`), el callee recibe `None`, NO `True`. Python aplica el default SOLO cuando el argumento se **omite literalmente** en la llamada, no cuando recibe `None` explícito. Causa: el binding del parámetro usa el valor pasado, sea cual sea — el default es fallback de *ausencia*, no de *nulidad*. El bug es silencioso: pasa los tests donde el caller omite el parámetro y falla en producción cuando el cliente omite el valor y una capa intermedia lo traduce a `None` antes de propagarlo. Caso real: un endpoint que documentaba "omitir devuelve solo activos" retornaba inactivos porque el router pasaba `activa=None` al service cuyo default era `True`. Solución: normalizar en la frontera — en el caller (`listar(activa=True if valor is None else valor)`) o tratando `None` como sentinel en el callee (`if activa is None: activa = True`). Regla: cuando un valor opcional cruza de una capa a otra y el callee tiene un default distinto de `None`, convertir `None → default` explícitamente en el punto de cruce.
|
|
209
|
+
- **`getattr(obj, "attr", default)` tampoco aplica `default` si el atributo existe con valor `None`**: misma raíz que el gotcha anterior, otra manifestación. `getattr` usa `default` SOLO cuando el atributo **no existe**; si existe con valor `None` (caso típico: campos opcionales de Pydantic/SQLAlchemy inicializados a `None`), devuelve `None`, no `default`. Así `getattr(modelo, "campo_opcional", "X")` retorna `None` cuando `campo_opcional is None`, no `"X"`. Solución sin ambigüedad: `raw = getattr(modelo, "campo_opcional", None); valor = raw if raw is not None else "X"` (el atajo `getattr(...) or "X"` solo sirve si `""` y `0` son aceptables como falsy). Regla unificada para ambos casos: en Python el `default` —de parámetro o de `getattr`— es fallback de **ausencia**, nunca de **nulidad**; si `None` es un valor posible, normalízalo en la frontera.
|
|
210
|
+
- **`except A, B:` sin paréntesis es sintaxis válida en Python ≥3.14 (PEP 758), NO un SyntaxError**: desde 3.14 los paréntesis en `except`/`except*` con múltiples tipos son opcionales (`except ValueError, TypeError:` ≡ `except (ValueError, TypeError):`). Bajo intérpretes <3.14 esa forma SÍ es SyntaxError, lo que confunde a herramientas y revisores que juzgan la sintaxis contra su conocimiento general del lenguaje en vez de contra la versión objetivo del proyecto (`requires-python`/`tool.ruff.target-version` en `pyproject.toml`). Causa: la validez de la sintaxis depende de la versión, no es absoluta. Efecto colateral a vigilar: `ruff format` con `target-version = "py314"` **quita** los paréntesis redundantes y, además, reformatea el archivo COMPLETO — puede tocar `except` ajenos a tu cambio (churn no intencional); `ruff check` (lo único que suele correr el CI) conserva la forma que encuentre. Solución: antes de marcar cualquier `except A, B:` como error, leer la versión objetivo del proyecto y verificar con evidencia ejecutable (`python -c "import <módulo>"` + `ruff check`). Si se prefieren los paréntesis por portabilidad/Pyright, restaurarlos a mano y NO correr `ruff format` sobre esas líneas. Caso real (2026-06-05): un proyecto con `requires-python >=3.14` recibió un falso positivo de SyntaxError CRÍTICO por esta forma; refutado con import + ruff + CI verde.
|
|
208
211
|
|
|
209
212
|
---
|
|
210
213
|
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: proceso-debate-adversarial
|
|
3
|
+
description: >
|
|
4
|
+
Protocolo de debate adversarial con jueces ciegos para decisiones técnicas
|
|
5
|
+
subjetivas: dos autores en frío generan candidatos, un crítico forzosamente
|
|
6
|
+
adversarial los ataca, un sintetizador produce un híbrido y un panel de jueces
|
|
7
|
+
ciegos con etiquetas aleatorizadas elige al ganador hasta convergencia.
|
|
8
|
+
Cargar cuando una decisión de arquitectura, diseño o estrategia no tiene
|
|
9
|
+
métrica objetiva y el riesgo de sycophancy (auto-aprobarse) es alto; también
|
|
10
|
+
cuando arquitecto-swl evalúa alternativas o /swl:predecir necesita el
|
|
11
|
+
protocolo de personas en frío.
|
|
12
|
+
version: "1.0.0"
|
|
13
|
+
herramientasPermitidas: [Read, Agent, Skill]
|
|
14
|
+
exclusiones:
|
|
15
|
+
- "No cargar para decisiones con métrica objetiva verificable — ahí aplica el loop de autoresearch (Verify numérico), no un debate."
|
|
16
|
+
- "No cargar para revisión de código post-implementación — eso es revisor-codigo-swl / nemesis-auditor-swl."
|
|
17
|
+
- "No cargar para decisiones triviales o de preferencia personal sin impacto técnico — el costo de 5+ invocaciones de agente no se justifica."
|
|
18
|
+
evolvable: true
|
|
19
|
+
---
|
|
20
|
+
# Debate Adversarial con Jueces Ciegos
|
|
21
|
+
|
|
22
|
+
Protocolo para decidir entre alternativas técnicas **sin métrica objetiva**
|
|
23
|
+
eliminando los dos sesgos que arruinan las auto-evaluaciones de un LLM:
|
|
24
|
+
**sycophancy** (el agente aprueba lo que él mismo generó) y **position bias**
|
|
25
|
+
(el juez prefiere la opción presentada primero). Patrón adoptado del análisis
|
|
26
|
+
de autoresearch v2.1 (`reason-judge-protocol`), adaptado al ecosistema SWL.
|
|
27
|
+
|
|
28
|
+
**Principio**: el que genera nunca juzga, el que juzga nunca sabe quién generó.
|
|
29
|
+
|
|
30
|
+
## Cuándo cargar este skill
|
|
31
|
+
|
|
32
|
+
- Decisión de arquitectura con 2+ alternativas viables sin benchmark objetivo
|
|
33
|
+
(ej: event sourcing vs CRUD+audit, monolito modular vs microservicios).
|
|
34
|
+
- `arquitecto-swl` necesita justificar un ADR con alternativas evaluadas de
|
|
35
|
+
forma no sesgada.
|
|
36
|
+
- `/swl:predecir` requiere el protocolo de aislamiento de personas.
|
|
37
|
+
- Decisión de producto/estrategia donde el usuario pide "dame la mejor opción"
|
|
38
|
+
y una sola pasada produciría la primera idea plausible, no la mejor.
|
|
39
|
+
|
|
40
|
+
## Los 5 roles — aislamiento obligatorio (COLD START)
|
|
41
|
+
|
|
42
|
+
| Rol | Recibe | Produce | Regla dura |
|
|
43
|
+
|-----|--------|---------|------------|
|
|
44
|
+
| **Autor-A** | tarea | candidato A | No ve crítica ni candidato B |
|
|
45
|
+
| **Crítico** | tarea + candidato A | ≥3 debilidades con evidencia + qué haría un candidato superior | NUNCA elogia — rol puramente adversarial |
|
|
46
|
+
| **Autor-B** | tarea + candidato A + crítica | candidato B que resuelve la crítica preservando fortalezas de A | No ve al sintetizador |
|
|
47
|
+
| **Sintetizador** | tarea + A + B | candidato híbrido AB | Fusiona lo mejor de ambos, no promedia |
|
|
48
|
+
| **Panel de jueces** (3 default) | tarea + 3 candidatos con **etiquetas aleatorizadas** (X, Y, Z) | ranking 1°/2°/3° + justificación de un párrafo c/u | "Todos están bien" NO es veredicto válido |
|
|
49
|
+
|
|
50
|
+
**COLD START**: cada rol se ejecuta como invocación independiente del Agent
|
|
51
|
+
tool **sin contexto compartido de sesión** — recibe SOLO los insumos de su fila.
|
|
52
|
+
Pasar el historial completo a un juez invalida el protocolo (sabría quién
|
|
53
|
+
escribió qué).
|
|
54
|
+
|
|
55
|
+
**Aleatorización de etiquetas**: antes de invocar a los jueces, mapear
|
|
56
|
+
A/B/AB → X/Y/Z con orden aleatorio distinto por juez. Previene position bias.
|
|
57
|
+
|
|
58
|
+
## El loop de convergencia
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
Ronda N:
|
|
62
|
+
1. Autor-A genera candidato (ronda 1) o presenta al incumbente (ronda N>1)
|
|
63
|
+
2. Crítico ataca → ≥3 debilidades con evidencia
|
|
64
|
+
3. Autor-B genera candidato alternativo
|
|
65
|
+
4. Sintetizador produce híbrido AB
|
|
66
|
+
5. Panel ciego vota → ganador por mayoría (empate → gana el híbrido)
|
|
67
|
+
6. ¿Ganador == incumbente? → convergencia++ ; si no → convergencia = 1,
|
|
68
|
+
el ganador se vuelve incumbente
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Condición | Acción |
|
|
72
|
+
|-----------|--------|
|
|
73
|
+
| Mismo incumbente gana 3 rondas consecutivas | **CONVERGIDO** — terminar |
|
|
74
|
+
| Ronda ≥ max (default 8) | **ACOTADO** — reportar mejor candidato actual |
|
|
75
|
+
| Incumbente cambió >5 veces en las últimas 8 rondas | **OSCILACIÓN** — detener y reportar: la tarea está mal planteada o las alternativas son equivalentes |
|
|
76
|
+
|
|
77
|
+
Registrar cada ronda con `hooks/lib/loop-telemetry.js` (tipo `debate`,
|
|
78
|
+
columnas `ronda, timestamp, etiqueta_ganadora, veredicto, convergencia,
|
|
79
|
+
descripcion`) para que la trayectoria sea auditable y `/swl:metricas` la lea.
|
|
80
|
+
|
|
81
|
+
## Anti-herd check — obligatorio
|
|
82
|
+
|
|
83
|
+
Si TODOS los jueces coinciden en la primera ronda, el sintetizador DEBE
|
|
84
|
+
producir al menos un contraargumento antes de aceptar el consenso. Unanimidad
|
|
85
|
+
inmediata en un debate de alternativas reales es señal de herd bias, no de
|
|
86
|
+
calidad — las alternativas genuinas siempre tienen tradeoffs defendibles.
|
|
87
|
+
|
|
88
|
+
## Criterios de juez por dominio
|
|
89
|
+
|
|
90
|
+
| Dominio | Criterios de evaluación |
|
|
91
|
+
|---------|------------------------|
|
|
92
|
+
| Arquitectura de software | escalabilidad, mantenibilidad, rendimiento, seguridad, simplicidad |
|
|
93
|
+
| Estrategia de producto | encaje de mercado, factibilidad, diferenciación, riesgo, tiempos |
|
|
94
|
+
| Decisión de negocio | ROI, riesgo, alineación, recursos requeridos, reversibilidad |
|
|
95
|
+
| Enfoque de seguridad | cobertura, tasa de falsos positivos, practicidad, cumplimiento |
|
|
96
|
+
| Hipótesis de investigación | testabilidad, novedad, soporte de evidencia, poder explicativo |
|
|
97
|
+
|
|
98
|
+
El juez evalúa CADA candidato contra TODOS los criterios del dominio y
|
|
99
|
+
produce ranking con justificación — nunca un score suelto sin comparación.
|
|
100
|
+
|
|
101
|
+
## Personas para análisis predictivo
|
|
102
|
+
|
|
103
|
+
Cuando el objetivo es **predecir problemas de un cambio propuesto** (no
|
|
104
|
+
elegir entre alternativas), usar el modo personas: 5 expertos analizan EN FRÍO
|
|
105
|
+
el mismo cambio y un sintetizador deduplica y rankea. Definiciones completas,
|
|
106
|
+
preguntas guía y red flags por persona en
|
|
107
|
+
[recursos/personas.md](recursos/personas.md). Set default: Arquitecto de
|
|
108
|
+
Software, Analista de Seguridad, Ingeniero de Rendimiento, Ingeniero de
|
|
109
|
+
Confiabilidad, Abogado del Diablo. Set adversarial (`--adversarial`): El
|
|
110
|
+
Rompedor, El Tramposo, El Escalador, El Novato, El Insider Malicioso.
|
|
111
|
+
|
|
112
|
+
Ranking de hallazgos del sintetizador:
|
|
113
|
+
`severidad × confianza promedio × número de personas que coinciden`.
|
|
114
|
+
|
|
115
|
+
## Integración con el ecosistema SWL
|
|
116
|
+
|
|
117
|
+
| Componente | Uso del protocolo |
|
|
118
|
+
|-----------|-------------------|
|
|
119
|
+
| `arquitecto-swl` | Debate para la sección "Alternativas consideradas" de un ADR |
|
|
120
|
+
| `/swl:predecir` | Modo personas pre-implementación |
|
|
121
|
+
| `/swl:nemesis` | El evaluator puede pedir un debate cuando dos remediaciones compiten |
|
|
122
|
+
| `hooks/lib/loop-telemetry.js` | Registro de rondas + handoff para encadenar |
|
|
123
|
+
| `/swl:metricas` | Lectura de trayectorias de debates en `.planning/loops/` |
|
|
124
|
+
|
|
125
|
+
## Cuándo NO cargar
|
|
126
|
+
|
|
127
|
+
- La decisión tiene métrica objetiva (latencia, cobertura, bundle size) — usar
|
|
128
|
+
el loop autoresearch con Verify numérico; un debate es más caro y menos
|
|
129
|
+
preciso que medir.
|
|
130
|
+
- El usuario ya tomó la decisión y está documentada en ADR/vault — aplicar
|
|
131
|
+
`consultar-vault-primero`, no reabrir con un debate.
|
|
132
|
+
- Hay restricción dura que elimina las alternativas (compliance, presupuesto,
|
|
133
|
+
stack fijo) — verificar restricciones ANTES de armar el debate.
|
|
134
|
+
|
|
135
|
+
## Gotchas / Errores comunes no obvios
|
|
136
|
+
|
|
137
|
+
- **Jueces con contexto contaminado**: invocar a los jueces en la misma
|
|
138
|
+
conversación donde se generaron los candidatos les revela la autoría por el
|
|
139
|
+
historial. Causa: usar el contexto principal en vez del Agent tool con
|
|
140
|
+
prompt acotado. Solución: cada juez es una invocación Agent independiente
|
|
141
|
+
cuyo prompt contiene SOLO tarea + candidatos etiquetados.
|
|
142
|
+
- **Crítico que "equilibra"**: el crítico señala 3 debilidades pero cierra con
|
|
143
|
+
"en general es un buen enfoque" — eso re-introduce sycophancy y debilita al
|
|
144
|
+
Autor-B. Solución: el prompt del crítico prohíbe explícitamente elogios y
|
|
145
|
+
exige proponer qué haría un candidato superior.
|
|
146
|
+
- **Sintetizador que promedia en vez de fusionar**: produce un candidato
|
|
147
|
+
"tibio" que toma la mitad de cada uno y pierde la coherencia interna de
|
|
148
|
+
ambos. Solución: el híbrido debe tener una tesis propia — tomar la
|
|
149
|
+
arquitectura dominante de uno e injertar mecanismos puntuales del otro.
|
|
150
|
+
- **Convergencia falsa por candidatos idénticos**: si Autor-B produce
|
|
151
|
+
esencialmente el mismo candidato que A, el panel "converge" en ronda 2 sin
|
|
152
|
+
exploración real. Solución: el prompt de Autor-B exige divergencia
|
|
153
|
+
estructural, no cosmética; si la crítica fue débil, regenerar la crítica.
|
|
154
|
+
|
|
155
|
+
## Anti-patrones
|
|
156
|
+
|
|
157
|
+
- **Debate de 1 ronda presentado como consenso** — sin convergencia ×3 no hay
|
|
158
|
+
veredicto, hay una primera impresión cara.
|
|
159
|
+
- **Saltarse la aleatorización de etiquetas** "porque los jueces son
|
|
160
|
+
imparciales" — el position bias es estadístico, no intencional.
|
|
161
|
+
- **Usar el debate para decisiones ya cerradas** — teatro de proceso que
|
|
162
|
+
quema tokens para justificar lo decidido.
|
|
163
|
+
- **Panel de 1 juez** — un juez único reintroduce el sesgo individual que el
|
|
164
|
+
panel existe para diluir; mínimo 3, número impar.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Personas para análisis predictivo y debate adversarial
|
|
2
|
+
|
|
3
|
+
Definiciones operativas de las personas que consume `proceso-debate-adversarial`
|
|
4
|
+
(modo personas) y `/swl:predecir`. Cada persona se invoca EN FRÍO (COLD START):
|
|
5
|
+
recibe la descripción del cambio + conocimiento del codebase + sus criterios —
|
|
6
|
+
nunca el análisis de otra persona.
|
|
7
|
+
|
|
8
|
+
Formato de hallazgo obligatorio por persona:
|
|
9
|
+
|
|
10
|
+
```markdown
|
|
11
|
+
| # | Hallazgo | Severidad | Confianza (0-100%) | archivo:línea | Recomendación |
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Presupuesto: `max_hallazgos_por_persona = presupuesto_total / num_personas`
|
|
15
|
+
(default presupuesto 40 → 8 por persona). Obliga a priorizar, no a enumerar.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Set default (análisis predictivo estándar)
|
|
20
|
+
|
|
21
|
+
### 1. Arquitecto de Software
|
|
22
|
+
- **Enfoque**: diseño sistémico, fronteras de componentes, flujo de datos, escalabilidad.
|
|
23
|
+
- **Preguntas guía**: ¿Escala? ¿Los límites entre módulos son limpios? ¿El acoplamiento está minimizado? ¿Sobrevive un crecimiento 10x?
|
|
24
|
+
- **Evidencia exigida**: archivo:línea, grafo de dependencias, métricas de acoplamiento.
|
|
25
|
+
- **Red flags**: god classes, dependencias circulares, abstracciones con fugas, estado mutable compartido.
|
|
26
|
+
|
|
27
|
+
### 2. Analista de Seguridad
|
|
28
|
+
- **Enfoque**: superficies de ataque, autenticación/autorización, protección de datos, vectores de inyección.
|
|
29
|
+
- **Preguntas guía**: ¿Es explotable? ¿Los trust boundaries se aplican? ¿El input se sanitiza? ¿Los secretos están protegidos?
|
|
30
|
+
- **Evidencia exigida**: archivo:línea + escenario de ataque concreto (no especulación teórica).
|
|
31
|
+
- **Red flags**: SQL crudo, authz faltante, secretos hardcodeados, input sin sanitizar.
|
|
32
|
+
|
|
33
|
+
### 3. Ingeniero de Rendimiento
|
|
34
|
+
- **Enfoque**: latencia, throughput, uso de recursos, complejidad algorítmica.
|
|
35
|
+
- **Preguntas guía**: ¿Es suficientemente rápido? ¿Cuál es el peor caso? ¿Dónde están los cuellos de botella? ¿El caching es efectivo?
|
|
36
|
+
- **Evidencia exigida**: archivo:línea, análisis de complejidad, estimaciones de recursos.
|
|
37
|
+
- **Red flags**: queries N+1, loops sin cota, índices faltantes, I/O síncrono en hot paths.
|
|
38
|
+
|
|
39
|
+
### 4. Ingeniero de Confiabilidad
|
|
40
|
+
- **Enfoque**: manejo de errores, modos de falla, observabilidad, recuperación.
|
|
41
|
+
- **Preguntas guía**: ¿Qué pasa cuando falla? ¿Podemos detectarlo? ¿Hay camino de recuperación? ¿Es observable?
|
|
42
|
+
- **Evidencia exigida**: archivo:línea, escenarios de falla, rutas de recuperación.
|
|
43
|
+
- **Red flags**: errores tragados, retries faltantes, sin circuit breakers, fallas silenciosas.
|
|
44
|
+
|
|
45
|
+
### 5. Abogado del Diablo
|
|
46
|
+
- **Enfoque**: asunciones, casos límite, complejidad oculta, mantenibilidad.
|
|
47
|
+
- **Preguntas guía**: ¿Qué asunciones son falsas? ¿Qué rompe esto? ¿Está sobre-ingenierizado?
|
|
48
|
+
- **Evidencia exigida**: contraejemplos concretos, escenarios de caso límite.
|
|
49
|
+
- **Red flags**: diseño solo-happy-path, asunciones sin probar, complejidad sin justificación.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Set adversarial (`--adversarial`)
|
|
54
|
+
|
|
55
|
+
Reemplaza al set default cuando el objetivo es estresar el cambio como atacante,
|
|
56
|
+
no como revisor.
|
|
57
|
+
|
|
58
|
+
### 1. El Rompedor
|
|
59
|
+
Intenta crashear o corromper el sistema. Busca: estados imposibles, inputs
|
|
60
|
+
malformados, condiciones de carrera, límites de recursos.
|
|
61
|
+
|
|
62
|
+
### 2. El Tramposo
|
|
63
|
+
Busca formas de saltarse reglas y abusar de features. Busca: validaciones solo
|
|
64
|
+
en frontend, límites evadibles, flujos alternos sin guards.
|
|
65
|
+
|
|
66
|
+
### 3. El Escalador
|
|
67
|
+
Imagina carga 1000x y encuentra qué se rompe primero. Busca: queries sin
|
|
68
|
+
paginación, locks globales, colas sin backpressure, costos lineales ocultos.
|
|
69
|
+
|
|
70
|
+
### 4. El Novato
|
|
71
|
+
Usa mal cada API esperando que funcione. Busca: defaults peligrosos, errores
|
|
72
|
+
crípticos, documentación que asume contexto, footguns de la interfaz.
|
|
73
|
+
|
|
74
|
+
### 5. El Insider Malicioso
|
|
75
|
+
Tiene credenciales válidas y quiere exfiltrar. Busca: permisos excesivos,
|
|
76
|
+
auditoría faltante, datos sensibles accesibles lateralmente.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Set red-team de seguridad (para `checklist-seguridad` modo cobertura)
|
|
81
|
+
|
|
82
|
+
Rotación de mentalidades para auditoría STRIDE/OWASP iterativa:
|
|
83
|
+
|
|
84
|
+
| Persona | Foco | Mentalidad |
|
|
85
|
+
|---------|------|-----------|
|
|
86
|
+
| Adversario de Seguridad | auth, crypto, inyección | atacante externo con browser + proxy de intercepción |
|
|
87
|
+
| Atacante de Supply Chain | dependencias, CI/CD, build pipeline | comprometer vía código de terceros |
|
|
88
|
+
| Amenaza Interna | acceso a datos, abuso de privilegios, exfiltración | usuario autenticado con intención maliciosa |
|
|
89
|
+
| Atacante de Infraestructura | red, configuración cloud, contenedores | apuntar a misconfigurations de infra |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Protocolo de síntesis (tras el análisis individual)
|
|
94
|
+
|
|
95
|
+
1. **Deduplicar**: mismo archivo:línea + mismo problema → fusionar, conservar la severidad más alta.
|
|
96
|
+
2. **Resolver conflictos**: si dos personas discrepan → registrar el disenso, no silenciarlo.
|
|
97
|
+
3. **Anti-herd check**: si TODAS las personas coinciden → el sintetizador DEBE producir ≥1 contraargumento.
|
|
98
|
+
4. **Rankear**: `severidad × confianza promedio × número de personas que coinciden`.
|
|
99
|
+
|
|
100
|
+
Output del sintetizador:
|
|
101
|
+
|
|
102
|
+
```markdown
|
|
103
|
+
### Consenso — [N hallazgos tras dedup]
|
|
104
|
+
| # | Hallazgo | Severidad | Acuerdo | Personas origen | Acción |
|
|
105
|
+
```
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: proceso-dynamic-workflows
|
|
3
|
+
description: >
|
|
4
|
+
Patrones canónicos de dynamic workflows en Claude Code (Workflow tool / ultracode):
|
|
5
|
+
classify-and-act, fan-out-and-synthesize, adversarial-verification, generate-and-filter,
|
|
6
|
+
tournament. Cubre los 3 modos de falla que justifican orquestar (agentic laziness,
|
|
7
|
+
self-preferential bias, goal drift), cuándo NO usar workflows, token budgets, revisión
|
|
8
|
+
cross-modelo, y el anti-patrón de envolver skills auto-iterantes en /loop|/cron. Cargar
|
|
9
|
+
cuando se diseñe una orquestación multi-agente, se decida si una tarea amerita workflow,
|
|
10
|
+
o se quiera empaquetar un workflow como plantilla reutilizable en un skill.
|
|
11
|
+
when_to_use: >
|
|
12
|
+
Usar cuando el usuario diga "ultracode", "workflow", "orquesta esto", "fan-out", "panel
|
|
13
|
+
de revisores", "tournament", o cuando una tarea sea larga, masivamente paralela o
|
|
14
|
+
adversarial y un solo context window sufra laziness/goal-drift.
|
|
15
|
+
herramientasPermitidas: [Read, Grep, Glob]
|
|
16
|
+
exclusiones:
|
|
17
|
+
- "No cargar para tareas de codificación normales de un solo paso — la mayoría NO necesitan un workflow; el harness por defecto basta y un workflow gasta muchos más tokens."
|
|
18
|
+
- "No cargar para el detalle de evaluator-optimizer del nemesis — eso vive en nemesis-evaluacion-json y el comando /swl:nemesis."
|
|
19
|
+
- "No cargar para escribir el .js de un workflow concreto — este skill da los patrones; el script se escribe con el Workflow tool directamente."
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# Dynamic workflows: patrones, modos de falla y cuándo orquestar
|
|
23
|
+
|
|
24
|
+
Síntesis del blog oficial de Anthropic *"A harness for every task: dynamic workflows
|
|
25
|
+
in Claude Code"* + patrones validados en swl-ses (nemesis evaluator-optimizer,
|
|
26
|
+
`/swl:verificar --until-converge`, `/swl:autoresearch`). Un dynamic workflow deja que
|
|
27
|
+
Claude escriba su propio harness (JS que coordina subagentes con ventanas aisladas).
|
|
28
|
+
|
|
29
|
+
## Cuándo cargar
|
|
30
|
+
|
|
31
|
+
- Se va a diseñar una orquestación multi-agente o decidir si una tarea amerita workflow.
|
|
32
|
+
- La tarea es larga, masivamente paralela, o adversarial (review, research, migración, triage).
|
|
33
|
+
- Se quiere empaquetar un workflow como plantilla reutilizable en un skill.
|
|
34
|
+
|
|
35
|
+
## Cuándo NO cargar
|
|
36
|
+
|
|
37
|
+
- Tarea de codificación normal de 1-2 archivos — el harness por defecto basta. Un workflow
|
|
38
|
+
gasta significativamente más tokens; la mayoría de tareas de coding NO necesitan un panel
|
|
39
|
+
de 5 revisores.
|
|
40
|
+
- Ya estás dentro del detalle del nemesis (`nemesis-evaluacion-json`).
|
|
41
|
+
- Solo necesitas escribir el `.js` — usa el Workflow tool directo con los patrones de abajo.
|
|
42
|
+
|
|
43
|
+
## Los 3 modos de falla que JUSTIFICAN orquestar
|
|
44
|
+
|
|
45
|
+
Un solo context window largo degrada de tres formas — nombrarlas guía cuándo separar Claudes:
|
|
46
|
+
|
|
47
|
+
1. **Agentic laziness** — Claude se detiene antes de terminar una tarea multi-parte y la
|
|
48
|
+
declara hecha con progreso parcial (ej. 20 de 50 ítems de un security review). Mitiga:
|
|
49
|
+
fan-out (un subagente por ítem, ventana limpia) + `/goal` (requisito de completitud duro).
|
|
50
|
+
2. **Self-preferential bias** — Claude prefiere sus propios resultados al verificarlos contra
|
|
51
|
+
un rubric. Mitiga: **adversarial-verification** con subagente separado, y mejor aún
|
|
52
|
+
**revisión cross-modelo** (el reviewer es OTRO modelo). Es el mismo principio que
|
|
53
|
+
`gobernanza.md § Separación revisor/ejecutor`.
|
|
54
|
+
3. **Goal drift** — pérdida gradual de fidelidad al objetivo tras muchos turnos y compaction
|
|
55
|
+
(cada resumen es lossy; se pierde "no hagas X"). Mitiga: subagentes con goal aislado y
|
|
56
|
+
constraints explícitas por invocación; `/goal` como ancla.
|
|
57
|
+
|
|
58
|
+
## Los 5 patrones canónicos
|
|
59
|
+
|
|
60
|
+
| Patrón | Cuándo | Forma |
|
|
61
|
+
|--------|--------|-------|
|
|
62
|
+
| **Classify-and-act** | Rutear según tipo de tarea/ítem | Clasificador → ramas distintas (al inicio o al final para decidir output) |
|
|
63
|
+
| **Fan-out-and-synthesize** | Muchos sub-pasos; cada uno se beneficia de ventana limpia sin cross-contaminación | N agentes en paralelo → barrier → synthesize merge. **Default** para descomponer |
|
|
64
|
+
| **Adversarial-verification** | Evitar self-preferential bias | Por cada agente, un agente separado verifica su output contra rubric. Diversificar lentes |
|
|
65
|
+
| **Generate-and-filter** | Espacio de ideas amplio (taste-based) | Generar N → filtrar por rubric/verificación → dedupe → solo las mejores probadas |
|
|
66
|
+
| **Tournament** | Solución taste-based con criterio | N agentes compiten con enfoques distintos → juez pairwise hasta ganador |
|
|
67
|
+
|
|
68
|
+
`pipeline()` (sin barrier entre stages) es el default; usa barrier (`parallel()` entre
|
|
69
|
+
stages) SOLO cuando el stage N necesita TODOS los resultados del N-1 (dedup global,
|
|
70
|
+
early-exit por conteo cero, comparación cruzada).
|
|
71
|
+
|
|
72
|
+
## Revisión cross-modelo (combate self-preferential bias estructuralmente)
|
|
73
|
+
|
|
74
|
+
El reviewer adversarial en el MISMO modelo aún arrastra sesgo. La mejora: **executor en un
|
|
75
|
+
modelo, reviewer en otro** (Claude ejecuta → Gemini/Codex/otro revisa). Patrón de ARIS
|
|
76
|
+
(`mcp-servers/gemini-review`): MCP que expone `review`/`review_reply`, devuelve JSON con
|
|
77
|
+
`threadId` + `response` → rastro auditable de un veredicto independiente.
|
|
78
|
+
|
|
79
|
+
En swl-ses esto es **opt-in** vía `/swl:nemesis --cross-model` (ver el comando): si hay un
|
|
80
|
+
MCP reviewer configurado, la verificación se rutea a un modelo externo; si no, degrada al
|
|
81
|
+
reviewer same-model sin fallar (regla `arreglar-al-detectar.md` / no-fallback-silencioso →
|
|
82
|
+
se anuncia la degradación). Mejoras del reviewer (de ARIS `auto-review-loop`):
|
|
83
|
+
- **Reviewer memory**: el reviewer arrastra sus sospechas entre rondas (un solo `threadId`),
|
|
84
|
+
no parte de cero cada iteración.
|
|
85
|
+
- **Debate protocol**: el executor puede rebatir; el reviewer falla el veredicto final —
|
|
86
|
+
*"it can drive, never acquit"* (el loop conduce, el jurado decide).
|
|
87
|
+
- **POSITIVE_THRESHOLD compuesto**: `score ≥ N` **AND** `verdict ∈ {ready, almost}` — un
|
|
88
|
+
score alto con verdict "not ready" NO detiene el loop.
|
|
89
|
+
|
|
90
|
+
## Anti-patrón: no envolver un skill auto-iterante en /loop · /cron · /schedule
|
|
91
|
+
|
|
92
|
+
Si un skill YA itera internamente (review→fix→re-review con memoria de reviewer en un
|
|
93
|
+
`threadId`), envolverlo en `/loop`, `/schedule` o `CronCreate` lo re-entra desde cero cada
|
|
94
|
+
tick → `threadId` nuevo, memoria del reviewer reseteada → dispara el veredicto por
|
|
95
|
+
wall-clock en vez de por cambio de artefacto: cero señal nueva, costo de tokens completo.
|
|
96
|
+
Aplica a `/swl:autoresearch`, `/swl:verificar --until-converge`, nemesis `--remediar`. Si
|
|
97
|
+
hay que agendar algo, agenda **la espera externa que lo precede** (ej. experimentos listos →
|
|
98
|
+
entonces corre el loop UNA vez), no el loop mismo.
|
|
99
|
+
|
|
100
|
+
## Token budgets, /goal y model routing
|
|
101
|
+
|
|
102
|
+
- **Token budget en workflows**: el global `budget` (o "use 10k tokens" en el prompt) pone
|
|
103
|
+
cap duro. Alinea con `scripts/lib/budget-enforcer.js` de swl-ses.
|
|
104
|
+
- **/goal + /loop**: para workflows repetibles (triage, research, verificación), `/goal` da
|
|
105
|
+
requisito de completitud y `/loop` la cadencia. NO sobre skills auto-iterantes (arriba).
|
|
106
|
+
- **Dynamic model routing**: un agente clasificador investiga la complejidad real del scope
|
|
107
|
+
y rutea a Sonnet vs Opus por tarea (más fino que el Model-Tier estático del frontmatter).
|
|
108
|
+
|
|
109
|
+
## Workflows como plantillas en skills
|
|
110
|
+
|
|
111
|
+
Un `.js` de workflow puede vivir en `recursos/` de un skill y referenciarse en su SKILL.md
|
|
112
|
+
como **plantilla** (no script verbatim) — Claude lo adapta al caso. Plantillas incluidas:
|
|
113
|
+
|
|
114
|
+
- `recursos/template-adversarial-verify.js` — fan-out de hallazgos + verificación adversarial
|
|
115
|
+
por hallazgo (filtra los reales).
|
|
116
|
+
- `recursos/template-triage.js` — clasifica ítems de un backlog, dedupe contra lo ya
|
|
117
|
+
trackeado, y rutea (fix automático vs escalar a humano) con patrón quarantine.
|
|
118
|
+
|
|
119
|
+
## Quarantine (triage de contenido no confiable)
|
|
120
|
+
|
|
121
|
+
En triage que lee contenido público/no confiable, los agentes lectores quedan **barred** de
|
|
122
|
+
acciones de alto privilegio; esas las ejecutan agentes distintos que actúan sobre la info ya
|
|
123
|
+
saneada. Coherente con `seguridad-agentes.md § Prompt injection` y el quarantine de SWL.
|
|
124
|
+
|
|
125
|
+
## Gotchas
|
|
126
|
+
|
|
127
|
+
- **Workflows gastan más tokens** — úsalos cuando la tarea de verdad necesita más cómputo
|
|
128
|
+
(adversarial, masivamente paralela), no por reflejo.
|
|
129
|
+
- **Subagentes heredan el contexto del padre** (CLAUDE.md + reglas globales). En proyectos
|
|
130
|
+
rule-heavy pueden saturar al arrancar (autocompact thrashing). Pasa scope acotado y evita
|
|
131
|
+
hacer que el subagente explore el codebase completo (ver `harness-claude-code`).
|
|
132
|
+
- **Barrier injustificado** mata wall-clock: si no hay dependencia cross-ítem entre stages,
|
|
133
|
+
usa `pipeline()`, no `parallel()` entre stages.
|
|
134
|
+
|
|
135
|
+
## Origen
|
|
136
|
+
|
|
137
|
+
Adoptado 2026-06-05 desde análisis de `temp/` (blog Anthropic dynamic-workflows +
|
|
138
|
+
ARIS `auto-review-loop`/`mcp-servers`). Ver APRENDIZAJES.md Tipo D 2026-06-05.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// PLANTILLA (no ejecutar verbatim) — Workflow tool de Claude Code.
|
|
2
|
+
// Patrón: fan-out de hallazgos -> verificación adversarial por hallazgo -> filtrar reales.
|
|
3
|
+
// Combate self-preferential bias y agentic laziness. Adapta DIMENSIONS, rubrics y schemas.
|
|
4
|
+
//
|
|
5
|
+
// Uso: el agente lee esta plantilla, la ajusta al caso concreto y la pasa al Workflow tool.
|
|
6
|
+
|
|
7
|
+
export const meta = {
|
|
8
|
+
name: 'adversarial-verify',
|
|
9
|
+
description: 'Revisa por dimensiones y verifica cada hallazgo adversarialmente antes de aceptarlo',
|
|
10
|
+
phases: [{ title: 'Review' }, { title: 'Verify' }],
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Dimensiones de revisión — una por lente independiente (no se contaminan entre sí).
|
|
14
|
+
const DIMENSIONS = [
|
|
15
|
+
{ key: 'correctness', prompt: 'Revisa SOLO correctness del scope. Devuelve hallazgos.' },
|
|
16
|
+
{ key: 'security', prompt: 'Revisa SOLO seguridad del scope. Devuelve hallazgos.' },
|
|
17
|
+
{ key: 'dry', prompt: 'Revisa SOLO duplicación/DRY del scope. Devuelve hallazgos.' },
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
const FINDINGS = {
|
|
21
|
+
type: 'object', additionalProperties: false,
|
|
22
|
+
properties: {
|
|
23
|
+
findings: {
|
|
24
|
+
type: 'array',
|
|
25
|
+
items: {
|
|
26
|
+
type: 'object', additionalProperties: false,
|
|
27
|
+
properties: {
|
|
28
|
+
title: { type: 'string' }, file: { type: 'string' }, line: { type: 'string' },
|
|
29
|
+
severity: { type: 'string', enum: ['critico', 'mayor', 'menor'] },
|
|
30
|
+
},
|
|
31
|
+
required: ['title', 'file', 'severity'],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
required: ['findings'],
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const VERDICT = {
|
|
39
|
+
type: 'object', additionalProperties: false,
|
|
40
|
+
properties: {
|
|
41
|
+
isReal: { type: 'boolean' },
|
|
42
|
+
reason: { type: 'string' },
|
|
43
|
+
},
|
|
44
|
+
required: ['isReal', 'reason'],
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// pipeline (sin barrier): cada dimensión verifica sus hallazgos en cuanto su review termina.
|
|
48
|
+
const results = await pipeline(
|
|
49
|
+
DIMENSIONS,
|
|
50
|
+
(d) => agent(d.prompt, { label: `review:${d.key}`, phase: 'Review', schema: FINDINGS }),
|
|
51
|
+
(review, d) =>
|
|
52
|
+
parallel(
|
|
53
|
+
(review?.findings || []).map((f) => () =>
|
|
54
|
+
// Verificador adversarial: prompt orientado a REFUTAR (default refutado si dudoso).
|
|
55
|
+
agent(
|
|
56
|
+
`Intenta REFUTAR este hallazgo contra el código real (cita archivo:linea). ` +
|
|
57
|
+
`Si no puedes probarlo, isReal=false: ${JSON.stringify(f)}`,
|
|
58
|
+
{ label: `verify:${f.file}`, phase: 'Verify', schema: VERDICT }
|
|
59
|
+
).then((v) => ({ ...f, verdict: v }))
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
const confirmados = results.flat().filter(Boolean).filter((f) => f.verdict?.isReal)
|
|
65
|
+
return { confirmados }
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// PLANTILLA (no ejecutar verbatim) — Workflow tool de Claude Code.
|
|
2
|
+
// Patrón: classify-and-act + quarantine. Clasifica ítems de un backlog, dedupe contra lo
|
|
3
|
+
// ya trackeado, y rutea: fix automático (bajo riesgo) vs escalar a humano (alto riesgo).
|
|
4
|
+
// Quarantine: el agente que LEE contenido no confiable NO ejecuta acciones de alto privilegio.
|
|
5
|
+
//
|
|
6
|
+
// `args` = lista de ítems del backlog (issues, reportes, etc.). Adapta schemas y umbrales.
|
|
7
|
+
|
|
8
|
+
export const meta = {
|
|
9
|
+
name: 'triage',
|
|
10
|
+
description: 'Clasifica un backlog, dedupe contra lo trackeado y rutea fix-vs-escalar (con quarantine)',
|
|
11
|
+
phases: [{ title: 'Clasificar' }, { title: 'Actuar' }],
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const items = Array.isArray(args) ? args : []
|
|
15
|
+
|
|
16
|
+
const CLASIFICACION = {
|
|
17
|
+
type: 'object', additionalProperties: false,
|
|
18
|
+
properties: {
|
|
19
|
+
categoria: { type: 'string', enum: ['bug', 'feature', 'duplicado', 'ruido'] },
|
|
20
|
+
yaTrackeado: { type: 'boolean' },
|
|
21
|
+
riesgo: { type: 'string', enum: ['bajo', 'alto'] },
|
|
22
|
+
resumen: { type: 'string' },
|
|
23
|
+
},
|
|
24
|
+
required: ['categoria', 'yaTrackeado', 'riesgo', 'resumen'],
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const ACCION = {
|
|
28
|
+
type: 'object', additionalProperties: false,
|
|
29
|
+
properties: {
|
|
30
|
+
accion: { type: 'string', enum: ['fix-aplicado', 'escalado-humano', 'descartado'] },
|
|
31
|
+
detalle: { type: 'string' },
|
|
32
|
+
},
|
|
33
|
+
required: ['accion', 'detalle'],
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Stage 1 (quarantine): clasificador LEE el ítem (posible contenido no confiable) pero NO
|
|
37
|
+
// tiene permiso de actuar — solo emite estructura. Stage 2 actúa sobre data ya saneada.
|
|
38
|
+
const triaged = await pipeline(
|
|
39
|
+
items,
|
|
40
|
+
(item) =>
|
|
41
|
+
agent(
|
|
42
|
+
`Clasifica este ítem del backlog. NO ejecutes ninguna acción: solo describe. ` +
|
|
43
|
+
`Marca yaTrackeado=true si ya existe ticket. Ítem: ${JSON.stringify(item)}`,
|
|
44
|
+
{ label: 'clasificar', phase: 'Clasificar', schema: CLASIFICACION }
|
|
45
|
+
),
|
|
46
|
+
(clf, item) => {
|
|
47
|
+
if (!clf || clf.yaTrackeado || clf.categoria === 'ruido' || clf.categoria === 'duplicado') {
|
|
48
|
+
return { accion: 'descartado', detalle: clf?.resumen || 'sin señal' }
|
|
49
|
+
}
|
|
50
|
+
if (clf.riesgo === 'alto') {
|
|
51
|
+
// Alto riesgo: escalar a humano, no auto-actuar.
|
|
52
|
+
return agent(
|
|
53
|
+
`Redacta un escalamiento conciso para humano sobre: ${clf.resumen}`,
|
|
54
|
+
{ label: 'escalar', phase: 'Actuar', schema: ACCION }
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
// Bajo riesgo: el agente que ACTÚA es distinto del que leyó contenido no confiable.
|
|
58
|
+
return agent(
|
|
59
|
+
`Aplica el fix de bajo riesgo para: ${clf.resumen}. Devuelve qué hiciste.`,
|
|
60
|
+
{ label: 'fix', phase: 'Actuar', schema: ACCION }
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return triaged.filter(Boolean)
|