@saulwade/swl-ses 2.5.3 → 2.6.1

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 (214) hide show
  1. package/CLAUDE.md +9 -9
  2. package/README.md +37 -37
  3. package/agentes/_intent-spec.md +73 -73
  4. package/agentes/_propose-step.md +90 -90
  5. package/agentes/accesibilidad-wcag-swl.md +690 -690
  6. package/agentes/arquitecto-swl.md +267 -267
  7. package/agentes/auto-evolucion-swl.md +932 -908
  8. package/agentes/backend-csharp-swl.md +420 -420
  9. package/agentes/backend-go-swl.md +390 -390
  10. package/agentes/backend-java-swl.md +281 -281
  11. package/agentes/backend-rust-swl.md +364 -364
  12. package/agentes/backend-workers-swl.md +482 -482
  13. package/agentes/cloud-infra-swl.md +509 -509
  14. package/agentes/consolidador-swl.md +541 -541
  15. package/agentes/depurador-swl.md +352 -352
  16. package/agentes/devops-ci-swl.md +400 -400
  17. package/agentes/disenador-ui-swl.md +569 -569
  18. package/agentes/documentador-swl.md +345 -345
  19. package/agentes/frontend-angular-swl.md +621 -621
  20. package/agentes/frontend-css-swl.md +716 -716
  21. package/agentes/frontend-react-swl.md +692 -692
  22. package/agentes/frontend-swl.md +496 -496
  23. package/agentes/frontend-tailwind-swl.md +826 -826
  24. package/agentes/investigador-swl.md +432 -432
  25. package/agentes/investigador-ux-swl.md +505 -505
  26. package/agentes/migrador-swl.md +442 -442
  27. package/agentes/mobile-android-swl.md +511 -511
  28. package/agentes/mobile-cross-swl.md +541 -541
  29. package/agentes/mobile-ios-swl.md +502 -502
  30. package/agentes/mobile-testing-swl.md +302 -302
  31. package/agentes/nemesis-auditor-swl.md +285 -285
  32. package/agentes/observabilidad-swl.md +438 -438
  33. package/agentes/pagos-swl.md +310 -310
  34. package/agentes/perfilador-usuario-swl.md +321 -321
  35. package/agentes/planificador-swl.md +399 -399
  36. package/agentes/producto-prd-swl.md +589 -589
  37. package/agentes/red-team-swl.md +218 -218
  38. package/agentes/release-manager-swl.md +590 -590
  39. package/agentes/rendimiento-swl.md +713 -713
  40. package/agentes/revisor-angular-swl.md +278 -278
  41. package/agentes/revisor-csharp-swl.md +264 -264
  42. package/agentes/revisor-go-swl.md +259 -259
  43. package/agentes/revisor-java-swl.md +257 -257
  44. package/agentes/revisor-kotlin-swl.md +273 -273
  45. package/agentes/revisor-nextjs-swl.md +281 -281
  46. package/agentes/revisor-php-swl.md +271 -271
  47. package/agentes/revisor-react-swl.md +278 -278
  48. package/agentes/revisor-rust-swl.md +346 -346
  49. package/agentes/revisor-seguridad-swl.md +399 -399
  50. package/agentes/revisor-swift-swl.md +268 -268
  51. package/agentes/revisor-typescript-swl.md +346 -346
  52. package/agentes/tdd-qa-swl.md +393 -393
  53. package/bin/swl-ses.js +32 -7
  54. package/comandos/swl/actualizar.md +3 -3
  55. package/comandos/swl/aprender.md +13 -0
  56. package/comandos/swl/deuda-codigo.md +97 -97
  57. package/comandos/swl/evaluar-skill.md +18 -3
  58. package/comandos/swl/evolucion-continua.md +73 -0
  59. package/comandos/swl/evolucionar.md +13 -0
  60. package/comandos/swl/instalar.md +4 -4
  61. package/comandos/swl/notificaciones.md +1 -1
  62. package/comandos/swl/status.md +2 -2
  63. package/gateway/cron/jobs.example.json +12 -0
  64. package/habilidades/auto-evolucion-protocolo/SKILL.md +19 -1
  65. package/habilidades/autoresearch/SKILL.md +3 -2
  66. package/habilidades/backend-async-postgres-testing/SKILL.md +2 -1
  67. package/habilidades/benchmark-memoria/SKILL.md +7 -7
  68. package/habilidades/changelog-generator/SKILL.md +1 -1
  69. package/habilidades/changelog-generator/scripts/parse-commits.js +2 -1
  70. package/habilidades/checkpoints-verificacion/SKILL.md +6 -0
  71. package/habilidades/compactacion-contexto/SKILL.md +2 -1
  72. package/habilidades/contenedores-docker/SKILL.md +4 -2
  73. package/habilidades/context-builder/SKILL.md +4 -0
  74. package/habilidades/doubt-driven-review/SKILL.md +17 -1
  75. package/habilidades/drift-detection/SKILL.md +6 -1
  76. package/habilidades/ejecutar-fase/SKILL.md +6 -6
  77. package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
  78. package/habilidades/eval-framework/SKILL.md +8 -3
  79. package/habilidades/extractor-de-aprendizajes/SKILL.md +8 -2
  80. package/habilidades/git-worktrees-paralelo/SKILL.md +19 -1
  81. package/habilidades/harness-claude-code/SKILL.md +7 -3
  82. package/habilidades/infra-github-actions/SKILL.md +4 -3
  83. package/habilidades/instalar-sistema/SKILL.md +5 -1
  84. package/habilidades/memoria-busqueda/SKILL.md +31 -39
  85. package/habilidades/planear-fase/SKILL.md +9 -1
  86. package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -166
  87. package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -85
  88. package/habilidades/proceso-ddia-fundamentos/SKILL.md +3 -2
  89. package/habilidades/proceso-ingenieria-requerimientos/SKILL.md +147 -147
  90. package/habilidades/release-semver/SKILL.md +2 -2
  91. package/habilidades/swl-claudemd/SKILL.md +6 -7
  92. package/habilidades/swl-dashboard/SKILL.md +11 -43
  93. package/habilidades/tdd-workflow/SKILL.md +12 -7
  94. package/habilidades/validacion-ci-sistema/SKILL.md +1 -1
  95. package/hooks/agente-lifecycle.js +2 -1
  96. package/hooks/aiisms-detector.js +13 -4
  97. package/hooks/audit-trail.js +2 -1
  98. package/hooks/auto-consolidacion.js +2 -1
  99. package/hooks/captura-acciones-post.js +2 -1
  100. package/hooks/captura-acciones-session.js +2 -1
  101. package/hooks/captura-feedback-usuario.js +3 -2
  102. package/hooks/claudemd-bloat-detector.js +12 -3
  103. package/hooks/claudemd-duplicacion-detector.js +13 -3
  104. package/hooks/contexto-iteracion.js +2 -1
  105. package/hooks/contexto-subagente.js +68 -68
  106. package/hooks/degradacion-instintos.js +2 -1
  107. package/hooks/extraccion-aprendizajes.js +109 -15
  108. package/hooks/grafo-contexto.js +2 -1
  109. package/hooks/guardrail-modelo.js +2 -1
  110. package/hooks/inbox-aviso.js +2 -1
  111. package/hooks/inyeccion-contexto.js +2 -1
  112. package/hooks/lib/agent-matcher.js +2 -1
  113. package/hooks/lib/agent-routing.js +2 -1
  114. package/hooks/lib/autonomia.js +5 -3
  115. package/hooks/lib/captura-acciones.js +2 -1
  116. package/hooks/lib/consolidation-lock.js +21 -10
  117. package/hooks/lib/etapa-auto-evolucion.js +10 -4
  118. package/hooks/lib/etapa-metricas.js +2 -1
  119. package/hooks/lib/etapa-perfil-usuario.js +20 -4
  120. package/hooks/lib/evolution-tracker.js +2 -1
  121. package/hooks/lib/gateway-notify.js +17 -3
  122. package/hooks/lib/loop-telemetry.js +5 -4
  123. package/hooks/lib/mcp-health.js +2 -1
  124. package/hooks/lib/memory-search.js +4 -0
  125. package/hooks/lib/merkle-audit.js +58 -6
  126. package/hooks/lib/notificacion-formato.js +58 -0
  127. package/hooks/lib/nudge-tracker.js +2 -1
  128. package/hooks/lib/otlp-exporter.js +2 -1
  129. package/hooks/lib/propose-step.js +3 -2
  130. package/hooks/lib/raiz-proyecto.js +127 -0
  131. package/hooks/lib/run-log.js +2 -1
  132. package/hooks/lib/singleton-guard.js +225 -27
  133. package/hooks/lib/telegram-cliente.js +28 -11
  134. package/hooks/notificacion-telegram.js +13 -3
  135. package/hooks/preservar-estado-pre-compact.js +2 -1
  136. package/hooks/proteccion-rutas.js +59 -3
  137. package/hooks/registro-turnos.js +2 -1
  138. package/hooks/resumen-sesion.js +2 -1
  139. package/hooks/risk-scoring.js +2 -1
  140. package/hooks/rotar-audit-auto.js +46 -20
  141. package/hooks/session-briefing.js +127 -1
  142. package/hooks/spec-gate.js +2 -1
  143. package/hooks/sugerir-contribuir.js +6 -3
  144. package/hooks/sugerir-regenerar-inventario.js +3 -2
  145. package/hooks/tdd-gate.js +2 -1
  146. package/hooks/telemetria-agentes.js +2 -1
  147. package/hooks/telemetria-skill-routing.js +2 -1
  148. package/hooks/tracking-costos.js +4 -3
  149. package/hooks/validar-formato-post-subagente.js +2 -1
  150. package/hooks/validar-intent-spec.js +2 -1
  151. package/hooks/validar-memoria-hook.js +13 -3
  152. package/hooks/validar-planning-paths.js +2 -1
  153. package/instintos/perfil-usuario.yaml +506 -3
  154. package/instintos/proyecto.yaml +78 -0
  155. package/llms.txt +29 -29
  156. package/manifiestos/canonical-hashes.json +5588 -4925
  157. package/manifiestos/hooks-config.json +469 -469
  158. package/manifiestos/invariantes-criticos.json +30 -30
  159. package/manifiestos/modulos.json +1429 -1423
  160. package/manifiestos/planning-paths.json +1 -0
  161. package/manifiestos/skills-lock.json +1275 -1275
  162. package/package.json +94 -95
  163. package/plugin.json +369 -369
  164. package/scripts/actualizar.js +3 -0
  165. package/scripts/auditar-clases-conocidas.js +134 -106
  166. package/scripts/benchmark-memoria.js +1 -0
  167. package/scripts/bootstrap-instintos.js +85 -14
  168. package/scripts/canario-hooks.js +166 -166
  169. package/scripts/cli/autonomia.js +23 -0
  170. package/scripts/cli/benchmark-memoria.js +37 -0
  171. package/scripts/cli/ciclo-autonomo.js +73 -0
  172. package/scripts/cli/ciclo-fase-b.js +102 -0
  173. package/scripts/cli/guardrail-metrics.js +39 -0
  174. package/scripts/cli/loop-telemetry.js +4 -2
  175. package/scripts/cli/memoria-search.js +69 -0
  176. package/scripts/cli/nudge-accionar.js +39 -0
  177. package/scripts/cli/run-eval.js +38 -0
  178. package/scripts/cli/run-skill-evals.js +13 -2
  179. package/scripts/derivar-feature-list.js +15 -14
  180. package/scripts/desinstalar.js +11 -0
  181. package/scripts/doctor.js +50 -13
  182. package/scripts/evidencia-valor.js +101 -101
  183. package/scripts/field-report.js +16 -16
  184. package/scripts/instalador.js +98 -7
  185. package/scripts/lib/activar-hooks-proyecto.js +116 -104
  186. package/scripts/lib/auditar-invocaciones-comandos.js +96 -6
  187. package/scripts/lib/ciclo-autonomo/candidatos.js +174 -0
  188. package/scripts/lib/ciclo-autonomo/config.js +165 -0
  189. package/scripts/lib/ciclo-autonomo/drenador-feedback.js +174 -0
  190. package/scripts/lib/ciclo-autonomo/fallback.js +77 -0
  191. package/scripts/lib/ciclo-autonomo/guard-convivencia.js +139 -0
  192. package/scripts/lib/ciclo-autonomo/higiene-nudges.js +112 -0
  193. package/scripts/lib/ciclo-autonomo/index.js +301 -0
  194. package/scripts/lib/ciclo-autonomo/lock.js +124 -0
  195. package/scripts/lib/ciclo-autonomo/presupuesto.js +122 -0
  196. package/scripts/lib/ciclo-autonomo/puente-degradacion.js +240 -0
  197. package/scripts/lib/ciclo-autonomo/runner-fase-b.js +248 -0
  198. package/scripts/lib/ciclo-autonomo/writer-instintos.js +190 -0
  199. package/scripts/lib/ciclo-autonomo/yaml-instintos.js +535 -0
  200. package/scripts/lib/estado.js +9 -0
  201. package/scripts/lib/evidencia-valor.js +228 -228
  202. package/scripts/lib/expandir-targets.js +71 -71
  203. package/scripts/lib/gitignore-manifest.js +8 -1
  204. package/scripts/lib/hooks-settings.js +45 -0
  205. package/scripts/lib/limpiar-basura-global.js +161 -0
  206. package/scripts/lib/toml-merge.js +204 -204
  207. package/scripts/mcp-server/auth.js +105 -105
  208. package/scripts/mcp-server/cache.js +106 -106
  209. package/scripts/rotar-audit-logs.js +48 -2
  210. package/scripts/run-eval.js +1 -0
  211. package/scripts/run-skill-evals.js +287 -8
  212. package/scripts/smoke-test.js +16 -8
  213. package/scripts/tui/pantallas/install-wizard.js +69 -13
  214. package/scripts/validar.js +40 -1
@@ -1,228 +1,228 @@
1
- 'use strict';
2
-
3
- /**
4
- * evidencia-valor.js — Evidencia de valor del harness EN CAMPO (downstream).
5
- *
6
- * En un proyecto real no hay A/B (nadie desarrolla la misma feature dos
7
- * veces); la evidencia creíble son HECHOS producidos por actores
8
- * independientes del sistema, nunca scores autodeclarados (regla
9
- * anti-gaming de seguridad-agentes). Cuatro fuentes, cada una con
10
- * procedencia explícita:
11
- *
12
- * 1. Intercepciones de gates — eventos durables de `.planning/evolution/
13
- * nudges.jsonl` (hook disparó una intervención). Evidencia directa:
14
- * cada bloqueo/alerta con causa real es un defecto prevenido contable.
15
- * 2. Presión de defectos — git: ratio commits fix/feat en la ventana
16
- * + fixes POSTERIORES a la última verificación de fase (escape rate
17
- * aproximado — la métrica que CASTIGA al harness). Proxy honesto.
18
- * 3. DORA-lite + CI — reusa scripts/lib/metricas-dora.js
19
- * (deployment frequency, lead time, change failure rate, MTTR vía gh).
20
- * Producido por git/GitHub, no por el sistema.
21
- * 4. Uso del sistema — .planning/skill-metrics.json. Solo
22
- * contexto (correlación, jamás causalidad).
23
- *
24
- * FRONTERA DE DATOS (documentada en docs/evidencia-valor.md): TODO se lee
25
- * LOCAL, del `.planning/` y el git del propio proyecto downstream donde
26
- * corre el comando. El repo madre NUNCA lee estos datos; el único canal
27
- * hacia el autor es el field-report opt-in con agregados anónimos.
28
- *
29
- * Zero-deps. Funciones puras respecto a raíz/inyecciones para testeo.
30
- */
31
-
32
- const fs = require('fs');
33
- const path = require('path');
34
- const { execFileSync } = require('child_process');
35
-
36
- const DIA_MS = 24 * 60 * 60 * 1000;
37
-
38
- /** Tipos de nudge que constituyen una intervención del sistema (evidencia). */
39
- const TIPOS_INTERVENCION = new Set([
40
- 'bloqueo', 'bloqueo-hook', 'secreto-detectado', 'calidad-pre-commit',
41
- 'drift-detectado', 'recovery-activado', 'riesgo-alto', 'proteccion-rutas',
42
- 'prompt-injection', 'veto', 'gate-fallido',
43
- ]);
44
-
45
- // ─── 1. Intercepciones (nudges.jsonl) ────────────────────────────────────────
46
-
47
- /**
48
- * Diagnóstico de POR QUÉ no hay telemetría: los hooks solo se registran a
49
- * nivel PROYECTO (ADR-0009 — un install --global copia los archivos pero NO
50
- * los registra en ~/.claude/settings.json, para no correr 49 hooks en cada
51
- * sesión de cualquier proyecto). Sin registro local, nudges.jsonl y
52
- * skill-metrics.json jamás aparecerán — el panel debe decir CÓMO activarlo,
53
- * no solo "no disponible" (reporte de campo SIGM, 2026-07-09).
54
- */
55
- function hooksRegistradosEnProyecto(raiz) {
56
- try {
57
- const s = JSON.parse(fs.readFileSync(path.join(raiz, '.claude', 'settings.json'), 'utf8'));
58
- return Object.values(s.hooks || {}).some(m => Array.isArray(m) && m.length > 0);
59
- } catch { return false; }
60
- }
61
-
62
- const HINT_ACTIVACION = 'hooks sin registrar a nivel proyecto (el install --global no los registra por diseño, ADR-0009) — activar SIN duplicar componentes con: npx -y @saulwade/swl-ses@latest install --target claude --solo-hooks (en la raíz del proyecto)';
63
-
64
- function leerNudges(raiz, { ventanaDias = 30, hoy = new Date() } = {}) {
65
- const ruta = path.join(raiz, '.planning', 'evolution', 'nudges.jsonl');
66
- if (!fs.existsSync(ruta)) {
67
- const razon = hooksRegistradosEnProyecto(raiz)
68
- ? 'sin nudges.jsonl aún (hooks registrados; se acumula con el uso de sesiones)'
69
- : HINT_ACTIVACION;
70
- return { disponible: false, razon, eventos: [] };
71
- }
72
- const corte = hoy.getTime() - ventanaDias * DIA_MS;
73
- const eventos = [];
74
- for (const linea of fs.readFileSync(ruta, 'utf8').split(/\r?\n/)) {
75
- if (!linea.trim()) continue;
76
- try {
77
- const e = JSON.parse(linea);
78
- const t = Date.parse(e.timestamp || e.ts || '');
79
- if (!Number.isNaN(t) && t >= corte) eventos.push(e);
80
- } catch { /* línea corrupta: se omite, no revienta el panel */ }
81
- }
82
- return { disponible: true, eventos };
83
- }
84
-
85
- function intercepciones(raiz, opts = {}) {
86
- const { disponible, razon, eventos } = leerNudges(raiz, opts);
87
- if (!disponible) return { disponible, razon, procedencia: 'hook-event (nudges.jsonl)', total: 0, porTipo: {} };
88
- const porTipo = {};
89
- let intervenciones = 0;
90
- for (const e of eventos) {
91
- const tipo = String(e.tipo || 'desconocido');
92
- porTipo[tipo] = (porTipo[tipo] || 0) + 1;
93
- if (TIPOS_INTERVENCION.has(tipo) || e.blocking === true || e.estado === 'critico') intervenciones++;
94
- }
95
- return {
96
- disponible: true,
97
- procedencia: 'hook-event (nudges.jsonl)',
98
- caracter: 'evidencia',
99
- totalEventos: eventos.length,
100
- intervenciones,
101
- porTipo,
102
- };
103
- }
104
-
105
- // ─── 2. Presión de defectos (git) ────────────────────────────────────────────
106
-
107
- function ejecutarGit(raiz, args) {
108
- return execFileSync('git', args, { cwd: raiz, encoding: 'utf8' });
109
- }
110
-
111
- function presionDefectos(raiz, { ventanaDias = 30, hoy = new Date(), git = ejecutarGit } = {}) {
112
- let sujetos;
113
- try {
114
- const desde = new Date(hoy.getTime() - ventanaDias * DIA_MS).toISOString();
115
- sujetos = git(raiz, ['log', `--since=${desde}`, '--pretty=format:%ad|%s', '--date=iso-strict'])
116
- .split(/\r?\n/).filter(Boolean);
117
- } catch (err) {
118
- return { disponible: false, razon: `git no disponible: ${err.message.split(/\r?\n/)[0]}` };
119
- }
120
- const esFix = s => /^(fix|hotfix)(\(|!|:)/.test(s);
121
- const esFeat = s => /^feat(\(|!|:)/.test(s);
122
- const fixes = sujetos.filter(l => esFix(l.split('|').slice(1).join('|')));
123
- const feats = sujetos.filter(l => esFeat(l.split('|').slice(1).join('|')));
124
-
125
- // Escape aproximado: fixes con fecha POSTERIOR a la última verificación de
126
- // fase (VERIFICACION.md más reciente). Sin fases verificadas → no aplica.
127
- // La fecha se toma de GIT (autoría del último commit que tocó el archivo),
128
- // no del mtime del filesystem: tras un clone/checkout todos los archivos
129
- // comparten el mtime de la operación y el proxy daba falso negativo
130
- // (reporte de campo SIGM, 2026-07-09). Fallback a mtime fuera de git.
131
- let escapes = { disponible: false, razon: 'sin VERIFICACION.md en .planning/fases/ (proyecto sin ciclo GSD verificado)' };
132
- try {
133
- const dirFases = path.join(raiz, '.planning', 'fases');
134
- let ultimaVerificacion = 0;
135
- if (fs.existsSync(dirFases)) {
136
- for (const fase of fs.readdirSync(dirFases)) {
137
- const v = path.join(dirFases, fase, 'VERIFICACION.md');
138
- if (!fs.existsSync(v)) continue;
139
- let fecha = 0;
140
- try {
141
- const rel = path.relative(raiz, v);
142
- const ct = parseInt(git(raiz, ['log', '-1', '--format=%ct', '--', rel]).trim(), 10);
143
- if (Number.isFinite(ct) && ct > 0) fecha = ct * 1000;
144
- } catch { /* fuera de git o archivo sin commits → fallback mtime */ }
145
- if (fecha === 0) fecha = fs.statSync(v).mtimeMs;
146
- ultimaVerificacion = Math.max(ultimaVerificacion, fecha);
147
- }
148
- }
149
- if (ultimaVerificacion > 0) {
150
- const post = fixes.filter(l => {
151
- const t = Date.parse(l.split('|')[0]);
152
- return !Number.isNaN(t) && t > ultimaVerificacion;
153
- });
154
- escapes = {
155
- disponible: true,
156
- fixesTrasUltimaVerificacion: post.length,
157
- ultimaVerificacion: new Date(ultimaVerificacion).toLocaleDateString('sv'),
158
- nota: 'lo que el harness NO atrapó — la métrica que lo castiga',
159
- };
160
- }
161
- } catch { /* best-effort */ }
162
-
163
- return {
164
- disponible: true,
165
- procedencia: 'git log (independiente del sistema)',
166
- caracter: 'proxy',
167
- commits: sujetos.length,
168
- fixes: fixes.length,
169
- feats: feats.length,
170
- ratioFixFeat: feats.length > 0 ? +(fixes.length / feats.length).toFixed(2) : null,
171
- escapes,
172
- };
173
- }
174
-
175
- // ─── 3 y 4. DORA/CI y uso (reuso de fuentes existentes) ─────────────────────
176
-
177
- function doraCi(raiz, { ventanaDias = 30, hoy = new Date() } = {}) {
178
- try {
179
- const { calcularDora } = require('./metricas-dora.js');
180
- const d = calcularDora(raiz, { ventanaDias, hoy });
181
- return { disponible: true, procedencia: 'git + GitHub Actions (gh)', caracter: 'evidencia (tendencia; correlación, no causalidad)', dora: d };
182
- } catch (err) {
183
- return { disponible: false, razon: `metricas-dora no disponible: ${err.message.split(/\r?\n/)[0]}` };
184
- }
185
- }
186
-
187
- function usoSistema(raiz) {
188
- const ruta = path.join(raiz, '.planning', 'skill-metrics.json');
189
- if (!fs.existsSync(ruta)) {
190
- const razon = hooksRegistradosEnProyecto(raiz)
191
- ? 'sin skill-metrics.json aún (hooks registrados; se acumula con el uso de skills)'
192
- : HINT_ACTIVACION;
193
- return { disponible: false, razon };
194
- }
195
- try {
196
- // Forma real: { version, entries: [{skill, estado, timestamp}, ...] }
197
- // (eventos individuales del hook telemetria-skill-routing) — agregamos aquí.
198
- const m = JSON.parse(fs.readFileSync(ruta, 'utf8'));
199
- const eventos = Object.values(m.entries || {});
200
- const porSkill = {};
201
- for (const e of eventos) {
202
- if (!e || !e.skill) continue;
203
- porSkill[e.skill] = (porSkill[e.skill] || 0) + 1;
204
- }
205
- const top = Object.entries(porSkill).sort((a, b) => b[1] - a[1]).slice(0, 5)
206
- .map(([k, n]) => `${k}:${n}`);
207
- return { disponible: true, procedencia: 'telemetria-skill-routing', caracter: 'contexto (solo correlación)', invocaciones: eventos.length, top5: top };
208
- } catch (err) {
209
- return { disponible: false, razon: `skill-metrics ilegible: ${err.message}` };
210
- }
211
- }
212
-
213
- // ─── Panel ────────────────────────────────────────────────────────────────────
214
-
215
- function panelEvidencia(raiz = process.cwd(), opts = {}) {
216
- const ventanaDias = opts.ventanaDias || 30;
217
- return {
218
- generado: new Date().toLocaleDateString('sv'),
219
- ventanaDias,
220
- frontera: 'Datos 100% locales del proyecto (.planning/ + git). El repo madre no lee nada de aquí (docs/evidencia-valor.md).',
221
- intercepciones: intercepciones(raiz, { ventanaDias, hoy: opts.hoy }),
222
- defectos: presionDefectos(raiz, { ventanaDias, hoy: opts.hoy, git: opts.git }),
223
- doraCi: doraCi(raiz, { ventanaDias, hoy: opts.hoy }),
224
- uso: usoSistema(raiz),
225
- };
226
- }
227
-
228
- module.exports = { panelEvidencia, intercepciones, presionDefectos, leerNudges, usoSistema, TIPOS_INTERVENCION };
1
+ 'use strict';
2
+
3
+ /**
4
+ * evidencia-valor.js — Evidencia de valor del harness EN CAMPO (downstream).
5
+ *
6
+ * En un proyecto real no hay A/B (nadie desarrolla la misma feature dos
7
+ * veces); la evidencia creíble son HECHOS producidos por actores
8
+ * independientes del sistema, nunca scores autodeclarados (regla
9
+ * anti-gaming de seguridad-agentes). Cuatro fuentes, cada una con
10
+ * procedencia explícita:
11
+ *
12
+ * 1. Intercepciones de gates — eventos durables de `.planning/evolution/
13
+ * nudges.jsonl` (hook disparó una intervención). Evidencia directa:
14
+ * cada bloqueo/alerta con causa real es un defecto prevenido contable.
15
+ * 2. Presión de defectos — git: ratio commits fix/feat en la ventana
16
+ * + fixes POSTERIORES a la última verificación de fase (escape rate
17
+ * aproximado — la métrica que CASTIGA al harness). Proxy honesto.
18
+ * 3. DORA-lite + CI — reusa scripts/lib/metricas-dora.js
19
+ * (deployment frequency, lead time, change failure rate, MTTR vía gh).
20
+ * Producido por git/GitHub, no por el sistema.
21
+ * 4. Uso del sistema — .planning/skill-metrics.json. Solo
22
+ * contexto (correlación, jamás causalidad).
23
+ *
24
+ * FRONTERA DE DATOS (documentada en docs/evidencia-valor.md): TODO se lee
25
+ * LOCAL, del `.planning/` y el git del propio proyecto downstream donde
26
+ * corre el comando. El repo madre NUNCA lee estos datos; el único canal
27
+ * hacia el autor es el field-report opt-in con agregados anónimos.
28
+ *
29
+ * Zero-deps. Funciones puras respecto a raíz/inyecciones para testeo.
30
+ */
31
+
32
+ const fs = require('fs');
33
+ const path = require('path');
34
+ const { execFileSync } = require('child_process');
35
+
36
+ const DIA_MS = 24 * 60 * 60 * 1000;
37
+
38
+ /** Tipos de nudge que constituyen una intervención del sistema (evidencia). */
39
+ const TIPOS_INTERVENCION = new Set([
40
+ 'bloqueo', 'bloqueo-hook', 'secreto-detectado', 'calidad-pre-commit',
41
+ 'drift-detectado', 'recovery-activado', 'riesgo-alto', 'proteccion-rutas',
42
+ 'prompt-injection', 'veto', 'gate-fallido',
43
+ ]);
44
+
45
+ // ─── 1. Intercepciones (nudges.jsonl) ────────────────────────────────────────
46
+
47
+ /**
48
+ * Diagnóstico de POR QUÉ no hay telemetría: los hooks solo se registran a
49
+ * nivel PROYECTO (ADR-0009 — un install --global copia los archivos pero NO
50
+ * los registra en ~/.claude/settings.json, para no correr 49 hooks en cada
51
+ * sesión de cualquier proyecto). Sin registro local, nudges.jsonl y
52
+ * skill-metrics.json jamás aparecerán — el panel debe decir CÓMO activarlo,
53
+ * no solo "no disponible" (reporte de campo SIGM, 2026-07-09).
54
+ */
55
+ function hooksRegistradosEnProyecto(raiz) {
56
+ try {
57
+ const s = JSON.parse(fs.readFileSync(path.join(raiz, '.claude', 'settings.json'), 'utf8'));
58
+ return Object.values(s.hooks || {}).some(m => Array.isArray(m) && m.length > 0);
59
+ } catch { return false; }
60
+ }
61
+
62
+ const HINT_ACTIVACION = 'hooks sin registrar a nivel proyecto (el install --global no los registra por diseño, ADR-0009) — activar SIN duplicar componentes con: npx -y @saulwade/swl-ses@latest install --target claude --solo-hooks (en la raíz del proyecto)';
63
+
64
+ function leerNudges(raiz, { ventanaDias = 30, hoy = new Date() } = {}) {
65
+ const ruta = path.join(raiz, '.planning', 'evolution', 'nudges.jsonl');
66
+ if (!fs.existsSync(ruta)) {
67
+ const razon = hooksRegistradosEnProyecto(raiz)
68
+ ? 'sin nudges.jsonl aún (hooks registrados; se acumula con el uso de sesiones)'
69
+ : HINT_ACTIVACION;
70
+ return { disponible: false, razon, eventos: [] };
71
+ }
72
+ const corte = hoy.getTime() - ventanaDias * DIA_MS;
73
+ const eventos = [];
74
+ for (const linea of fs.readFileSync(ruta, 'utf8').split(/\r?\n/)) {
75
+ if (!linea.trim()) continue;
76
+ try {
77
+ const e = JSON.parse(linea);
78
+ const t = Date.parse(e.timestamp || e.ts || '');
79
+ if (!Number.isNaN(t) && t >= corte) eventos.push(e);
80
+ } catch { /* línea corrupta: se omite, no revienta el panel */ }
81
+ }
82
+ return { disponible: true, eventos };
83
+ }
84
+
85
+ function intercepciones(raiz, opts = {}) {
86
+ const { disponible, razon, eventos } = leerNudges(raiz, opts);
87
+ if (!disponible) return { disponible, razon, procedencia: 'hook-event (nudges.jsonl)', total: 0, porTipo: {} };
88
+ const porTipo = {};
89
+ let intervenciones = 0;
90
+ for (const e of eventos) {
91
+ const tipo = String(e.tipo || 'desconocido');
92
+ porTipo[tipo] = (porTipo[tipo] || 0) + 1;
93
+ if (TIPOS_INTERVENCION.has(tipo) || e.blocking === true || e.estado === 'critico') intervenciones++;
94
+ }
95
+ return {
96
+ disponible: true,
97
+ procedencia: 'hook-event (nudges.jsonl)',
98
+ caracter: 'evidencia',
99
+ totalEventos: eventos.length,
100
+ intervenciones,
101
+ porTipo,
102
+ };
103
+ }
104
+
105
+ // ─── 2. Presión de defectos (git) ────────────────────────────────────────────
106
+
107
+ function ejecutarGit(raiz, args) {
108
+ return execFileSync('git', args, { cwd: raiz, encoding: 'utf8' });
109
+ }
110
+
111
+ function presionDefectos(raiz, { ventanaDias = 30, hoy = new Date(), git = ejecutarGit } = {}) {
112
+ let sujetos;
113
+ try {
114
+ const desde = new Date(hoy.getTime() - ventanaDias * DIA_MS).toISOString();
115
+ sujetos = git(raiz, ['log', `--since=${desde}`, '--pretty=format:%ad|%s', '--date=iso-strict'])
116
+ .split(/\r?\n/).filter(Boolean);
117
+ } catch (err) {
118
+ return { disponible: false, razon: `git no disponible: ${err.message.split(/\r?\n/)[0]}` };
119
+ }
120
+ const esFix = s => /^(fix|hotfix)(\(|!|:)/.test(s);
121
+ const esFeat = s => /^feat(\(|!|:)/.test(s);
122
+ const fixes = sujetos.filter(l => esFix(l.split('|').slice(1).join('|')));
123
+ const feats = sujetos.filter(l => esFeat(l.split('|').slice(1).join('|')));
124
+
125
+ // Escape aproximado: fixes con fecha POSTERIOR a la última verificación de
126
+ // fase (VERIFICACION.md más reciente). Sin fases verificadas → no aplica.
127
+ // La fecha se toma de GIT (autoría del último commit que tocó el archivo),
128
+ // no del mtime del filesystem: tras un clone/checkout todos los archivos
129
+ // comparten el mtime de la operación y el proxy daba falso negativo
130
+ // (reporte de campo SIGM, 2026-07-09). Fallback a mtime fuera de git.
131
+ let escapes = { disponible: false, razon: 'sin VERIFICACION.md en .planning/fases/ (proyecto sin ciclo GSD verificado)' };
132
+ try {
133
+ const dirFases = path.join(raiz, '.planning', 'fases');
134
+ let ultimaVerificacion = 0;
135
+ if (fs.existsSync(dirFases)) {
136
+ for (const fase of fs.readdirSync(dirFases)) {
137
+ const v = path.join(dirFases, fase, 'VERIFICACION.md');
138
+ if (!fs.existsSync(v)) continue;
139
+ let fecha = 0;
140
+ try {
141
+ const rel = path.relative(raiz, v);
142
+ const ct = parseInt(git(raiz, ['log', '-1', '--format=%ct', '--', rel]).trim(), 10);
143
+ if (Number.isFinite(ct) && ct > 0) fecha = ct * 1000;
144
+ } catch { /* fuera de git o archivo sin commits → fallback mtime */ }
145
+ if (fecha === 0) fecha = fs.statSync(v).mtimeMs;
146
+ ultimaVerificacion = Math.max(ultimaVerificacion, fecha);
147
+ }
148
+ }
149
+ if (ultimaVerificacion > 0) {
150
+ const post = fixes.filter(l => {
151
+ const t = Date.parse(l.split('|')[0]);
152
+ return !Number.isNaN(t) && t > ultimaVerificacion;
153
+ });
154
+ escapes = {
155
+ disponible: true,
156
+ fixesTrasUltimaVerificacion: post.length,
157
+ ultimaVerificacion: new Date(ultimaVerificacion).toLocaleDateString('sv'),
158
+ nota: 'lo que el harness NO atrapó — la métrica que lo castiga',
159
+ };
160
+ }
161
+ } catch { /* best-effort */ }
162
+
163
+ return {
164
+ disponible: true,
165
+ procedencia: 'git log (independiente del sistema)',
166
+ caracter: 'proxy',
167
+ commits: sujetos.length,
168
+ fixes: fixes.length,
169
+ feats: feats.length,
170
+ ratioFixFeat: feats.length > 0 ? +(fixes.length / feats.length).toFixed(2) : null,
171
+ escapes,
172
+ };
173
+ }
174
+
175
+ // ─── 3 y 4. DORA/CI y uso (reuso de fuentes existentes) ─────────────────────
176
+
177
+ function doraCi(raiz, { ventanaDias = 30, hoy = new Date() } = {}) {
178
+ try {
179
+ const { calcularDora } = require('./metricas-dora.js');
180
+ const d = calcularDora(raiz, { ventanaDias, hoy });
181
+ return { disponible: true, procedencia: 'git + GitHub Actions (gh)', caracter: 'evidencia (tendencia; correlación, no causalidad)', dora: d };
182
+ } catch (err) {
183
+ return { disponible: false, razon: `metricas-dora no disponible: ${err.message.split(/\r?\n/)[0]}` };
184
+ }
185
+ }
186
+
187
+ function usoSistema(raiz) {
188
+ const ruta = path.join(raiz, '.planning', 'skill-metrics.json');
189
+ if (!fs.existsSync(ruta)) {
190
+ const razon = hooksRegistradosEnProyecto(raiz)
191
+ ? 'sin skill-metrics.json aún (hooks registrados; se acumula con el uso de skills)'
192
+ : HINT_ACTIVACION;
193
+ return { disponible: false, razon };
194
+ }
195
+ try {
196
+ // Forma real: { version, entries: [{skill, estado, timestamp}, ...] }
197
+ // (eventos individuales del hook telemetria-skill-routing) — agregamos aquí.
198
+ const m = JSON.parse(fs.readFileSync(ruta, 'utf8'));
199
+ const eventos = Object.values(m.entries || {});
200
+ const porSkill = {};
201
+ for (const e of eventos) {
202
+ if (!e || !e.skill) continue;
203
+ porSkill[e.skill] = (porSkill[e.skill] || 0) + 1;
204
+ }
205
+ const top = Object.entries(porSkill).sort((a, b) => b[1] - a[1]).slice(0, 5)
206
+ .map(([k, n]) => `${k}:${n}`);
207
+ return { disponible: true, procedencia: 'telemetria-skill-routing', caracter: 'contexto (solo correlación)', invocaciones: eventos.length, top5: top };
208
+ } catch (err) {
209
+ return { disponible: false, razon: `skill-metrics ilegible: ${err.message}` };
210
+ }
211
+ }
212
+
213
+ // ─── Panel ────────────────────────────────────────────────────────────────────
214
+
215
+ function panelEvidencia(raiz = process.cwd(), opts = {}) {
216
+ const ventanaDias = opts.ventanaDias || 30;
217
+ return {
218
+ generado: new Date().toLocaleDateString('sv'),
219
+ ventanaDias,
220
+ frontera: 'Datos 100% locales del proyecto (.planning/ + git). El repo madre no lee nada de aquí (docs/evidencia-valor.md).',
221
+ intercepciones: intercepciones(raiz, { ventanaDias, hoy: opts.hoy }),
222
+ defectos: presionDefectos(raiz, { ventanaDias, hoy: opts.hoy, git: opts.git }),
223
+ doraCi: doraCi(raiz, { ventanaDias, hoy: opts.hoy }),
224
+ uso: usoSistema(raiz),
225
+ };
226
+ }
227
+
228
+ module.exports = { panelEvidencia, intercepciones, presionDefectos, leerNudges, usoSistema, TIPOS_INTERVENCION };
@@ -1,71 +1,71 @@
1
- 'use strict';
2
-
3
- /**
4
- * Expansión de --target (CSV) y --all-runtimes para multi-target install.
5
- *
6
- * ADR-0019 Sub-fase 2.5.
7
- *
8
- * Extraído de bin/swl-ses.js para permitir tests unitarios. NUNCA debe tener
9
- * side effects fuera de logging — solo manipula la lista de strings.
10
- *
11
- * @module scripts/lib/expandir-targets
12
- */
13
-
14
- const { listarRuntimesInstalables, RUNTIMES } = require('./detectar-runtime');
15
-
16
- /**
17
- * Expande las opciones del CLI a un array de target IDs.
18
- *
19
- * Reglas:
20
- * - `all_runtimes` tiene prioridad sobre `target`. Si ambos vienen, se loggea
21
- * aviso (vía `logger.warn`) y se usa `all_runtimes`.
22
- * - `target='a,b,c'` → ['a','b','c'].
23
- * - `target='claude'` → ['claude'].
24
- * - Sin nada → ['claude'] (default histórico, backward-compat).
25
- * - Duplicados se deduplican preservando el primer orden.
26
- * - Targets desconocidos se omiten con aviso (logger.warn).
27
- *
28
- * @param {object} opciones - Objeto con `target` (string|undefined) y `all_runtimes` (bool).
29
- * @param {object} [logger] - Sustituible para tests. Default console.
30
- * @returns {{ targets: string[], omitidos: string[], errores: string[] }}
31
- */
32
- function expandirTargets(opciones, logger) {
33
- const log = logger || console;
34
- const omitidos = [];
35
- const errores = [];
36
-
37
- let candidatos;
38
- if (opciones.all_runtimes) {
39
- if (opciones.target && typeof opciones.target === 'string') {
40
- log.warn && log.warn('[expandir-targets] --all-runtimes tiene prioridad sobre --target; se ignora --target.');
41
- }
42
- candidatos = listarRuntimesInstalables();
43
- } else if (typeof opciones.target === 'string' && opciones.target.includes(',')) {
44
- candidatos = opciones.target.split(',').map(s => s.trim()).filter(Boolean);
45
- } else if (typeof opciones.target === 'string' && opciones.target.length > 0) {
46
- candidatos = [opciones.target.trim()];
47
- } else {
48
- candidatos = ['claude']; // backward-compat
49
- }
50
-
51
- const visto = new Set();
52
- const targets = [];
53
- for (const t of candidatos) {
54
- if (visto.has(t)) continue;
55
- visto.add(t);
56
- if (!RUNTIMES[t]) {
57
- log.warn && log.warn(`[expandir-targets] Target desconocido omitido: "${t}". Disponibles: ${Object.keys(RUNTIMES).join(', ')}`);
58
- omitidos.push(t);
59
- continue;
60
- }
61
- targets.push(t);
62
- }
63
-
64
- if (targets.length === 0) {
65
- errores.push('Ningún target válido tras expansión.');
66
- }
67
-
68
- return { targets, omitidos, errores };
69
- }
70
-
71
- module.exports = { expandirTargets };
1
+ 'use strict';
2
+
3
+ /**
4
+ * Expansión de --target (CSV) y --all-runtimes para multi-target install.
5
+ *
6
+ * ADR-0019 Sub-fase 2.5.
7
+ *
8
+ * Extraído de bin/swl-ses.js para permitir tests unitarios. NUNCA debe tener
9
+ * side effects fuera de logging — solo manipula la lista de strings.
10
+ *
11
+ * @module scripts/lib/expandir-targets
12
+ */
13
+
14
+ const { listarRuntimesInstalables, RUNTIMES } = require('./detectar-runtime');
15
+
16
+ /**
17
+ * Expande las opciones del CLI a un array de target IDs.
18
+ *
19
+ * Reglas:
20
+ * - `all_runtimes` tiene prioridad sobre `target`. Si ambos vienen, se loggea
21
+ * aviso (vía `logger.warn`) y se usa `all_runtimes`.
22
+ * - `target='a,b,c'` → ['a','b','c'].
23
+ * - `target='claude'` → ['claude'].
24
+ * - Sin nada → ['claude'] (default histórico, backward-compat).
25
+ * - Duplicados se deduplican preservando el primer orden.
26
+ * - Targets desconocidos se omiten con aviso (logger.warn).
27
+ *
28
+ * @param {object} opciones - Objeto con `target` (string|undefined) y `all_runtimes` (bool).
29
+ * @param {object} [logger] - Sustituible para tests. Default console.
30
+ * @returns {{ targets: string[], omitidos: string[], errores: string[] }}
31
+ */
32
+ function expandirTargets(opciones, logger) {
33
+ const log = logger || console;
34
+ const omitidos = [];
35
+ const errores = [];
36
+
37
+ let candidatos;
38
+ if (opciones.all_runtimes) {
39
+ if (opciones.target && typeof opciones.target === 'string') {
40
+ log.warn && log.warn('[expandir-targets] --all-runtimes tiene prioridad sobre --target; se ignora --target.');
41
+ }
42
+ candidatos = listarRuntimesInstalables();
43
+ } else if (typeof opciones.target === 'string' && opciones.target.includes(',')) {
44
+ candidatos = opciones.target.split(',').map(s => s.trim()).filter(Boolean);
45
+ } else if (typeof opciones.target === 'string' && opciones.target.length > 0) {
46
+ candidatos = [opciones.target.trim()];
47
+ } else {
48
+ candidatos = ['claude']; // backward-compat
49
+ }
50
+
51
+ const visto = new Set();
52
+ const targets = [];
53
+ for (const t of candidatos) {
54
+ if (visto.has(t)) continue;
55
+ visto.add(t);
56
+ if (!RUNTIMES[t]) {
57
+ log.warn && log.warn(`[expandir-targets] Target desconocido omitido: "${t}". Disponibles: ${Object.keys(RUNTIMES).join(', ')}`);
58
+ omitidos.push(t);
59
+ continue;
60
+ }
61
+ targets.push(t);
62
+ }
63
+
64
+ if (targets.length === 0) {
65
+ errores.push('Ningún target válido tras expansión.');
66
+ }
67
+
68
+ return { targets, omitidos, errores };
69
+ }
70
+
71
+ module.exports = { expandirTargets };
@@ -76,6 +76,11 @@ const ENTRADAS_BASE = [
76
76
  // singleton-guard, actualizar-perfil-usuario). Output de runtime, no
77
77
  // artefactos del proyecto usuario.
78
78
  ".planning/evolution/",
79
+ // Shim de migración: instalaciones pre-ADR-0031 (dirs runtime en español)
80
+ // aún escriben aquí hasta que el usuario corra `update` (caso real
81
+ // sistema-verificacion-oic 2026-07-10 con nudges/agent-commits en
82
+ // .planning/evolucion/).
83
+ ".planning/evolucion/",
79
84
  ".planning/auto-evolution/",
80
85
  // locks/: contenido runtime ignorado (singleton-guard {pid,ts},
81
86
  // fase-activa.json) EXCEPTO los plan-locks de G1 (*PLAN.md.lock), que son
@@ -102,7 +107,9 @@ const ENTRADAS_BASE = [
102
107
  // destino vs lo que ENTRADAS_BASE propagaba. Todo esto es output de runtime,
103
108
  // regenerable, no editado por el equipo → no se commitea.
104
109
  ".planning/feature-list.json", // derivar-feature-list.js (derivado de HOJA-RUTA.md)
105
- ".planning/loops/", // loop-telemetry.js (/swl:verificar, /swl:status loops)
110
+ // .planning/loops/ NO se ignora (decisión del usuario 2026-07-10): el
111
+ // historial de loops (iteraciones.tsv de loop-telemetry) es evidencia
112
+ // portable del trabajo — el equipo decide commitearlo como memoria.
106
113
  ".planning/archive/", // rotar-audit-auto.js (audit/logs rotados y comprimidos)
107
114
  ".planning/analysis/", // outputs de análisis
108
115
  ".planning/graph.json", // code-review-graph (cache del grafo)