@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
@@ -226,6 +226,9 @@ async function update(opciones) {
226
226
  profile: estado.perfil,
227
227
  global: estado.global,
228
228
  force: true,
229
+ // Módulos opt-in registrados por --with en installs previos
230
+ // (DT-MODULOS-OPT-IN): sin re-pasarlos, el update los perdía.
231
+ with: (estado.modulosExtra || []).join(',') || undefined,
229
232
  });
230
233
 
231
234
  sp.exito(`${runtime.nombre} actualizado a v${VERSION} (${estado.archivosInstalados.length} archivos)`);
@@ -1,106 +1,134 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * auditar-clases-conocidas.js — Gate de clases de bug ya documentadas.
6
- *
7
- * "El conocimiento documentado no previene — solo los gates previenen."
8
- * Cada clase de este gate mordió al proyecto en producción pese a estar
9
- * escrita como regla/aprendizaje. El gate la vuelve imposible de reintroducir:
10
- *
11
- * C1 — Fecha UTC truncada: toISOString + slice(0,10) fecha artefactos
12
- * "mañana" desde México (23 instancias corregidas 2026-07-08/09).
13
- * Correcto: `toLocaleDateString('sv')`. Escape: `// fecha-utc-ok`.
14
- * (El patrón no se escribe literal aquí: el gate escanea este archivo
15
- * también — se auto-detectó en el publish v2.5.1.)
16
- * C2 — Comparación byte a byte en modos --check: comparar texto leído de
17
- * disco sin normalizar EOL revienta con CRLF tras checkout/reset de
18
- * git (tumbó el publish v2.5.0). Un script con modo --check que lee
19
- * texto debe normalizar `\r\n` o comparar estructuras (JSON.parse).
20
- * Escape: `// eol-ok: <razón>`.
21
- * C3 — Bloqueo ciego en hooks: exit(2) sin console.error deja al modelo
22
- * sin la razón del bloqueo (el canal de exit 2 es stderr).
23
- *
24
- * Alcance: archivos .js versionados en scripts/, hooks/, bin/, gateway/
25
- * (tests/ excluido: los tests DEBEN poder citar el patrón para validarlo).
26
- * Exit: 0 limpio · 1 con hallazgos.
27
- */
28
-
29
- const { execFileSync } = require('child_process');
30
- const fs = require('fs');
31
- const path = require('path');
32
-
33
- const RAIZ = path.resolve(__dirname, '..');
34
-
35
- const RX_UTC = /toISOString\(\)\s*\.\s*(?:slice|substring)\s*\(\s*0\s*,\s*10\s*\)/;
36
- const RX_ESCAPE_UTC = /fecha-utc-ok/;
37
- const RX_ESCAPE_EOL = /eol-ok:/;
38
-
39
- function archivosObjetivo() {
40
- const salida = execFileSync('git', [
41
- 'ls-files', '--',
42
- 'scripts/*.js', 'scripts/**/*.js',
43
- 'hooks/*.js', 'hooks/**/*.js',
44
- 'bin/*.js',
45
- 'gateway/*.js', 'gateway/**/*.js',
46
- ], { cwd: RAIZ, encoding: 'utf8' });
47
- return salida.split(/\r?\n/).filter(f => f && !f.includes('vendor/'));
48
- }
49
-
50
- function auditarUtc(ruta, lineas, hallazgos) {
51
- lineas.forEach((linea, i) => {
52
- if (RX_UTC.test(linea) && !RX_ESCAPE_UTC.test(linea)) {
53
- hallazgos.push(`[C1 fecha-UTC] ${ruta}:${i + 1} — toISOString().slice(0,10) fecha "mañana" desde UTC-6; usar toLocaleDateString('sv') o marcar \`// fecha-utc-ok\` si la semántica UTC es deliberada`);
54
- }
55
- });
56
- }
57
-
58
- function auditarEolCheck(ruta, contenido, hallazgos) {
59
- // Aplica solo a GENERADORES con modo --check (escriben el artefacto y en
60
- // check comparan lo regenerado contra disco). Un --check selector de
61
- // sub-checks (verificar-docs) no compara texto por igualdad — no aplica.
62
- if (!contenido.includes("'--check'") && !contenido.includes('"--check"')) return;
63
- if (!/atomicWrite|writeFileSync/.test(contenido)) return;
64
- if (!/readFileSync\([^)]*utf-?8/i.test(contenido)) return;
65
- const normaliza = contenido.includes('\\r\\n') || contenido.includes('\\r?\\n');
66
- const comparaEstructura = contenido.includes('JSON.parse');
67
- const escape = RX_ESCAPE_EOL.test(contenido);
68
- if (!normaliza && !comparaEstructura && !escape) {
69
- hallazgos.push(`[C2 EOL] ${ruta} — modo --check que lee texto sin normalizar \\r\\n ni comparar estructuras: reventará con CRLF tras checkout/reset de git (caso publish v2.5.0). Normalizar EOL antes de comparar o marcar \`// eol-ok: <razón>\``);
70
- }
71
- }
72
-
73
- function auditarBloqueoCiego(ruta, contenido, hallazgos) {
74
- if (!ruta.startsWith('hooks/') || ruta.startsWith('hooks/lib/')) return;
75
- const usaExit2 = /exit\(2\)|exitCode\s*=\s*2/.test(contenido);
76
- const escribeStderr = contenido.includes('console.error') || contenido.includes('process.stderr.write');
77
- if (usaExit2 && !escribeStderr) {
78
- hallazgos.push(`[C3 bloqueo-ciego] ${ruta} usa exit 2 sin escribir a stderr (console.error/process.stderr.write): el modelo recibe el bloqueo sin la razón`);
79
- }
80
- }
81
-
82
- function main() {
83
- const hallazgos = [];
84
- const archivos = archivosObjetivo();
85
- for (const ruta of archivos) {
86
- let contenido;
87
- try {
88
- contenido = fs.readFileSync(path.join(RAIZ, ruta), 'utf8');
89
- } catch { continue; }
90
- const lineas = contenido.split(/\r?\n/);
91
- auditarUtc(ruta, lineas, hallazgos);
92
- auditarEolCheck(ruta, contenido, hallazgos);
93
- auditarBloqueoCiego(ruta, contenido, hallazgos);
94
- }
95
-
96
- if (hallazgos.length === 0) {
97
- console.log(`[clases-conocidas] OK — ${archivos.length} archivos, 0 reincidencias de clases documentadas (C1 UTC, C2 EOL, C3 bloqueo ciego)`);
98
- return;
99
- }
100
- console.error(`[clases-conocidas] ${hallazgos.length} reincidencia(s) de clases de bug documentadas:`);
101
- for (const h of hallazgos) console.error(' ' + h);
102
- process.exitCode = 1;
103
- }
104
-
105
- module.exports = { auditarUtc, auditarEolCheck, auditarBloqueoCiego };
106
- if (require.main === module) main();
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * auditar-clases-conocidas.js — Gate de clases de bug ya documentadas.
6
+ *
7
+ * "El conocimiento documentado no previene — solo los gates previenen."
8
+ * Cada clase de este gate mordió al proyecto en producción pese a estar
9
+ * escrita como regla/aprendizaje. El gate la vuelve imposible de reintroducir:
10
+ *
11
+ * C1 — Fecha UTC truncada: toISOString + slice(0,10) fecha artefactos
12
+ * "mañana" desde México (23 instancias corregidas 2026-07-08/09).
13
+ * Correcto: `toLocaleDateString('sv')`. Escape: `// fecha-utc-ok`.
14
+ * (El patrón no se escribe literal aquí: el gate escanea este archivo
15
+ * también — se auto-detectó en el publish v2.5.1.)
16
+ * C2 — Comparación byte a byte en modos --check: comparar texto leído de
17
+ * disco sin normalizar EOL revienta con CRLF tras checkout/reset de
18
+ * git (tumbó el publish v2.5.0). Un script con modo --check que lee
19
+ * texto debe normalizar `\r\n` o comparar estructuras (JSON.parse).
20
+ * Escape: `// eol-ok: <razón>`.
21
+ * C3 — Bloqueo ciego en hooks: exit(2) sin console.error deja al modelo
22
+ * sin la razón del bloqueo (el canal de exit 2 es stderr).
23
+ * C4 — Ancla .planning/instintos al CWD crudo en hooks: en monorepos con
24
+ * sesión abierta en un subdirectorio fragmenta la memoria del proyecto
25
+ * en árboles huérfanos (DT-HOOKS-RAIZ-GIT, reportes de campo oic+sigm
26
+ * 2026-07-10; flota completa migrada el mismo día). Correcto:
27
+ * `raizProyecto(process.cwd())` de hooks/lib/raiz-proyecto.js.
28
+ * Escape: `// raiz-ok: <razón>`. Solo hooks/** — los scripts CLI se
29
+ * invocan desde la raíz del proyecto por contrato, y el instalador
30
+ * ancla al cwd DELIBERADAMENTE (instala hacia ese proyecto; saltar a
31
+ * un repo padre sería un bug).
32
+ *
33
+ * Alcance: archivos .js versionados en scripts/, hooks/, bin/, gateway/ y
34
+ * los scripts ejecutables de skills (habilidades/<skill>/scripts/) —
35
+ * parse-commits.js escapó al gate C1 hasta 2026-07-10 por no estar en el alcance.
36
+ * (tests/ excluido: los tests DEBEN poder citar el patrón para validarlo.)
37
+ * Exit: 0 limpio · 1 con hallazgos.
38
+ */
39
+
40
+ const { execFileSync } = require('child_process');
41
+ const fs = require('fs');
42
+ const path = require('path');
43
+
44
+ const RAIZ = path.resolve(__dirname, '..');
45
+
46
+ const RX_UTC = /toISOString\(\)\s*\.\s*(?:slice|substring)\s*\(\s*0\s*,\s*10\s*\)/;
47
+ const RX_ESCAPE_UTC = /fecha-utc-ok/;
48
+ const RX_ESCAPE_EOL = /eol-ok:/;
49
+ // C4: join directo del cwd crudo con .planning o instintos. El patrón se
50
+ // construye por concatenación para que el gate no se detecte a sí mismo.
51
+ const RX_CWD_PLANNING = new RegExp(
52
+ 'join\\(\\s*process\\.cwd\\(\\)\\s*,\\s*[\'"]' + '(?:\\.plan' + 'ning|instin' + 'tos)'
53
+ );
54
+ const RX_ESCAPE_RAIZ = /raiz-ok:/;
55
+
56
+ function archivosObjetivo() {
57
+ const salida = execFileSync('git', [
58
+ 'ls-files', '--',
59
+ 'scripts/*.js', 'scripts/**/*.js',
60
+ 'hooks/*.js', 'hooks/**/*.js',
61
+ 'bin/*.js',
62
+ 'gateway/*.js', 'gateway/**/*.js',
63
+ 'habilidades/*/scripts/*.js', 'habilidades/*/scripts/**/*.js',
64
+ ], { cwd: RAIZ, encoding: 'utf8' });
65
+ return salida.split(/\r?\n/).filter(f => f && !f.includes('vendor/'));
66
+ }
67
+
68
+ function auditarUtc(ruta, lineas, hallazgos) {
69
+ lineas.forEach((linea, i) => {
70
+ if (RX_UTC.test(linea) && !RX_ESCAPE_UTC.test(linea)) {
71
+ hallazgos.push(`[C1 fecha-UTC] ${ruta}:${i + 1} — toISOString().slice(0,10) fecha "mañana" desde UTC-6; usar toLocaleDateString('sv') o marcar \`// fecha-utc-ok\` si la semántica UTC es deliberada`);
72
+ }
73
+ });
74
+ }
75
+
76
+ function auditarEolCheck(ruta, contenido, hallazgos) {
77
+ // Aplica solo a GENERADORES con modo --check (escriben el artefacto y en
78
+ // check comparan lo regenerado contra disco). Un --check selector de
79
+ // sub-checks (verificar-docs) no compara texto por igualdad — no aplica.
80
+ if (!contenido.includes("'--check'") && !contenido.includes('"--check"')) return;
81
+ if (!/atomicWrite|writeFileSync/.test(contenido)) return;
82
+ if (!/readFileSync\([^)]*utf-?8/i.test(contenido)) return;
83
+ const normaliza = contenido.includes('\\r\\n') || contenido.includes('\\r?\\n');
84
+ const comparaEstructura = contenido.includes('JSON.parse');
85
+ const escape = RX_ESCAPE_EOL.test(contenido);
86
+ if (!normaliza && !comparaEstructura && !escape) {
87
+ hallazgos.push(`[C2 EOL] ${ruta} — modo --check que lee texto sin normalizar \\r\\n ni comparar estructuras: reventará con CRLF tras checkout/reset de git (caso publish v2.5.0). Normalizar EOL antes de comparar o marcar \`// eol-ok: <razón>\``);
88
+ }
89
+ }
90
+
91
+ function auditarBloqueoCiego(ruta, contenido, hallazgos) {
92
+ if (!ruta.startsWith('hooks/') || ruta.startsWith('hooks/lib/')) return;
93
+ const usaExit2 = /exit\(2\)|exitCode\s*=\s*2/.test(contenido);
94
+ const escribeStderr = contenido.includes('console.error') || contenido.includes('process.stderr.write');
95
+ if (usaExit2 && !escribeStderr) {
96
+ hallazgos.push(`[C3 bloqueo-ciego] ${ruta} — usa exit 2 sin escribir a stderr (console.error/process.stderr.write): el modelo recibe el bloqueo sin la razón`);
97
+ }
98
+ }
99
+
100
+ function auditarCwdPlanning(ruta, lineas, hallazgos) {
101
+ if (!ruta.startsWith('hooks/')) return;
102
+ lineas.forEach((linea, i) => {
103
+ if (RX_CWD_PLANNING.test(linea) && !RX_ESCAPE_RAIZ.test(linea)) {
104
+ hallazgos.push(`[C4 raiz-proyecto] ${ruta}:${i + 1} — ancla .planning/instintos a process.cwd() crudo: fragmenta la memoria en monorepos (DT-HOOKS-RAIZ-GIT); usar raizProyecto(process.cwd()) de hooks/lib/raiz-proyecto.js o marcar \`// raiz-ok: <razón>\``);
105
+ }
106
+ });
107
+ }
108
+
109
+ function main() {
110
+ const hallazgos = [];
111
+ const archivos = archivosObjetivo();
112
+ for (const ruta of archivos) {
113
+ let contenido;
114
+ try {
115
+ contenido = fs.readFileSync(path.join(RAIZ, ruta), 'utf8');
116
+ } catch { continue; }
117
+ const lineas = contenido.split(/\r?\n/);
118
+ auditarUtc(ruta, lineas, hallazgos);
119
+ auditarEolCheck(ruta, contenido, hallazgos);
120
+ auditarBloqueoCiego(ruta, contenido, hallazgos);
121
+ auditarCwdPlanning(ruta, lineas, hallazgos);
122
+ }
123
+
124
+ if (hallazgos.length === 0) {
125
+ console.log(`[clases-conocidas] OK — ${archivos.length} archivos, 0 reincidencias de clases documentadas (C1 UTC, C2 EOL, C3 bloqueo ciego, C4 ancla cwd)`);
126
+ return;
127
+ }
128
+ console.error(`[clases-conocidas] ${hallazgos.length} reincidencia(s) de clases de bug documentadas:`);
129
+ for (const h of hallazgos) console.error(' ' + h);
130
+ process.exitCode = 1;
131
+ }
132
+
133
+ module.exports = { auditarUtc, auditarEolCheck, auditarBloqueoCiego, auditarCwdPlanning };
134
+ if (require.main === module) main();
@@ -164,4 +164,5 @@ if (require.main === module) {
164
164
 
165
165
  module.exports = {
166
166
  parseArgs,
167
+ main,
167
168
  };
@@ -5,9 +5,23 @@
5
5
  * bootstrap-instintos.js
6
6
  *
7
7
  * Pobla instintos/proyecto.yaml inicial a partir de las entradas de
8
- * .planning/APRENDIZAJES.md. Cada entrada `### [YYYY-MM-DD] título` se
9
- * convierte en un instinto con confidence inicial según la sección que lo
10
- * contiene:
8
+ * .planning/APRENDIZAJES.md. Reconoce dos formatos de entrada:
9
+ *
10
+ * ### [YYYY-MM-DD] Título (formato histórico)
11
+ * ### L-NNN: Título (RATING, Tipo X → destino) (formato /swl:aprender actual)
12
+ *
13
+ * Para el formato L-NNN, la fecha se toma de una línea `**valid_at**: YYYY-MM-DD`
14
+ * dentro del cuerpo de la entrada (o de la fecha del encabezado `## [YYYY-MM-DD] ...`
15
+ * que la contiene, si la trae); si ninguna aparece, se usa la fecha de generación.
16
+ * El rating (HIGH/MEDIUM/LOW) y el Tipo (A/B/C/D) del título, cuando existen,
17
+ * tienen prioridad sobre la confidence/tags inferidas de la sección — son más
18
+ * específicos a la entrada individual que el título de la sección que la contiene.
19
+ *
20
+ * Gap conocido, no cubierto: entradas registradas como ítem de lista numerada
21
+ * dentro de un bloque de sesión (`4. **[L-285]** ...`) sin encabezado `###`
22
+ * propio — requieren un parser de listas dedicado, fuera del alcance de este fix.
23
+ *
24
+ * Cada instinto recibe confidence inicial según la sección que lo contiene:
11
25
  *
12
26
  * ## Anti-patrones detectados → confidence 0.7, tags: [anti-patron]
13
27
  * ## Patrones exitosos → confidence 0.6, tags: [patron]
@@ -81,21 +95,54 @@ function detectarConfidence(seccion) {
81
95
  return { confidence: 0.5, tags: [] };
82
96
  }
83
97
 
98
+ /**
99
+ * Detecta un rating (HIGH/MEDIUM/LOW) en el título de una entrada `### L-NNN: ...`.
100
+ * Más específico que la confidence por sección — prioridad cuando existe.
101
+ * @param {string} titulo
102
+ * @returns {number|null}
103
+ */
104
+ function detectarConfidenceDeTitulo(titulo) {
105
+ const t = titulo || '';
106
+ if (/\bHIGH\b/i.test(t)) return 0.8;
107
+ if (/\bMEDIUM\b/i.test(t)) return 0.6;
108
+ if (/\bLOW\b/i.test(t)) return 0.4;
109
+ return null;
110
+ }
111
+
112
+ /**
113
+ * Detecta el Tipo (A/B/C/D, taxonomía de /swl:aprender § Paso 4) en el título
114
+ * de una entrada `### L-NNN: ...` y lo mapea a un tag. Más específico que los
115
+ * tags por sección — prioridad cuando existe al menos un match.
116
+ * @param {string} titulo
117
+ * @returns {string[]}
118
+ */
119
+ function detectarTagsDeTitulo(titulo) {
120
+ const t = titulo || '';
121
+ const tags = [];
122
+ if (/\bTipo\s*A\b/.test(t)) tags.push('regla-proyecto');
123
+ if (/\bTipo\s*B\b/.test(t)) tags.push('anti-patron');
124
+ if (/\bTipo\s*C\b/.test(t)) tags.push('nueva-habilidad');
125
+ if (/\bTipo\s*D\b/.test(t)) tags.push('metodologia');
126
+ return tags;
127
+ }
128
+
84
129
  // ---------------------------------------------------------------------------
85
130
  // Parser de APRENDIZAJES.md
86
131
  // ---------------------------------------------------------------------------
87
132
 
88
133
  /**
89
134
  * Parsea el markdown en objetos de instinto.
90
- * Formato esperado por entrada:
135
+ * Formatos reconocidos por entrada:
91
136
  * ## Sección
92
- * ### [YYYY-MM-DD] Título
93
- * Párrafo de descripción (puede ser multi-línea hasta el siguiente ###)
137
+ * ### [YYYY-MM-DD] Título — histórico
138
+ * ### L-NNN: Título (RATING, Tipo X destino) actual (/swl:aprender)
139
+ * Párrafo de descripción (puede ser multi-línea hasta el siguiente ### o ##).
94
140
  */
95
141
  function parsearAprendizajes(texto) {
96
142
  const lineas = texto.split(/\r?\n/);
97
143
  const entradas = [];
98
144
  let seccionActual = '';
145
+ let fechaSeccionActual = null;
99
146
  let actual = null;
100
147
 
101
148
  const flushActual = () => {
@@ -110,15 +157,17 @@ function parsearAprendizajes(texto) {
110
157
  for (const raw of lineas) {
111
158
  const linea = raw.replace(/\s+$/, '');
112
159
 
113
- // Sección (##)
160
+ // Sección (## sin ser ###) — puede traer su propia fecha "## [YYYY-MM-DD] ..."
114
161
  const mSec = linea.match(/^##\s+(.+)$/);
115
162
  if (mSec && !linea.startsWith('###')) {
116
163
  flushActual();
117
164
  seccionActual = mSec[1].trim();
165
+ const mFechaSec = seccionActual.match(/^\[(\d{4}-\d{2}-\d{2})\]/);
166
+ fechaSeccionActual = mFechaSec ? mFechaSec[1] : null;
118
167
  continue;
119
168
  }
120
169
 
121
- // Entrada (### [YYYY-MM-DD] Título)
170
+ // Entrada formato histórico: ### [YYYY-MM-DD] Título
122
171
  const mEntry = linea.match(/^###\s+\[(\d{4}-\d{2}-\d{2})\]\s+(.+)$/);
123
172
  if (mEntry) {
124
173
  flushActual();
@@ -131,9 +180,26 @@ function parsearAprendizajes(texto) {
131
180
  continue;
132
181
  }
133
182
 
134
- // Acumular descripción si tenemos entrada activa
183
+ // Entrada formato actual: ### L-NNN: Título (RATING, Tipo X → destino)
184
+ const mEntryL = linea.match(/^###\s+L-\d+:\s*(.+)$/);
185
+ if (mEntryL) {
186
+ flushActual();
187
+ actual = {
188
+ fecha: fechaSeccionActual, // se corrige abajo con **valid_at** si aparece
189
+ titulo: mEntryL[1].trim(),
190
+ seccion: seccionActual,
191
+ _descParts: [],
192
+ };
193
+ continue;
194
+ }
195
+
196
+ // Acumular descripción / capturar valid_at si tenemos entrada activa
135
197
  if (actual) {
136
198
  const t = linea.trim();
199
+ if (t) {
200
+ const mValidAt = t.match(/\*\*valid_at\*\*:\s*(\d{4}-\d{2}-\d{2})/);
201
+ if (mValidAt) actual.fecha = mValidAt[1];
202
+ }
137
203
  if (t && !t.startsWith('#') && !t.startsWith('---')) {
138
204
  actual._descParts.push(t);
139
205
  }
@@ -161,9 +227,14 @@ function truncate(s, n) {
161
227
  function generarInstintos(entradas) {
162
228
  const hoy = new Date().toLocaleDateString('sv');
163
229
  return entradas.map((e, idx) => {
164
- const { confidence, tags } = detectarConfidence(e.seccion);
230
+ const base = detectarConfidence(e.seccion);
231
+ const confidenceTitulo = detectarConfidenceDeTitulo(e.titulo);
232
+ const tagsTitulo = detectarTagsDeTitulo(e.titulo);
233
+ const confidence = confidenceTitulo != null ? confidenceTitulo : base.confidence;
234
+ const tags = tagsTitulo.length > 0 ? tagsTitulo : base.tags;
165
235
  const pattern = truncate(e.titulo, 200);
166
236
  const description = truncate(e.description || e.titulo, 500);
237
+ const fecha = e.fecha || hoy;
167
238
  return {
168
239
  id: `inst-${String(idx + 1).padStart(3, '0')}`,
169
240
  pattern,
@@ -172,8 +243,8 @@ function generarInstintos(entradas) {
172
243
  scope: 'project',
173
244
  source: 'APRENDIZAJES.md',
174
245
  evidence_count: 1,
175
- last_validated: e.fecha,
176
- last_validated_at: e.fecha,
246
+ last_validated: fecha,
247
+ last_validated_at: fecha,
177
248
  contradictions: 0,
178
249
  status: 'active',
179
250
  tags,
@@ -242,7 +313,7 @@ function main() {
242
313
  const entradas = parsearAprendizajes(texto);
243
314
 
244
315
  if (entradas.length === 0) {
245
- console.log('No se detectaron entradas en APRENDIZAJES.md (patrón "### [YYYY-MM-DD] Título").');
316
+ console.log('No se detectaron entradas en APRENDIZAJES.md (patrones "### [YYYY-MM-DD] Título" o "### L-NNN: Título").');
246
317
  return;
247
318
  }
248
319
 
@@ -284,4 +355,4 @@ function main() {
284
355
 
285
356
  if (require.main === module) main();
286
357
 
287
- module.exports = { parsearAprendizajes, generarInstintos, detectarConfidence, inferirTipoMemoria, main };
358
+ module.exports = { parsearAprendizajes, generarInstintos, detectarConfidence, detectarConfidenceDeTitulo, detectarTagsDeTitulo, inferirTipoMemoria, main };