@saulwade/swl-ses 1.0.1 → 1.1.2

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.
Files changed (113) hide show
  1. package/CLAUDE.md +8 -5
  2. package/README.md +3 -3
  3. package/agentes/accesibilidad-wcag-swl.md +5 -7
  4. package/agentes/arquitecto-swl.md +5 -3
  5. package/agentes/auto-evolucion-swl.md +42 -12
  6. package/agentes/backend-api-swl.md +5 -3
  7. package/agentes/backend-csharp-swl.md +5 -3
  8. package/agentes/backend-go-swl.md +5 -3
  9. package/agentes/backend-java-swl.md +5 -3
  10. package/agentes/backend-node-swl.md +5 -3
  11. package/agentes/backend-python-swl.md +5 -3
  12. package/agentes/backend-rust-swl.md +5 -3
  13. package/agentes/backend-workers-swl.md +5 -3
  14. package/agentes/cloud-infra-swl.md +5 -6
  15. package/agentes/consolidador-swl.md +5 -3
  16. package/agentes/datos-swl.md +5 -7
  17. package/agentes/depurador-swl.md +6 -3
  18. package/agentes/devops-ci-swl.md +5 -3
  19. package/agentes/disenador-ui-swl.md +5 -7
  20. package/agentes/documentador-swl.md +5 -3
  21. package/agentes/frontend-angular-swl.md +5 -11
  22. package/agentes/frontend-css-swl.md +5 -9
  23. package/agentes/frontend-react-swl.md +5 -9
  24. package/agentes/frontend-swl.md +5 -9
  25. package/agentes/frontend-tailwind-swl.md +5 -9
  26. package/agentes/implementador-swl.md +6 -3
  27. package/agentes/investigador-swl.md +5 -3
  28. package/agentes/investigador-ux-swl.md +5 -9
  29. package/agentes/llm-apps-swl.md +5 -3
  30. package/agentes/migrador-swl.md +6 -3
  31. package/agentes/mobile-android-swl.md +5 -3
  32. package/agentes/mobile-cross-swl.md +5 -3
  33. package/agentes/mobile-ios-swl.md +5 -3
  34. package/agentes/mobile-testing-swl.md +5 -3
  35. package/agentes/notificador-swl.md +5 -3
  36. package/agentes/observabilidad-swl.md +5 -3
  37. package/agentes/orquestador-swl.md +29 -8
  38. package/agentes/pagos-swl.md +5 -3
  39. package/agentes/perfilador-usuario-swl.md +4 -2
  40. package/agentes/planificador-swl.md +5 -3
  41. package/agentes/producto-prd-swl.md +5 -3
  42. package/agentes/red-team-swl.md +4 -2
  43. package/agentes/release-manager-swl.md +6 -8
  44. package/agentes/rendimiento-swl.md +5 -6
  45. package/agentes/resolutor-build-swl.md +5 -3
  46. package/agentes/revisor-angular-swl.md +5 -3
  47. package/agentes/revisor-codigo-swl.md +90 -4
  48. package/agentes/revisor-csharp-swl.md +5 -3
  49. package/agentes/revisor-go-swl.md +5 -3
  50. package/agentes/revisor-java-swl.md +5 -3
  51. package/agentes/revisor-kotlin-swl.md +5 -3
  52. package/agentes/revisor-nextjs-swl.md +5 -3
  53. package/agentes/revisor-php-swl.md +5 -3
  54. package/agentes/revisor-react-swl.md +5 -3
  55. package/agentes/revisor-rust-swl.md +5 -3
  56. package/agentes/revisor-seguridad-swl.md +5 -3
  57. package/agentes/revisor-swift-swl.md +5 -3
  58. package/agentes/revisor-typescript-swl.md +5 -3
  59. package/agentes/sre-swl.md +5 -3
  60. package/agentes/tdd-qa-swl.md +5 -3
  61. package/agentes/ux-disenador-swl.md +5 -9
  62. package/comandos/swl/evaluar-skill.md +18 -0
  63. package/comandos/swl/evolucion-estado.md +49 -0
  64. package/comandos/swl/release.md +77 -1
  65. package/comandos/swl/salud.md +23 -0
  66. package/habilidades/checklist-seguridad/SKILL.md +57 -1
  67. package/habilidades/extractor-de-aprendizajes/SKILL.md +15 -5
  68. package/habilidades/fastapi-experto/SKILL.md +10 -1
  69. package/habilidades/manejo-errores/.evolved.json +8 -8
  70. package/habilidades/manejo-errores/SKILL.md +63 -4
  71. package/habilidades/patrones-python/SKILL.md +5 -4
  72. package/habilidades/release-semver/.evolved.json +8 -8
  73. package/habilidades/release-semver/SKILL.md +85 -1
  74. package/hooks/auto-evolucion.js +35 -1
  75. package/hooks/clasificador-mensajes.js +50 -3
  76. package/hooks/lib/agent-routing.js +107 -0
  77. package/hooks/lib/delegation-tracker.js +162 -44
  78. package/hooks/lib/evolution-tracker.js +12 -3
  79. package/hooks/lib/memory-search.js +59 -1
  80. package/hooks/lib/nudge-tracker.js +10 -1
  81. package/hooks/lib/provenance-tracker.js +11 -3
  82. package/hooks/lib/text-similarity.js +241 -0
  83. package/hooks/metricas-evolucion.js +168 -1
  84. package/hooks/monitor-contexto.js +54 -6
  85. package/hooks/preservar-estado-pre-compact.js +11 -1
  86. package/hooks/risk-scoring.js +10 -1
  87. package/hooks/tracking-costos.js +10 -1
  88. package/hooks/validar-formato-post-subagente.js +140 -0
  89. package/hooks/validar-memoria-hook.js +218 -0
  90. package/manifiestos/agent-output-schemas.json +57 -0
  91. package/manifiestos/hooks-config.json +18 -0
  92. package/manifiestos/modulos.json +3 -0
  93. package/manifiestos/skills-lock.json +1065 -0
  94. package/package.json +1 -1
  95. package/plugin.json +1 -1
  96. package/reglas/arquitectura.md +20 -0
  97. package/reglas/fragmentos-compartidos.md +152 -0
  98. package/reglas/gobernanza.md +10 -1
  99. package/reglas/seguridad-agentes.md +12 -0
  100. package/reglas/skills-estandar.md +19 -0
  101. package/schemas/agent-frontmatter.schema.json +18 -0
  102. package/scripts/auditar-agentes-gaps.js +9 -1
  103. package/scripts/auditar-cobertura-frameworks.js +9 -1
  104. package/scripts/auditar-skills-gaps.js +9 -1
  105. package/scripts/bootstrap-instintos.js +11 -1
  106. package/scripts/generar-inventario.js +112 -9
  107. package/scripts/generar-matriz-lenguajes.js +271 -0
  108. package/scripts/generar-skills-lock.js +190 -0
  109. package/scripts/lib/estado.js +12 -2
  110. package/scripts/lib/gitignore-manifest.js +32 -2
  111. package/scripts/migrar-csv-a-array.js +168 -0
  112. package/scripts/migrar-fase-dominio.js +201 -0
  113. package/scripts/publicar.js +88 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saulwade/swl-ses",
3
- "version": "1.0.1",
3
+ "version": "1.1.2",
4
4
  "description": "Sistema de ingenieria de software auto-evolutivo multi-runtime polyglot con 59 agentes, 151 habilidades, 42 comandos, 60 reglas y 37 hooks. Soporta 11 lenguajes y 5 runtimes: Claude Code, Copilot, OpenCode, Codex y Gemini CLI. 100% en espanol (Mexico). Incluye gateway bidireccional con relay Telegram a Claude Code.",
5
5
  "bin": {
6
6
  "swl-ses": "bin/swl-ses.js",
package/plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swl-ses",
3
- "version": "1.0.1",
3
+ "version": "1.1.2",
4
4
  "description": "Sistema de ingenieria de software auto-evolutivo multi-runtime polyglot. 59 agentes, 151 habilidades, 42 comandos, 60 reglas y 37 hooks. 60 librerias. 11 lenguajes. Soporta Claude Code, Copilot, OpenCode, Codex y Gemini CLI.",
5
5
  "author": "Saul Wade Leon",
6
6
  "license": "MIT",
@@ -214,6 +214,26 @@ de abstracciones (protocolos, interfaces, clases abstractas).
214
214
 
215
215
  ---
216
216
 
217
+ ## Tres capas de reutilización: regla, skill, fragmento
218
+
219
+ El sistema SWL distingue tres capas para evitar duplicación. La elección de la
220
+ capa correcta depende de cuándo y cómo se carga el contenido:
221
+
222
+ - **Regla** (`reglas/X.md`): política global cargada por matcher de archivos.
223
+ Aplicable a múltiples agentes y skills. Ejemplo: `reglas/seguridad.md`.
224
+ - **Skill** (`habilidades/X/SKILL.md`): conocimiento operacional invocado
225
+ dinámicamente con `Skill("X")`. Tiene frontmatter, descripción y carga
226
+ bajo demanda. Ejemplo: `habilidades/tdd-workflow/SKILL.md`.
227
+ - **Fragmento** (`agentes/_X.md`): bloque de prompt compartido por varios
228
+ agentes que se incrusta literalmente en su system prompt. Sin overhead
229
+ runtime, sin frontmatter operacional, no routable por el orquestador.
230
+ Ejemplo: `agentes/_hitl-alto-riesgo.md`.
231
+
232
+ Antes de crear texto compartido entre agentes, decidir cuál capa aplica. Ver
233
+ `reglas/fragmentos-compartidos.md` para la convención completa.
234
+
235
+ ---
236
+
217
237
  ## DRY — Don't Repeat Yourself
218
238
 
219
239
  Si una regla de negocio, una validación, un cálculo o una transformación cambia,
@@ -0,0 +1,152 @@
1
+ # Regla: Fragmentos compartidos no-routables
2
+
3
+ Esta regla es **OBLIGATORIA** para autores y mantenedores de agentes y reglas
4
+ del sistema SWL. Define un tercer tier entre reglas y skills: bloques de prompt
5
+ compartidos que se incrustan literalmente en agentes/reglas para eliminar
6
+ duplicación textual sin convertirse en componentes invocables.
7
+
8
+ ---
9
+
10
+ ## El problema que resuelve
11
+
12
+ Hoy cuando dos agentes necesitan repetir el mismo párrafo (protocolo HITL para
13
+ nivelRiesgo ALTO, anti-fallback silencioso, regla de paralelización, mención
14
+ de "aplica brevedad-output"), las opciones existentes son:
15
+
16
+ | Mecanismo | Problema |
17
+ |-----------|----------|
18
+ | Copiar y pegar el bloque | Drift textual: una copia se actualiza, la otra no. |
19
+ | Convertirlo en regla obligatoria | Las reglas son globales (cargadas por matchers), no se incrustan literal en un agente concreto. |
20
+ | Convertirlo en skill | Las skills se invocan dinámicamente con `Skill("nombre")` y consumen tokens; un fragmento de 6 líneas no justifica el overhead. |
21
+ | Convertirlo en agente | Los agentes son routables; un guard compartido no debe aparecer en la tabla de routing del orquestador. |
22
+
23
+ Faltaba una capa para "texto compartido que vive en el system prompt del agente,
24
+ sin overhead runtime ni routing". Esta regla la formaliza.
25
+
26
+ ---
27
+
28
+ ## Convención
29
+
30
+ ### Ubicación y naming
31
+
32
+ | Tipo | Ubicación | Naming |
33
+ |------|-----------|--------|
34
+ | Fragmento para agentes | `agentes/_*.md` | prefijo `_`, kebab-case, **sin sufijo `-swl`** |
35
+ | Fragmento para reglas | `reglas/_fragmentos/_*.md` | prefijo `_`, kebab-case |
36
+
37
+ **Ejemplos válidos**:
38
+ - `agentes/_hitl-alto-riesgo.md`
39
+ - `agentes/_anti-fallback-silencioso.md`
40
+ - `agentes/_brevedad-output-ref.md`
41
+ - `reglas/_fragmentos/_descarga-progresiva.md`
42
+
43
+ **Ejemplos inválidos**:
44
+ - `agentes/_scope-guard-swl.md` (no usar sufijo `-swl` — no es un agente)
45
+ - `agentes/scope_guard.md` (snake_case prohibido)
46
+ - `agentes/_ScopeGuard.md` (camelCase prohibido)
47
+
48
+ ### Reglas duras
49
+
50
+ 1. **NO routable**: el orquestador NO incluye `agentes/_*.md` en su tabla de
51
+ rutas. El prefijo `_` señala al loader que ese archivo es un fragmento, no
52
+ un agente. La validación `scripts/validar-manifest.js` rechaza si un
53
+ `agentes/_*.md` aparece en `manifiestos/modulos.json` como agente.
54
+
55
+ 2. **NO invocable**: no se carga con `Skill("...")`. No tiene frontmatter
56
+ `name`/`description` ni schema. Es texto puro Markdown.
57
+
58
+ 3. **Importación declarativa via frontmatter**: el agente que usa el fragmento
59
+ lo declara en su frontmatter:
60
+ ```yaml
61
+ fragmentos: [_hitl-alto-riesgo, _anti-fallback-silencioso]
62
+ ```
63
+ El campo `fragmentos` está definido en `schemas/agent-frontmatter.schema.json`.
64
+ El loader (al cargar el agente) lee cada fragmento referenciado y lo concatena
65
+ al final del system prompt.
66
+
67
+ 4. **Tamaño máximo 100 líneas**: si un fragmento crece más, probablemente debe
68
+ ser una regla obligatoria o una skill. Los fragmentos son párrafos, no
69
+ documentos.
70
+
71
+ 5. **Sin frontmatter operacional**: el archivo `_*.md` puede tener un comentario
72
+ YAML opcional al inicio con metadata humana (autor, agentes que lo usan), pero
73
+ NO tiene campos como `nivelRiesgo` o `permisosRed` — los hereda del agente
74
+ que lo importa.
75
+
76
+ 6. **Auditoría obligatoria**: cuando se crea un fragmento, el script
77
+ `scripts/validar-manifest.js` verifica que el fragmento es referenciado por
78
+ ≥2 agentes. Un fragmento usado por un solo agente no justifica su existencia
79
+ y debe re-incrustarse en el agente.
80
+
81
+ ---
82
+
83
+ ## Cuándo crear un fragmento
84
+
85
+ ✅ Crear cuando:
86
+ - El mismo bloque de ≥3 líneas aparece literalmente en ≥3 agentes.
87
+ - El bloque es estructural (protocolo, regla operativa) — no contenido de
88
+ dominio variable.
89
+ - Cambiar el bloque en el futuro debe propagarse a todos los agentes.
90
+
91
+ ❌ NO crear cuando:
92
+ - El texto solo aparece en 1-2 agentes (no hay ahorro real).
93
+ - El bloque ya existe como regla obligatoria (cargada por matcher).
94
+ - El comportamiento se invoca dinámicamente (eso es una skill).
95
+ - Es un párrafo de menos de 80 caracteres (no justifica el overhead de gestión).
96
+
97
+ ---
98
+
99
+ ## Diferencia con reglas y skills
100
+
101
+ | Capa | Cuándo carga | Cómo se usa | Ejemplo |
102
+ |------|-------------|-------------|---------|
103
+ | **Regla** (`reglas/X.md`) | Por matcher cuando se tocan ciertos archivos | Cargada al contexto global de la sesión | `reglas/seguridad.md` aplica a `*.py`, `auth/`, etc. |
104
+ | **Skill** (`habilidades/X/SKILL.md`) | Cuando un agente invoca `Skill("X")` | Carga bajo demanda, contiene instrucciones operativas | `habilidades/tdd-workflow/SKILL.md` |
105
+ | **Fragmento** (`agentes/_X.md`) | Al cargar un agente que lo declara en `fragmentos` | Se incrusta literalmente en system prompt — sin overhead runtime | `agentes/_hitl-alto-riesgo.md` |
106
+
107
+ Los fragmentos son la opción más barata: cero tokens runtime adicional, máxima
108
+ consistencia textual entre agentes que comparten un bloque.
109
+
110
+ ---
111
+
112
+ ## Migración desde duplicación existente
113
+
114
+ Para extraer un bloque duplicado a fragmento:
115
+
116
+ 1. **Identificar**: con `Grep` confirmar que el texto aparece literal en ≥3 agentes.
117
+ 2. **Crear** el archivo `agentes/_nombre.md` con el bloque en el estado más
118
+ reciente/completo de las copias existentes.
119
+ 3. **Reemplazar** en cada agente la duplicación por una referencia mínima:
120
+ ```markdown
121
+ <!-- fragmento: _nombre -->
122
+ ```
123
+ El loader sustituye este marcador por el contenido del fragmento al cargar.
124
+ 4. **Declarar** en el frontmatter del agente:
125
+ ```yaml
126
+ fragmentos: [_nombre]
127
+ ```
128
+ 5. **Verificar** con `node scripts/validar-manifest.js` que el fragmento está
129
+ referenciado por ≥2 agentes.
130
+ 6. **Commit atómico**: `refactor(fragmentos): extraer X a _nombre — usado en N agentes`.
131
+
132
+ ---
133
+
134
+ ## Origen y referencias
135
+
136
+ - Patrón inspirado en `_scope-guard.md` de Bug-Bounty-Agents
137
+ (https://github.com/anthropics/bug-bounty-agents) — convención `_` como
138
+ shared prompt block no-routable.
139
+ - Decisión documentada en ADR-XXX (ver `.planning/adrs/`).
140
+ - Schema: `schemas/agent-frontmatter.schema.json` campo `fragmentos`.
141
+
142
+ ---
143
+
144
+ ## Checklist antes de crear un fragmento
145
+
146
+ - [ ] El texto aparece literal en ≥3 agentes
147
+ - [ ] El nombre sigue convención `_kebab-case.md` (sin sufijo `-swl`)
148
+ - [ ] Tamaño ≤100 líneas
149
+ - [ ] No reemplaza a una regla obligatoria existente
150
+ - [ ] No es contenido invocable dinámicamente (eso es skill)
151
+ - [ ] Cada agente que lo usa lo declara en `fragmentos: [...]`
152
+ - [ ] `scripts/validar-manifest.js` no reporta errores
@@ -32,7 +32,16 @@ Los skills generados por `auto-evolucion-swl` o `/swl:evolucionar`:
32
32
  - Requieren validación en al menos 3 sesiones de trabajo independientes
33
33
  antes de ser promovidos al perfil `completo`.
34
34
  - Deben pasar la verificación de `/swl:salud` sin degradar el score actual.
35
- - La promoción se registra en `.planning/AUDITORIA.md` con justificación.
35
+ - **Gate G8 evidencia de calidad obligatoria**: antes de mover un skill
36
+ desde `_userland/plugins/` a `habilidades/`, ejecutar
37
+ `/swl:evaluar-skill <nombre>` y exigir badge ≥ **Plata** (score ≥ 70).
38
+ Skills con Bronce o sin badge se devuelven a `_userland/` con feedback
39
+ de qué dimensiones bajan el score. Detalle del flujo en
40
+ `agentes/auto-evolucion-swl.md` sección "Gate G8". Origen ADR 0013
41
+ sección 3C.
42
+ - La promoción se registra en `.planning/AUDITORIA.md` con justificación
43
+ Y en `.planning/evolucion/evoluciones.jsonl` con evento
44
+ `tipo: "promocion-skill"` y `score`.
36
45
 
37
46
  ### Reglas nuevas obligatorias
38
47
 
@@ -34,6 +34,18 @@ Cada agente recibe exclusivamente los permisos que necesita para su función.
34
34
  - Un agente sin `permisosComandos` no debe ejecutar Bash bajo ninguna circunstancia.
35
35
  - El `toolBudget` limita la cantidad de herramientas por invocación. Agentes
36
36
  con presupuesto `simple` (1-3 tools) no deben recibir tareas que requieran más.
37
+ - El `maxTurnos` (definido en `schemas/agent-frontmatter.schema.json`) declara
38
+ el tope explícito de iteraciones (tool calls) que el agente puede ejecutar
39
+ antes de pausar y escalar. **OBLIGATORIO** declararlo en frontmatter para
40
+ agentes con patrón evaluator-optimizer, recovery catalog, debugging
41
+ iterativo, o cualquier loop autónomo. Valores recomendados según patrón
42
+ observado: thin orchestrator/QA lineal: 10; debugging iterativo: 12;
43
+ implementación con write-complete-test-once: 15; release multi-fase: 18;
44
+ auto-evolución con gates G1-G8: 20; migración expand-contract: 25.
45
+ Agentes one-shot (revisores, documentadores, diseñadores) NO requieren
46
+ `maxTurnos` — son no-iterativos por diseño. Patrón inspirado en *Building
47
+ Effective AI Agents* (Anthropic, p.10) — "stopping conditions, maximum
48
+ number of iterations".
37
49
 
38
50
  ### Anti-escalación en cadenas de delegación
39
51
 
@@ -239,6 +239,25 @@ CLAUDE.md el orquestador no sabe que existe.
239
239
 
240
240
  ---
241
241
 
242
+ ## Diferencia con fragmentos compartidos
243
+
244
+ Las skills NO son la única capa de reutilización del sistema SWL. Existen tres
245
+ capas distintas — cada una con su propósito:
246
+
247
+ | Capa | Cuándo usar | Cómo se carga |
248
+ |------|-------------|---------------|
249
+ | **Regla** (`reglas/X.md`) | Política transversal aplicable por matcher de archivos | Carga global cuando el matcher se cumple |
250
+ | **Skill** (`habilidades/X/SKILL.md`) | Conocimiento operacional invocable bajo demanda | Carga al invocar `Skill("X")` |
251
+ | **Fragmento** (`agentes/_X.md`) | Bloque de texto compartido por ≥3 agentes en su system prompt | Se incrusta literalmente al cargar el agente — sin overhead runtime |
252
+
253
+ NO crear una skill cuando el contenido es:
254
+ - Texto compartido entre múltiples agentes que no se invoca dinámicamente → es fragmento.
255
+ - Política aplicable a archivos del codebase → es regla.
256
+
257
+ Ver `reglas/fragmentos-compartidos.md` para la convención completa de fragmentos.
258
+
259
+ ---
260
+
242
261
  ## Convención de naming para skills nuevos
243
262
 
244
263
  Todo skill nuevo DEBE usar prefijo de dominio. Facilita agrupación lógica sin
@@ -143,6 +143,24 @@
143
143
  "type": "array",
144
144
  "items": { "type": "string" },
145
145
  "description": "Situaciones donde este agente NO debe invocarse aunque parezca relevante superficialmente. Campo propio de swl-ses; previene activacion tangencial (agent hijacking) por similitud de terminos en description. Equivalente estructural de la seccion 'Cuando NO invocarme' en el cuerpo, legible por herramientas de analisis sin parsear markdown. Aditivo, no breaking, aplicable a agentes con evolvable:false (metadata defensiva, no evolucion AGP — ver ADR-0004)."
146
+ },
147
+ "fase": {
148
+ "type": "string",
149
+ "enum": ["discover", "plan", "implement", "verify", "release", "learn", "meta", "cross"],
150
+ "description": "Fase del SDLC en que opera el agente. Vocabulario controlado para routing deterministico del orquestador sin parsear description. discover=research/UX/discovery; plan=arquitectura/planificacion; implement=codigo/implementacion; verify=revision/testing/seguridad; release=deploy/release; learn=auto-evolucion/aprendizaje; meta=componentes del sistema SWL mismo; cross=transversal a varias fases. Aditivo, opcional. Inspirado en patron Phase de Bug-Bounty-Agents/AGENTS.md y patron Workflow-vs-Agent del articulo Anthropic 'Building Effective AI Agents'."
151
+ },
152
+ "dominio": {
153
+ "type": "string",
154
+ "enum": ["backend", "frontend", "mobile", "data", "infra", "security", "ux", "quality", "docs", "process", "meta", "general"],
155
+ "description": "Dominio tecnico primario del agente. Vocabulario controlado para filtrado y matching. backend=APIs/servidores; frontend=UI/web; mobile=Android/iOS/cross; data=BD/ETL/datos; infra=cloud/CI/devops; security=auditoria/OWASP; ux=diseno/usabilidad; quality=tests/QA/code review; docs=documentacion; process=workflow/orquestacion; meta=sistema SWL; general=transversal. Aditivo, opcional."
156
+ },
157
+ "fragmentos": {
158
+ "type": "array",
159
+ "items": {
160
+ "type": "string",
161
+ "pattern": "^_[a-z][a-z0-9-]*$"
162
+ },
163
+ "description": "Lista de bloques de prompt compartidos no-routables (archivos agentes/_*.md) que este agente importa literalmente en su system prompt. Permite centralizar texto repetido entre multiples agentes sin duplicacion. Los fragmentos NO son skills (no se invocan dinamicamente) ni agentes (no son routables por el orquestador). Convencion: prefijo _ en kebab-case, sin sufijo -swl. Ejemplo: ['_brevedad-output-ref', '_hitl-alto-riesgo']. Inspirado en patron _scope-guard.md de Bug-Bounty-Agents."
146
164
  }
147
165
  },
148
166
  "additionalProperties": true
@@ -24,6 +24,14 @@
24
24
  var fs = require('fs');
25
25
  var path = require('path');
26
26
 
27
+ // Escritura atómica obligatoria (regla CLAUDE.md). Fallback defensivo.
28
+ var atomicWriteJSON;
29
+ try {
30
+ atomicWriteJSON = require(path.join(__dirname, '..', 'hooks', 'lib', 'atomic-write.js')).atomicWriteJSON;
31
+ } catch (_) {
32
+ atomicWriteJSON = function (p, o) { fs.writeFileSync(p, JSON.stringify(o, null, 2), 'utf8'); };
33
+ }
34
+
27
35
  var RAIZ_AGENTES = path.resolve(__dirname, '..', 'agentes');
28
36
  var DESTINO_PLANNING = path.resolve(
29
37
  __dirname, '..', '.planning', 'evolucion', 'audit-agentes-gaps.json'
@@ -140,7 +148,7 @@ if (argv.indexOf('--resumen') !== -1) {
140
148
  } else if (argv.indexOf('--save') !== -1) {
141
149
  var dir = path.dirname(DESTINO_PLANNING);
142
150
  fs.mkdirSync(dir, { recursive: true });
143
- fs.writeFileSync(DESTINO_PLANNING, JSON.stringify(resultado, null, 2), 'utf8');
151
+ atomicWriteJSON(DESTINO_PLANNING, resultado);
144
152
  imprimirResumen(resultado);
145
153
  process.stderr.write('Inventario persistido en: ' + DESTINO_PLANNING + '\n');
146
154
  } else {
@@ -25,6 +25,14 @@
25
25
  const fs = require('fs');
26
26
  const path = require('path');
27
27
 
28
+ // Escritura atómica obligatoria (regla CLAUDE.md). Fallback defensivo.
29
+ let atomicWriteJSON;
30
+ try {
31
+ ({ atomicWriteJSON } = require(path.join(__dirname, '..', 'hooks', 'lib', 'atomic-write.js')));
32
+ } catch {
33
+ atomicWriteJSON = (p, o) => fs.writeFileSync(p, JSON.stringify(o, null, 2), 'utf8');
34
+ }
35
+
28
36
  const RAIZ_SKILLS = path.resolve(__dirname, '..', 'habilidades');
29
37
  const DESTINO = path.resolve(
30
38
  __dirname, '..', '.planning', 'evolucion', 'cobertura-frameworks.json'
@@ -232,7 +240,7 @@ if (argv.includes('--resumen')) {
232
240
  } else if (argv.includes('--save')) {
233
241
  const dir = path.dirname(DESTINO);
234
242
  fs.mkdirSync(dir, { recursive: true });
235
- fs.writeFileSync(DESTINO, JSON.stringify(resultado, null, 2), 'utf8');
243
+ atomicWriteJSON(DESTINO, resultado);
236
244
  imprimirResumen(resultado);
237
245
  process.stderr.write(`Inventario persistido en: ${DESTINO}\n`);
238
246
  } else {
@@ -20,6 +20,14 @@
20
20
  const fs = require('fs');
21
21
  const path = require('path');
22
22
 
23
+ // Escritura atómica obligatoria (regla CLAUDE.md). Fallback defensivo.
24
+ let atomicWriteJSON;
25
+ try {
26
+ ({ atomicWriteJSON } = require(path.join(__dirname, '..', 'hooks', 'lib', 'atomic-write.js')));
27
+ } catch {
28
+ atomicWriteJSON = (p, o) => fs.writeFileSync(p, JSON.stringify(o, null, 2), 'utf8');
29
+ }
30
+
23
31
  const RAIZ_SKILLS = path.resolve(__dirname, '..', 'habilidades');
24
32
  const DESTINO_PLANNING = path.resolve(
25
33
  __dirname, '..', '.planning', 'evolucion', 'audit-skills-gaps.json'
@@ -197,7 +205,7 @@ if (argv.includes('--resumen')) {
197
205
  } else if (argv.includes('--save')) {
198
206
  const dir = path.dirname(DESTINO_PLANNING);
199
207
  fs.mkdirSync(dir, { recursive: true });
200
- fs.writeFileSync(DESTINO_PLANNING, JSON.stringify(resultado, null, 2), 'utf8');
208
+ atomicWriteJSON(DESTINO_PLANNING, resultado);
201
209
  imprimirResumen(resultado);
202
210
  process.stderr.write(`Inventario persistido en: ${DESTINO_PLANNING}\n`);
203
211
  } else {
@@ -30,6 +30,16 @@ const CWD = process.cwd();
30
30
  const APRENDIZAJES_PATH = path.join(CWD, '.planning', 'APRENDIZAJES.md');
31
31
  const INSTINTOS_PATH = path.join(CWD, 'instintos', 'proyecto.yaml');
32
32
 
33
+ // Escritura atómica obligatoria — instintos/proyecto.yaml es el catálogo
34
+ // vivo de instintos del proyecto; corromperlo equivale a perder la memoria
35
+ // inductiva del sistema. Fallback defensivo si el módulo no está disponible.
36
+ let atomicWriteSync;
37
+ try {
38
+ ({ atomicWriteSync } = require(path.join(CWD, 'hooks', 'lib', 'atomic-write.js')));
39
+ } catch {
40
+ atomicWriteSync = (p, c, e) => fs.writeFileSync(p, c, e);
41
+ }
42
+
33
43
  const flags = {
34
44
  write: process.argv.includes('--write'),
35
45
  force: process.argv.includes('--force'),
@@ -250,7 +260,7 @@ function main() {
250
260
  }
251
261
 
252
262
  const yaml = serializarYaml(instintos);
253
- fs.writeFileSync(INSTINTOS_PATH, yaml, 'utf8');
263
+ atomicWriteSync(INSTINTOS_PATH, yaml, 'utf8');
254
264
  console.log(`\nOK: ${instintos.length} instintos escritos en ${INSTINTOS_PATH}`);
255
265
  }
256
266
 
@@ -20,6 +20,15 @@ const fs = require('fs');
20
20
  const path = require('path');
21
21
 
22
22
  const RAIZ = path.resolve(__dirname, '..');
23
+
24
+ // Escritura atómica obligatoria (regla CLAUDE.md). INVENTARIO.md y SALUD.md
25
+ // son artefactos de release; corrupción durante regeneración rompe la gate.
26
+ let atomicWriteSync;
27
+ try {
28
+ ({ atomicWriteSync } = require(path.join(RAIZ, 'hooks', 'lib', 'atomic-write.js')));
29
+ } catch {
30
+ atomicWriteSync = (p, c, e) => fs.writeFileSync(p, c, e);
31
+ }
23
32
  const args = new Set(process.argv.slice(2));
24
33
  const DRY = args.has('--dry-run');
25
34
  const SOLO_INV = args.has('--inventario');
@@ -206,6 +215,62 @@ function generarInventario() {
206
215
 
207
216
  // ── Generar SALUD.md ──────────────────────────────────────────────────
208
217
 
218
+ /**
219
+ * Componentes críticos del sistema cuya conducta debe estar verificada con
220
+ * tests/evals para que el score conductual no sea opaco.
221
+ *
222
+ * Cada entrada lista los keywords que matchean nombres de archivos en tests/
223
+ * o evals/. Si al menos un archivo coincide, el componente tiene cobertura
224
+ * conductual mínima.
225
+ */
226
+ const COMPONENTES_CRITICOS = [
227
+ { id: 'orquestador', keywords: ['orquestador', 'orquestacion'] },
228
+ { id: 'auto-evolucion', keywords: ['auto-evolucion', 'auto-evolution'] },
229
+ { id: 'red-team', keywords: ['red-team', 'redteam'] },
230
+ { id: 'risk-scoring', keywords: ['risk-scoring', 'risk_scoring', 'risk-engine'] },
231
+ { id: 'memory-search', keywords: ['memory-search', 'memoria-busqueda'] },
232
+ { id: 'command-relay', keywords: ['command-relay', 'gateway'] },
233
+ { id: 'privacy-filter', keywords: ['privacy', 'privacy-filter', 'privacy-memoria'] },
234
+ { id: 'delegation', keywords: ['delegation', 'delegacion'] },
235
+ ];
236
+
237
+ /**
238
+ * Calcula cobertura conductual: cuántos componentes críticos tienen ≥1 archivo
239
+ * de test o eval. NO mide calidad de los tests — solo presencia mínima.
240
+ */
241
+ function calcularScoreConductual() {
242
+ const todosTests = [];
243
+ const walk = (dir) => {
244
+ let entries;
245
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); }
246
+ catch { return; }
247
+ for (const e of entries) {
248
+ const full = path.join(dir, e.name);
249
+ if (e.isDirectory()) walk(full);
250
+ else todosTests.push(full.toLowerCase());
251
+ }
252
+ };
253
+ walk(path.join(RAIZ, 'tests'));
254
+ // También considerar evals dentro de habilidades/*/evals/
255
+ walk(path.join(RAIZ, 'habilidades'));
256
+
257
+ const cobertura = COMPONENTES_CRITICOS.map(c => {
258
+ const cubierto = todosTests.some(t =>
259
+ c.keywords.some(k => t.includes(k.toLowerCase()))
260
+ );
261
+ return { id: c.id, cubierto };
262
+ });
263
+
264
+ const cubiertos = cobertura.filter(c => c.cubierto).length;
265
+ const total = cobertura.length;
266
+ return {
267
+ cubiertos,
268
+ total,
269
+ porcentaje: Math.round((cubiertos / total) * 100),
270
+ detalle: cobertura,
271
+ };
272
+ }
273
+
209
274
  function generarSalud() {
210
275
  const pkg = JSON.parse(fs.readFileSync(path.join(RAIZ, 'package.json'), 'utf8'));
211
276
  const plugin = JSON.parse(fs.readFileSync(path.join(RAIZ, 'plugin.json'), 'utf8'));
@@ -233,14 +298,24 @@ function generarSalud() {
233
298
  // Verificar versiones
234
299
  const versionConsistente = pkg.version === plugin.version;
235
300
 
236
- // Problemas
301
+ // Problemas estructurales
237
302
  const problemas = [];
238
303
  if (!versionConsistente) problemas.push(`Versiones inconsistentes: package.json=${pkg.version} plugin.json=${plugin.version}`);
239
304
  if (hookSyntax.fail > 0) problemas.push(`${hookSyntax.fail} hooks con error de sintaxis`);
240
305
  if (libSyntax.fail > 0) problemas.push(`${libSyntax.fail} libs con error de sintaxis`);
241
306
 
242
- const score = problemas.length === 0 ? '100%' : `${Math.round((1 - problemas.length / 10) * 100)}%`;
243
- const estado = problemas.length === 0 ? 'Excelente' : 'Con problemas';
307
+ const scoreEstructural = problemas.length === 0
308
+ ? '100%'
309
+ : `${Math.round((1 - problemas.length / 10) * 100)}%`;
310
+ const estadoEstructural = problemas.length === 0 ? 'Excelente' : 'Con problemas';
311
+
312
+ // Score conductual (cobertura mínima por componente crítico)
313
+ const conductual = calcularScoreConductual();
314
+ let estadoConductual;
315
+ if (conductual.porcentaje >= 80) estadoConductual = 'Excelente';
316
+ else if (conductual.porcentaje >= 50) estadoConductual = 'Aceptable';
317
+ else if (conductual.porcentaje >= 20) estadoConductual = 'Insuficiente';
318
+ else estadoConductual = 'Crítico';
244
319
 
245
320
  const lines = [];
246
321
  lines.push('# Reporte de Salud del Sistema SWL');
@@ -251,7 +326,19 @@ function generarSalud() {
251
326
  lines.push('> Archivo generado automáticamente por `node scripts/generar-inventario.js --salud`.');
252
327
  lines.push('> NO editar manualmente — se regenera con cada release.');
253
328
  lines.push('');
254
- lines.push(`## Score Global: ${score} — Sistema en ${estado.toLowerCase()}`);
329
+ lines.push('## Dos scores, dos preguntas');
330
+ lines.push('');
331
+ lines.push('- **Score estructural**: ¿el paquete está bien armado? (frontmatter,');
332
+ lines.push(' sintaxis, schemas, manifiestos, versiones consistentes).');
333
+ lines.push('- **Score conductual**: ¿el sistema hace lo que dice hacer? (cobertura');
334
+ lines.push(' de tests/evals para componentes críticos: orquestación, evolución,');
335
+ lines.push(' seguridad, memoria, gateway).');
336
+ lines.push('');
337
+ lines.push('Un score estructural alto sin score conductual igual NO implica que el');
338
+ lines.push('sistema funcione — solo que está bien empaquetado. La separación es');
339
+ lines.push('honestidad operativa: ver `.planning/adrs/` para histórico de decisiones.');
340
+ lines.push('');
341
+ lines.push(`## Score estructural: ${scoreEstructural} — ${estadoEstructural}`);
255
342
  lines.push('');
256
343
  lines.push('| Componente | Score | Estado | Problemas |');
257
344
  lines.push('|------------|-------|--------|-----------|');
@@ -265,19 +352,35 @@ function generarSalud() {
265
352
  lines.push(`| Versiones | ${versionConsistente ? '100%' : '0%'} | ${versionConsistente ? 'Consistentes' : 'ERROR'} | package.json = plugin.json = ${pkg.version} |`);
266
353
  lines.push('');
267
354
 
355
+ lines.push(`## Score conductual: ${conductual.porcentaje}% — ${estadoConductual}`);
356
+ lines.push('');
357
+ lines.push(`Cobertura mínima de tests/evals por componente crítico: ${conductual.cubiertos}/${conductual.total}.`);
358
+ lines.push('');
359
+ lines.push('Este score mide **presencia** de tests, no calidad. Un componente con un');
360
+ lines.push('solo test marca como cubierto. Subir esta cifra requiere agregar evals');
361
+ lines.push('comportamentales por componente — ver propuesta #4 en `.planning/`.');
362
+ lines.push('');
363
+ lines.push('| Componente crítico | Cobertura |');
364
+ lines.push('|--------------------|-----------|');
365
+ for (const c of conductual.detalle) {
366
+ const icono = c.cubierto ? '✅' : '❌';
367
+ lines.push(`| ${c.id} | ${icono} ${c.cubierto ? 'sí' : 'no — falta eval'} |`);
368
+ }
369
+ lines.push('');
370
+
268
371
  if (problemas.length > 0) {
269
- lines.push('## Problemas detectados');
372
+ lines.push('## Problemas estructurales detectados');
270
373
  lines.push('');
271
374
  for (const p of problemas) lines.push(`- ${p}`);
272
375
  lines.push('');
273
376
  } else {
274
- lines.push('## Problemas críticos');
377
+ lines.push('## Problemas estructurales');
275
378
  lines.push('');
276
379
  lines.push('Ninguno.');
277
380
  lines.push('');
278
381
  }
279
382
 
280
- lines.push('## Verificaciones positivas');
383
+ lines.push('## Verificaciones positivas (estructurales)');
281
384
  lines.push('');
282
385
  lines.push(`- **Frontmatter**: ${agentes.length} agentes + ${skills.length} skills + ${comandos} comandos verificados`);
283
386
  lines.push(`- **Skills**: ${skills.length}/${skills.length} con SKILL.md presente`);
@@ -300,7 +403,7 @@ if (AMBOS || SOLO_INV) {
300
403
  console.log('=== INVENTARIO.md (dry-run) ===\n');
301
404
  console.log(inventario.substring(0, 2000) + '\n...(truncado)');
302
405
  } else {
303
- fs.writeFileSync(path.join(RAIZ, 'INVENTARIO.md'), inventario, 'utf8');
406
+ atomicWriteSync(path.join(RAIZ, 'INVENTARIO.md'), inventario, 'utf8');
304
407
  console.log('INVENTARIO.md generado correctamente.');
305
408
  }
306
409
  }
@@ -311,7 +414,7 @@ if (AMBOS || SOLO_SAL) {
311
414
  console.log('\n=== SALUD.md (dry-run) ===\n');
312
415
  console.log(salud);
313
416
  } else {
314
- fs.writeFileSync(path.join(RAIZ, 'SALUD.md'), salud, 'utf8');
417
+ atomicWriteSync(path.join(RAIZ, 'SALUD.md'), salud, 'utf8');
315
418
  console.log('SALUD.md generado correctamente.');
316
419
  }
317
420
  }