@saulwade/swl-ses 2.2.4 → 2.3.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 +2 -2
- package/README.md +3 -3
- package/comandos/swl/aprobar-plan.md +146 -141
- package/comandos/swl/release.md +30 -8
- package/comandos/swl/status.md +343 -333
- package/habilidades/contenedores-docker/SKILL.md +3 -1
- package/habilidades/ejecutar-fase/SKILL.md +564 -541
- package/habilidades/extractor-de-aprendizajes/SKILL.md +3 -3
- package/habilidades/planear-fase/SKILL.md +15 -1
- package/habilidades/proceso-debate-adversarial/SKILL.md +191 -164
- package/habilidades/tdd-workflow/SKILL.md +33 -1
- package/hooks/captura-acciones-post.js +151 -0
- package/hooks/captura-acciones-session.js +155 -0
- package/hooks/lib/briefing.js +512 -474
- package/hooks/lib/captura-acciones.js +392 -0
- package/llms.txt +29 -29
- package/manifiestos/canonical-hashes.json +337 -10
- package/manifiestos/hooks-config.json +18 -0
- package/manifiestos/modulos.json +4 -0
- package/manifiestos/skills-lock.json +22 -22
- package/package.json +2 -2
- package/plugin.json +2 -2
- package/reglas/memoria-consolidada.md +41 -0
- package/reglas/seguridad-agentes.md +66 -0
- package/schemas/instinct.schema.json +161 -152
- package/scripts/bootstrap-instintos.js +25 -7
- package/scripts/lib/gitignore-manifest.js +44 -11
- package/scripts/lib/skill-metrics.js +41 -1
- package/scripts/verificar-trazabilidad.js +329 -298
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: extractor-de-aprendizajes
|
|
3
3
|
description: Convertir errores y patrones descubiertos durante la implementación en nuevas habilidades o reglas. Ciclo de mejora continua del sistema SWL.
|
|
4
|
-
version: "1.0.
|
|
4
|
+
version: "1.0.7"
|
|
5
5
|
herramientasPermitidas: [Read]
|
|
6
6
|
exclusiones:
|
|
7
7
|
- "No cargar para actualizar el perfil del usuario — las correcciones explícitas del usuario van a `instintos/perfil-usuario.yaml` vía `perfilador-usuario-swl`, no a APRENDIZAJES.md."
|
|
@@ -291,13 +291,13 @@ Durante `/swl:aprender`, aplicar estas reglas:
|
|
|
291
291
|
- **`rating: HIGH` asignado sin verificar criterio de irreversibilidad**: el agente promueve a CLAUDE.md un aprendizaje "MEDIUM" por el entusiasmo del momento. Causa: no se aplicó el criterio de "decisión irreversible o bug crítico". Solución: antes de asignar HIGH, verificar: ¿cambiar esto en el futuro requeriría refactorizar múltiples archivos o migrar datos? Si no, mantener MEDIUM.
|
|
292
292
|
- **Regla incompleta sobre registro de hooks**: una entrada en memoria dice "registrar en modulos.json" pero omite `hooks-config.json`, y en la siguiente iteración se repite el fallo en CI porque ambos manifiestos son obligatorios. Causa: la regla de la lección anterior no cubrió todos los manifiestos afectados. Solución: al documentar una regla sobre registro en manifiestos, listar EXPLÍCITAMENTE cada manifiesto con su responsabilidad distinta (`modulos.json` = qué copiar; `hooks-config.json` = cómo registrar evento). Evidencia: tres incidentes históricos del mismo patrón incompleto (v5.7.1, v5.7.2/3, v5.11.0).
|
|
293
293
|
- **Inventario estimado a mano en vez de regenerar**: el agente cuenta "28 hooks" visualmente y propaga la cifra a 5 archivos (CLAUDE/README/package/plugin/SALUD); al regenerar con `scripts/generar-inventario.js` el número real es 30. Causa: confiar en la observación directa en vez de la fuente de verdad determinista. Solución: antes de modificar cualquier contador en documentación oficial, ejecutar el script de inventario y usar su salida como ground truth.
|
|
294
|
-
- **Sub-agentes (Explore + revisores + agentes de documentación) reportan afirmaciones factuales no verificadas** [CONFIRMADO
|
|
294
|
+
- **Sub-agentes (Explore + revisores + agentes de documentación) reportan afirmaciones factuales no verificadas** [CONFIRMADO x7]: aplica a `Explore`, `revisor-codigo-swl`, `revisor-seguridad-swl`, `investigador-swl`, `planificador-swl`, `claude-code-guide`, y en general a cualquier sub-agente que reporte hallazgos sobre código o capacidades de herramientas. **Cuatro modos de falla observados**:
|
|
295
295
|
|
|
296
296
|
**Modo A — papers académicos** (sesión 2026-04-25): sub-agente Explore propuso 50h+ para implementar SPRT + Lyapunov + compositionality theorem del paper Bhardwaj 2026; costo real validado fue ~5h (solo Drift Score + Recovery Catalog). Causa: el Explore evalúa portabilidad técnica sin aplicar filtro de "datos disponibles" ni "infraestructura zero-deps". Evidencia: 3 papers analizados (evolver, Bhardwaj 2026, Zhang et al. 2026) con descarte sistemático ~70-90% del contenido propuesto.
|
|
297
297
|
|
|
298
298
|
**Modo B — repositorios externos** (sesión 2026-05-02 cosecha-temp/ EMAIA): sub-agente Explore analizando `temp/docetl-main` (UC Berkeley, arXiv:2410.12189) afirmó dos cosas FALSAS verificables contra el código del proyecto destino: (1) "EMAIA ya usa LiteLLM" — verificación contra `pyproject.toml` + `grep -r litellm core/` mostró que EMAIA tiene clientes propios (`OllamaClient`/`NIMClient`/`vLLMClient`) y NO usa LiteLLM en absoluto; (2) recomendó portar Gleaning como patrón nuevo, cuando `core/learning/evaluator_optimizer.py` y `core/llm/quality_judge.py` YA implementan Anthropic Evaluator-Optimizer + LLM-as-judge. El fix real era WIRING (~30 LOC), no porting. Costo "MEDIO" del agente quedó como BAJO real. Causa: el Explore evalúa el repo externo en aislamiento, sin leer paths del proyecto destino para verificar qué mecanismos ya existen.
|
|
299
299
|
|
|
300
|
-
**Modo C — revisores reportan ubicaciones y severidades incorrectas en código del proyecto** (sesión 2026-05-12 SIGM): `revisor-codigo-swl` ejecutado en `/swl:verificar --until-converge` reportó dos errores factuales: (1) **ubicación incorrecta**: "deferred import en `caja/router.py:545`" cuando la línea real estaba en `caja/service.py:545` — el archivo `caja/router.py` solo tiene 157 líneas (`wc -l caja/router.py = 157`); (2) **severidad subestimada**: reportó "mismatch `CobroConsolidadoResponse` en pantalla de éxito" como MAYOR, mi auditoría manual descubrió que `CobroConsolidadoRequest` también estaba completamente desalineado (más grave, debería ser CRÍTICO bloqueante) — el revisor solo miró el Response. Causa: el revisor analiza código sin re-verificar el path/línea/extensión reportada cuando produce el reporte; sub-agentes basados en LLM hallucinan números de línea con frecuencia ≥15% en archivos grandes.
|
|
300
|
+
**Modo C — revisores reportan ubicaciones y severidades incorrectas en código del proyecto** (sesión 2026-05-12 SIGM): `revisor-codigo-swl` ejecutado en `/swl:verificar --until-converge` reportó dos errores factuales: (1) **ubicación incorrecta**: "deferred import en `caja/router.py:545`" cuando la línea real estaba en `caja/service.py:545` — el archivo `caja/router.py` solo tiene 157 líneas (`wc -l caja/router.py = 157`); (2) **severidad subestimada**: reportó "mismatch `CobroConsolidadoResponse` en pantalla de éxito" como MAYOR, mi auditoría manual descubrió que `CobroConsolidadoRequest` también estaba completamente desalineado (más grave, debería ser CRÍTICO bloqueante) — el revisor solo miró el Response. Causa: el revisor analiza código sin re-verificar el path/línea/extensión reportada cuando produce el reporte; sub-agentes basados en LLM hallucinan números de línea con frecuencia ≥15% en archivos grandes. **Instancia adicional (sesión 2026-06-15, norma-studio, x7)**: `revisor-codigo-swl` afirmó "Python 3.14 es pre-release, falta `allow-prereleases` en la config" — FALSO: el entorno corría 3.14.4 estable (verificable con `python --version`). El revisor infirió "pre-release" del número de versión alto sin consultar el runtime real. Subcaso: **claims sobre la versión del entorno/runtime** (Python, Node, una dependencia) se verifican ejecutando el reporte del propio runtime (`python --version`, `node -v`, `pip show X`), nunca infiriendo del número.
|
|
301
301
|
|
|
302
302
|
**Modo D — agentes de documentación responden por inferencia del contexto local sin consultar la fuente oficial** (sesión 2026-06-11 swl-ses Fase 09; precedente 2026-05-15 con runtimes Cursor/Codex): `claude-code-guide` emitió veredicto categórico "Claude Code NO soporta carga condicional por `paths:` en rules; el frontmatter sería inerte" basándose en que la config local del usuario no usaba ese campo (declinó el WebFetch a docs). Un WebFetch directo a `code.claude.com/docs/en/memory` demostró lo contrario: `paths:` es feature nativa documentada con sintaxis exacta, brace expansion y soporte user-level. Aceptar el veredicto habría descartado un slice completo de la fase. Causa: "ausencia de evidencia en el contexto local" tratada como "evidencia de ausencia en el producto" — mismo patrón del precedente 2026-05-15 ("Cursor no soporta hooks" — falso). Las features recientes nunca aparecen en configs existentes precisamente por ser recientes.
|
|
303
303
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: planear-fase
|
|
3
3
|
description: Crea el PLAN.md ejecutable para una fase de desarrollo. Descompone la fase en tareas atómicas con dependencias explícitas, las agrupa en oleadas de ejecución paralela cuando es posible, y aplica verificación goal-backward para garantizar que el plan completo satisface los criterios de éxito definidos en CONTEXTO.md.
|
|
4
|
-
version: "1.3.
|
|
4
|
+
version: "1.3.2"
|
|
5
5
|
herramientasPermitidas: [Read, Write, Edit, Bash, Glob, Grep]
|
|
6
6
|
exclusiones:
|
|
7
7
|
- "No cargar si no existe CONTEXTO.md para la fase — ejecutar `discutir-fase` primero."
|
|
@@ -144,10 +144,24 @@ goal-backward deja de ser pregunta abierta y se vuelve **matriz verificable**:
|
|
|
144
144
|
planes con REQ huérfanos.
|
|
145
145
|
4. Tareas sin REQ son válidas (infraestructura, refactor habilitante) pero la matriz
|
|
146
146
|
las hace visibles.
|
|
147
|
+
5. **REQ verificable por inspección (config, CI, lint, docs) DEBE llevar la marca
|
|
148
|
+
literal `(verificación: inspección)` en su criterio del CONTEXTO.** Sin esa
|
|
149
|
+
marca, `verificar-trazabilidad.js` aplica el método por defecto (`test`) y exige
|
|
150
|
+
un test con marker `verifica: REQ-NN`; un REQ satisfecho por ruff/CI/prosa pero
|
|
151
|
+
sin esa anotación se reporta como **huérfano (REQ sin test)** aunque esté
|
|
152
|
+
genuinamente cubierto. Si detectas un REQ de este tipo sin la marca, agrégala al
|
|
153
|
+
CONTEXTO (`0N-CONTEXTO.md`) antes de construir la matriz — no inventes un test
|
|
154
|
+
artificial para satisfacer al verificador.
|
|
147
155
|
|
|
148
156
|
En CONTEXTOs legacy sin REQ-IDs (fases 01-09): aplicar la pregunta abierta clásica
|
|
149
157
|
("¿el criterio de éxito queda satisfecho?") — cláusula de gracia, sin matriz.
|
|
150
158
|
|
|
159
|
+
**Formato de header de tarea — dos puntos, no em-dash.** Cada tarea se escribe
|
|
160
|
+
`### T-NN:` (2-4 `#`, dos puntos tras el ID). `verificar-trazabilidad.js` parsea la
|
|
161
|
+
matriz con `/^#{2,4}\s*T-\d{1,3}\s*:/`; un header `#### T-01 — Foo` (em-dash) deja
|
|
162
|
+
la tarea **invisible** y sus REQ huérfanos sin causa aparente. El verificador emite
|
|
163
|
+
un WARN explicando esto, pero el plan debe nacer con el separador correcto.
|
|
164
|
+
|
|
151
165
|
---
|
|
152
166
|
|
|
153
167
|
## Estructura del PLAN.md
|
|
@@ -1,164 +1,191 @@
|
|
|
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.
|
|
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:status 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
|
-
##
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
##
|
|
116
|
-
|
|
117
|
-
|
|
|
118
|
-
|
|
119
|
-
|
|
|
120
|
-
|
|
|
121
|
-
|
|
|
122
|
-
|
|
|
123
|
-
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
-
|
|
158
|
-
|
|
159
|
-
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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.1.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:status 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
|
+
## Voto incompleto ≠ refutado + degradación del fan-out ante rate limit
|
|
89
|
+
|
|
90
|
+
Cuando el fan-out de personas/jueces se ejecuta en paralelo y la API responde
|
|
91
|
+
con rate limit (429) o un juez no completa su veredicto (timeout, error
|
|
92
|
+
terminal), el hallazgo asociado queda **"sin verificar"**, NUNCA "refutado" ni
|
|
93
|
+
"descartado". Conflar "no se pudo verificar" con "verificado falso" descarta
|
|
94
|
+
silenciosamente hallazgos reales — el modo de falla más caro de un panel
|
|
95
|
+
adversarial.
|
|
96
|
+
|
|
97
|
+
Reglas:
|
|
98
|
+
|
|
99
|
+
- Un veredicto que no se emite NO cuenta como voto en contra. El conteo de
|
|
100
|
+
refutación solo suma jueces que **emitieron** un veredicto de refutación
|
|
101
|
+
explícito. `votos_refutan / votos_emitidos`, nunca `/ jueces_lanzados`.
|
|
102
|
+
- Un hallazgo cuyos votos no se completaron se marca `estado: sin-verificar`
|
|
103
|
+
(distinto de `confirmado` y de `refutado`) y se reporta como tal — el usuario
|
|
104
|
+
decide si re-ejecutar ese subconjunto.
|
|
105
|
+
- Ante rate limit en el fan-out: **escalonar** las invocaciones (lanzar por
|
|
106
|
+
lotes con espera entre lotes) o **reducir** el número de jueces/personas a un
|
|
107
|
+
quórum mínimo (≥3) antes de abortar. Degradar el panel con aviso visible es
|
|
108
|
+
preferible a marcar todo lo no votado como refutado (regla
|
|
109
|
+
`seguridad-agentes.md § Anti-fallback silencioso`).
|
|
110
|
+
|
|
111
|
+
Nota de alcance: esto aplica al panel de este skill y a `/swl:predecir`. El
|
|
112
|
+
patrón adversarial-verify del harness de Workflow (built-in de Claude Code) NO
|
|
113
|
+
es editable desde swl-ses; su semántica de votos vive fuera de este repo.
|
|
114
|
+
|
|
115
|
+
## Criterios de juez por dominio
|
|
116
|
+
|
|
117
|
+
| Dominio | Criterios de evaluación |
|
|
118
|
+
|---------|------------------------|
|
|
119
|
+
| Arquitectura de software | escalabilidad, mantenibilidad, rendimiento, seguridad, simplicidad |
|
|
120
|
+
| Estrategia de producto | encaje de mercado, factibilidad, diferenciación, riesgo, tiempos |
|
|
121
|
+
| Decisión de negocio | ROI, riesgo, alineación, recursos requeridos, reversibilidad |
|
|
122
|
+
| Enfoque de seguridad | cobertura, tasa de falsos positivos, practicidad, cumplimiento |
|
|
123
|
+
| Hipótesis de investigación | testabilidad, novedad, soporte de evidencia, poder explicativo |
|
|
124
|
+
|
|
125
|
+
El juez evalúa CADA candidato contra TODOS los criterios del dominio y
|
|
126
|
+
produce ranking con justificación — nunca un score suelto sin comparación.
|
|
127
|
+
|
|
128
|
+
## Personas para análisis predictivo
|
|
129
|
+
|
|
130
|
+
Cuando el objetivo es **predecir problemas de un cambio propuesto** (no
|
|
131
|
+
elegir entre alternativas), usar el modo personas: 5 expertos analizan EN FRÍO
|
|
132
|
+
el mismo cambio y un sintetizador deduplica y rankea. Definiciones completas,
|
|
133
|
+
preguntas guía y red flags por persona en
|
|
134
|
+
[recursos/personas.md](recursos/personas.md). Set default: Arquitecto de
|
|
135
|
+
Software, Analista de Seguridad, Ingeniero de Rendimiento, Ingeniero de
|
|
136
|
+
Confiabilidad, Abogado del Diablo. Set adversarial (`--adversarial`): El
|
|
137
|
+
Rompedor, El Tramposo, El Escalador, El Novato, El Insider Malicioso.
|
|
138
|
+
|
|
139
|
+
Ranking de hallazgos del sintetizador:
|
|
140
|
+
`severidad × confianza promedio × número de personas que coinciden`.
|
|
141
|
+
|
|
142
|
+
## Integración con el ecosistema SWL
|
|
143
|
+
|
|
144
|
+
| Componente | Uso del protocolo |
|
|
145
|
+
|-----------|-------------------|
|
|
146
|
+
| `arquitecto-swl` | Debate para la sección "Alternativas consideradas" de un ADR |
|
|
147
|
+
| `/swl:predecir` | Modo personas pre-implementación |
|
|
148
|
+
| `/swl:nemesis` | El evaluator puede pedir un debate cuando dos remediaciones compiten |
|
|
149
|
+
| `hooks/lib/loop-telemetry.js` | Registro de rondas + handoff para encadenar |
|
|
150
|
+
| `/swl:status metricas` | Lectura de trayectorias de debates en `.planning/loops/` |
|
|
151
|
+
|
|
152
|
+
## Cuándo NO cargar
|
|
153
|
+
|
|
154
|
+
- La decisión tiene métrica objetiva (latencia, cobertura, bundle size) — usar
|
|
155
|
+
el loop autoresearch con Verify numérico; un debate es más caro y menos
|
|
156
|
+
preciso que medir.
|
|
157
|
+
- El usuario ya tomó la decisión y está documentada en ADR/vault — aplicar
|
|
158
|
+
`consultar-vault-primero`, no reabrir con un debate.
|
|
159
|
+
- Hay restricción dura que elimina las alternativas (compliance, presupuesto,
|
|
160
|
+
stack fijo) — verificar restricciones ANTES de armar el debate.
|
|
161
|
+
|
|
162
|
+
## Gotchas / Errores comunes no obvios
|
|
163
|
+
|
|
164
|
+
- **Jueces con contexto contaminado**: invocar a los jueces en la misma
|
|
165
|
+
conversación donde se generaron los candidatos les revela la autoría por el
|
|
166
|
+
historial. Causa: usar el contexto principal en vez del Agent tool con
|
|
167
|
+
prompt acotado. Solución: cada juez es una invocación Agent independiente
|
|
168
|
+
cuyo prompt contiene SOLO tarea + candidatos etiquetados.
|
|
169
|
+
- **Crítico que "equilibra"**: el crítico señala 3 debilidades pero cierra con
|
|
170
|
+
"en general es un buen enfoque" — eso re-introduce sycophancy y debilita al
|
|
171
|
+
Autor-B. Solución: el prompt del crítico prohíbe explícitamente elogios y
|
|
172
|
+
exige proponer qué haría un candidato superior.
|
|
173
|
+
- **Sintetizador que promedia en vez de fusionar**: produce un candidato
|
|
174
|
+
"tibio" que toma la mitad de cada uno y pierde la coherencia interna de
|
|
175
|
+
ambos. Solución: el híbrido debe tener una tesis propia — tomar la
|
|
176
|
+
arquitectura dominante de uno e injertar mecanismos puntuales del otro.
|
|
177
|
+
- **Convergencia falsa por candidatos idénticos**: si Autor-B produce
|
|
178
|
+
esencialmente el mismo candidato que A, el panel "converge" en ronda 2 sin
|
|
179
|
+
exploración real. Solución: el prompt de Autor-B exige divergencia
|
|
180
|
+
estructural, no cosmética; si la crítica fue débil, regenerar la crítica.
|
|
181
|
+
|
|
182
|
+
## Anti-patrones
|
|
183
|
+
|
|
184
|
+
- **Debate de 1 ronda presentado como consenso** — sin convergencia ×3 no hay
|
|
185
|
+
veredicto, hay una primera impresión cara.
|
|
186
|
+
- **Saltarse la aleatorización de etiquetas** "porque los jueces son
|
|
187
|
+
imparciales" — el position bias es estadístico, no intencional.
|
|
188
|
+
- **Usar el debate para decisiones ya cerradas** — teatro de proceso que
|
|
189
|
+
quema tokens para justificar lo decidido.
|
|
190
|
+
- **Panel de 1 juez** — un juez único reintroduce el sesgo individual que el
|
|
191
|
+
panel existe para diluir; mínimo 3, número impar.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tdd-workflow
|
|
3
3
|
description: Flujo completo de Test-Driven Development. Ciclo RED (el test falla) → GREEN (implementación mínima) → REFACTOR (limpieza). Incluye cobertura mínima obligatoria, tests de frontera, factories, fixtures y estrategias para diferentes tipos de código (APIs, services, componentes Angular).
|
|
4
|
-
version: "1.2.
|
|
4
|
+
version: "1.2.2"
|
|
5
5
|
herramientasPermitidas: [Read, Bash]
|
|
6
6
|
evolvable: true # default para skill estandar
|
|
7
7
|
exclusiones:
|
|
@@ -536,6 +536,38 @@ archivo creaba el flag, sin ejecutar ninguna assertion. Fix: env var
|
|
|
536
536
|
|
|
537
537
|
---
|
|
538
538
|
|
|
539
|
+
## Tests E2E de UI con Playwright — gotchas de actionability
|
|
540
|
+
|
|
541
|
+
Dos fallos recurrentes al automatizar UIs con Playwright donde el test "debería
|
|
542
|
+
pasar" pero hace timeout o no observa el efecto esperado:
|
|
543
|
+
|
|
544
|
+
- **`fill()` hace timeout en un input que el motor considera "not editable" aunque
|
|
545
|
+
sea interactivo para el usuario** (widgets custom: input dentro de un overlay/
|
|
546
|
+
option marcado `[disabled]`, combos con buscador, etc.). Playwright aplica
|
|
547
|
+
checks de actionability (visible, enabled, editable) y se niega a escribir.
|
|
548
|
+
Fix: setear el valor con el **setter nativo** y despachar el evento `input` vía
|
|
549
|
+
`evaluate`, en vez de `fill()`:
|
|
550
|
+
|
|
551
|
+
```js
|
|
552
|
+
await page.locator('#busqueda').evaluate((el, v) => {
|
|
553
|
+
const setter = Object.getOwnPropertyDescriptor(
|
|
554
|
+
window.HTMLInputElement.prototype, 'value').set;
|
|
555
|
+
setter.call(el, v);
|
|
556
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
557
|
+
}, 'texto');
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
- **Un click en «Guardar»/«Enviar» que no dispara su request** suele tener un
|
|
561
|
+
**paso de confirmación intermedio** (diálogo «¿Confirmar?», modal con timeout),
|
|
562
|
+
no un bug del submit. Antes de concluir "no persiste": verificar en la pestaña
|
|
563
|
+
de red si hubo (o no) la petición, y buscar un diálogo de confirmación que el
|
|
564
|
+
test deba cerrar dentro de su timeout. Un submit que pasa por confirmación
|
|
565
|
+
necesita el click de confirmación en la aserción, no solo el del botón inicial.
|
|
566
|
+
|
|
567
|
+
Regla general: ante un test E2E que falla, distinguir **bug del producto** de
|
|
568
|
+
**gap del test** verificando el estado real (red, DOM, BD) antes de declarar el
|
|
569
|
+
veredicto — un timeout de actionability casi nunca es un bug de la app.
|
|
570
|
+
|
|
539
571
|
## Tests E2E de CLIs interactivos sin PTY real
|
|
540
572
|
|
|
541
573
|
### El problema
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook: captura-acciones-post.js
|
|
6
|
+
* Tipo: PostToolUse (todas las herramientas)
|
|
7
|
+
*
|
|
8
|
+
* Pobla el estado que el hook SessionStart (`captura-acciones-session.js`)
|
|
9
|
+
* consumirá para detectar señales de acción del usuario (Fase 17):
|
|
10
|
+
* - `git commit` del agente → registra el SHA en agent-commits.jsonl
|
|
11
|
+
* (base para detectar revert/reset; sin co-author, regla git-coauthor.md).
|
|
12
|
+
* - `Write`/`Edit`/`MultiEdit` → registra el hash del archivo en
|
|
13
|
+
* agent-writes-index.json (base para detectar edición neta del usuario).
|
|
14
|
+
* - `Read` de un archivo que el agente escribió cuyo contenido en disco
|
|
15
|
+
* difiere → encola `edicion-manual` en caliente (REQ-17-03).
|
|
16
|
+
*
|
|
17
|
+
* Zero-deps salvo la lib `captura-acciones`. Opt-out unificado
|
|
18
|
+
* SWL_CAPTURAR_FEEDBACK=0. Exit 0 SIEMPRE; nunca bloquea ni muestra al modelo.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const { execFileSync } = require('child_process');
|
|
24
|
+
|
|
25
|
+
// Require con fallback dual: el instalador aplana `hooks/lib/X.js → <hooks>/X.js`.
|
|
26
|
+
// (Idioma compartido con session-briefing.js/propose-step.js; no se extrae a lib
|
|
27
|
+
// porque cargar esa lib requeriría a su vez el mismo fallback — paradoja de
|
|
28
|
+
// bootstrap del aplanado.)
|
|
29
|
+
function requireDual(nombre) {
|
|
30
|
+
try {
|
|
31
|
+
return require(`./lib/${nombre}`);
|
|
32
|
+
} catch (e1) {
|
|
33
|
+
if (e1 && e1.code !== 'MODULE_NOT_FOUND') throw e1;
|
|
34
|
+
return require(`./${nombre}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let lib;
|
|
39
|
+
try {
|
|
40
|
+
lib = requireDual('captura-acciones');
|
|
41
|
+
} catch (_) {
|
|
42
|
+
lib = null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Preview crudo amplio: se pasa a encolarAccion para que la sanitización corra
|
|
46
|
+
// sobre el texto íntegro (un bloque PEM cabe) ANTES del truncado final a
|
|
47
|
+
// MAX_EVIDENCIA_LEN. Pre-truncar aquí derrotaría el patrón PEM (sec BAJA-2).
|
|
48
|
+
const PREVIEW_RAW = 4096;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Convierte una ruta (abs o rel) a relativa POSIX respecto a baseDir; null si
|
|
52
|
+
* escapa. Guard léxico (path.relative): rechaza `..` y rutas absolutas. NO
|
|
53
|
+
* resuelve symlinks — la defensa real es la sanitización + el confinamiento por
|
|
54
|
+
* índice + que el contenido se trata como dato (nunca se ejecuta); aceptable
|
|
55
|
+
* para tooling de dev donde el usuario es dueño del repo (sec BAJA-3).
|
|
56
|
+
*/
|
|
57
|
+
function relPosix(baseDir, filePath) {
|
|
58
|
+
if (!filePath) return null;
|
|
59
|
+
const abs = path.isAbsolute(filePath) ? filePath : path.resolve(baseDir, filePath);
|
|
60
|
+
const rel = path.relative(baseDir, abs);
|
|
61
|
+
if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null;
|
|
62
|
+
return rel.split(path.sep).join('/');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function _leer(baseDir, rel) {
|
|
66
|
+
try {
|
|
67
|
+
return fs.readFileSync(path.resolve(baseDir, rel), 'utf8');
|
|
68
|
+
} catch (_) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** (a) git commit del agente → registrar SHA. */
|
|
74
|
+
function _manejarCommit(input, sessionId, baseDir) {
|
|
75
|
+
if (typeof input.command !== 'string' || !/\bgit\s+commit\b/.test(input.command)) return;
|
|
76
|
+
let sha = '';
|
|
77
|
+
try {
|
|
78
|
+
sha = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: baseDir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
79
|
+
} catch (_) {
|
|
80
|
+
sha = '';
|
|
81
|
+
}
|
|
82
|
+
if (sha) lib.registrarCommitAgente(baseDir, { sessionId, sha });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** (b) Write/Edit/MultiEdit → registrar hash del archivo tras la escritura. */
|
|
86
|
+
function _manejarEscritura(input, sessionId, baseDir) {
|
|
87
|
+
const rel = relPosix(baseDir, input.file_path);
|
|
88
|
+
if (!rel) return;
|
|
89
|
+
const contenido = _leer(baseDir, rel);
|
|
90
|
+
if (contenido === null) return;
|
|
91
|
+
lib.registrarEscrituraAgente(baseDir, rel, contenido, { sessionId });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** (c) Read de archivo escrito por el agente cuyo contenido difiere → edición manual. */
|
|
95
|
+
function _manejarLectura(input, sessionId, baseDir) {
|
|
96
|
+
const rel = relPosix(baseDir, input.file_path);
|
|
97
|
+
if (!rel) return;
|
|
98
|
+
const indice = lib.leerIndiceWrites(baseDir);
|
|
99
|
+
if (!indice[rel]) return;
|
|
100
|
+
const contenido = _leer(baseDir, rel);
|
|
101
|
+
if (contenido === null) return;
|
|
102
|
+
if (lib.detectarEdicionManual(rel, contenido, indice)) {
|
|
103
|
+
lib.encolarAccion(baseDir, {
|
|
104
|
+
accion: 'edicion-manual',
|
|
105
|
+
archivo: rel,
|
|
106
|
+
evidencia: contenido.slice(0, PREVIEW_RAW),
|
|
107
|
+
sessionId,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Procesa un evento PostToolUse. Orquesta los 3 handlers según la herramienta.
|
|
114
|
+
* @param {object} data payload del hook (tool_name, tool_input, session_id...)
|
|
115
|
+
* @param {string} baseDir raíz del proyecto.
|
|
116
|
+
*/
|
|
117
|
+
function procesarTurno(data, baseDir) {
|
|
118
|
+
if (process.env.SWL_CAPTURAR_FEEDBACK === '0') return;
|
|
119
|
+
if (!lib || !data) return;
|
|
120
|
+
const toolName = String(data.tool_name || data.tool?.name || '');
|
|
121
|
+
if (!toolName) return;
|
|
122
|
+
const sessionId = String(data.session_id || 'default');
|
|
123
|
+
const input = data.tool_input || data.tool?.input || {};
|
|
124
|
+
try {
|
|
125
|
+
if (toolName === 'Bash') {
|
|
126
|
+
_manejarCommit(input, sessionId, baseDir);
|
|
127
|
+
} else if (toolName === 'Write' || toolName === 'Edit' || toolName === 'MultiEdit') {
|
|
128
|
+
if (input.file_path) _manejarEscritura(input, sessionId, baseDir);
|
|
129
|
+
} else if (toolName === 'Read') {
|
|
130
|
+
if (input.file_path) _manejarLectura(input, sessionId, baseDir);
|
|
131
|
+
}
|
|
132
|
+
} catch (_) {
|
|
133
|
+
/* nunca bloquear */
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Listeners de stdin SOLO en modo CLI (evita colgar la suite de tests).
|
|
138
|
+
if (require.main === module) {
|
|
139
|
+
let inputRaw = '';
|
|
140
|
+
process.stdin.on('data', (c) => { inputRaw += c; });
|
|
141
|
+
process.stdin.on('end', () => {
|
|
142
|
+
try {
|
|
143
|
+
const data = JSON.parse(inputRaw);
|
|
144
|
+
procesarTurno(data, process.cwd());
|
|
145
|
+
} catch (_) {
|
|
146
|
+
/* silencioso */
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
} else {
|
|
150
|
+
module.exports = { procesarTurno, relPosix };
|
|
151
|
+
}
|