@saulwade/swl-ses 2.2.0 → 2.2.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 (74) hide show
  1. package/CLAUDE.md +199 -196
  2. package/README.md +597 -579
  3. package/agentes/arquitecto-swl.md +0 -5
  4. package/agentes/backend-python-swl.md +0 -5
  5. package/agentes/implementador-swl.md +0 -5
  6. package/agentes/nemesis-auditor-swl.md +0 -5
  7. package/agentes/orquestador-swl.md +0 -5
  8. package/agentes/planificador-swl.md +0 -5
  9. package/agentes/revisor-codigo-swl.md +0 -5
  10. package/comandos/swl/adoptar-proyecto.md +253 -258
  11. package/comandos/swl/aprender.md +823 -828
  12. package/comandos/swl/claudemd.md +234 -239
  13. package/comandos/swl/ejecutar-fase.md +0 -5
  14. package/comandos/swl/nuevo-proyecto.md +200 -205
  15. package/comandos/swl/release.md +19 -5
  16. package/comandos/swl/revisar-impacto.md +0 -5
  17. package/habilidades/agent-browser/SKILL.md +0 -5
  18. package/habilidades/angular-moderno/SKILL.md +0 -5
  19. package/habilidades/api-rest-diseno/SKILL.md +0 -5
  20. package/habilidades/aprendizaje-continuo/SKILL.md +0 -5
  21. package/habilidades/auth-patrones/SKILL.md +0 -5
  22. package/habilidades/build-errors-nextjs/SKILL.md +0 -5
  23. package/habilidades/changelog-generator/SKILL.md +174 -179
  24. package/habilidades/checklist-seguridad/SKILL.md +0 -5
  25. package/habilidades/contenedores-docker/SKILL.md +0 -5
  26. package/habilidades/datos-etl/SKILL.md +0 -5
  27. package/habilidades/doc-sync/SKILL.md +0 -5
  28. package/habilidades/extractor-de-aprendizajes/SKILL.md +0 -5
  29. package/habilidades/fastapi-experto/SKILL.md +0 -5
  30. package/habilidades/frontend-avanzado/SKILL.md +0 -5
  31. package/habilidades/iam-secretos/SKILL.md +0 -5
  32. package/habilidades/manejo-errores/SKILL.md +0 -5
  33. package/habilidades/mapear-codebase/SKILL.md +0 -5
  34. package/habilidades/meta-skills-estandar/SKILL.md +0 -5
  35. package/habilidades/monitoring-alertas/SKILL.md +0 -5
  36. package/habilidades/nextjs-experto/SKILL.md +0 -5
  37. package/habilidades/nextjs-testing/SKILL.md +0 -5
  38. package/habilidades/node-experto/SKILL.md +0 -5
  39. package/habilidades/orquestacion-async/SKILL.md +0 -5
  40. package/habilidades/patrones-python/SKILL.md +227 -232
  41. package/habilidades/planear-fase/SKILL.md +336 -341
  42. package/habilidades/postgresql-experto/SKILL.md +0 -5
  43. package/habilidades/prevencion-sobreingenieria/SKILL.md +0 -5
  44. package/habilidades/protocolo-revision-swl/SKILL.md +0 -5
  45. package/habilidades/react-experto/SKILL.md +0 -5
  46. package/habilidades/release-semver/SKILL.md +0 -5
  47. package/habilidades/swl-claudemd/SKILL.md +0 -5
  48. package/habilidades/tdd-workflow/SKILL.md +710 -715
  49. package/habilidades/testing-python/SKILL.md +335 -340
  50. package/habilidades/verificar-trabajo/SKILL.md +0 -5
  51. package/hooks/lib/evolution-tracker.js +191 -35
  52. package/llms.txt +1 -1
  53. package/manifiestos/canonical-hashes.json +656 -0
  54. package/manifiestos/modulos.json +3 -0
  55. package/manifiestos/skills-lock.json +70 -70
  56. package/package.json +1 -1
  57. package/plugin.json +1 -1
  58. package/scripts/generar-canonical-hashes.js +147 -0
  59. package/scripts/instalador.js +126 -53
  60. package/scripts/lib/audit-evolved.js +71 -0
  61. package/scripts/lib/canonical-hash.js +94 -0
  62. package/scripts/lib/evolved-fuente.js +138 -0
  63. package/scripts/remediar-evolved-instaladas.js +239 -0
  64. package/scripts/validar.js +14 -0
  65. package/scripts/verificar-evolucion.js +36 -0
  66. package/scripts/verificar-release.js +33 -0
  67. package/agentes/.evolved.json +0 -9
  68. package/comandos/swl/.evolved.json +0 -23
  69. package/habilidades/auth-patrones/.evolved.json +0 -9
  70. package/habilidades/extractor-de-aprendizajes/.evolved.json +0 -9
  71. package/habilidades/instalar-sistema/.evolved.json +0 -9
  72. package/habilidades/manejo-errores/.evolved.json +0 -9
  73. package/habilidades/node-experto/.evolved.json +0 -9
  74. package/habilidades/release-semver/.evolved.json +0 -9
@@ -0,0 +1,239 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * remediar-evolved-instaladas — Descongela copias instaladas con marcadores
6
+ * evolved espurios (Fase 16, T-25, REQ-16-14).
7
+ *
8
+ * Las instalaciones previas a Fase 16 quedaron con componentes `evolved:true`
9
+ * shippeados de fábrica que el instalador preservaba para siempre (bug). Este
10
+ * sub-comando los reclasifica en la máquina del usuario:
11
+ *
12
+ * - B (shipped intacto): el cuerpo canónico instalado coincide con el baseline
13
+ * de su `evolved-from` (manifiesto actual o `git show v<from>:<ruta>`).
14
+ * → el usuario NO lo tocó → actualizar al canónico actual (con backup + auditoría).
15
+ * - A (evolución del usuario): el cuerpo difiere del baseline, o no hay baseline
16
+ * verificable. → PRESERVAR (jamás overwrite). Invariante.
17
+ *
18
+ * SEGURO por defecto: `--dry-run` (solo reporta). Requiere `--apply` explícito.
19
+ * Ante CUALQUIER duda (sin baseline, sin fuente, error) clasifica A (preserva).
20
+ *
21
+ * Uso:
22
+ * node scripts/remediar-evolved-instaladas.js --target=~/.claude (dry-run)
23
+ * node scripts/remediar-evolved-instaladas.js --target=~/.claude --apply (aplica)
24
+ *
25
+ * Zero-deps salvo git (para baseline histórico). Layout runtime: claude.
26
+ *
27
+ * @module scripts/remediar-evolved-instaladas
28
+ */
29
+
30
+ const fs = require('fs');
31
+ const os = require('os');
32
+ const path = require('path');
33
+ const { spawnSync } = require('child_process');
34
+
35
+ const { canonicalHash } = require('./lib/canonical-hash');
36
+ const { auditarEscritura } = require('./lib/audit-evolved');
37
+ const { mergeEvolved } = require('../hooks/lib/evolution-tracker');
38
+
39
+ const RAIZ_PKG = path.resolve(__dirname, '..');
40
+
41
+ // Mapeo dir-runtime (claude) → dir-fuente (clave del manifiesto / git).
42
+ const MAP_DIRS = [
43
+ { runtime: 'agents', fuente: 'agentes', skill: false },
44
+ { runtime: 'commands/swl', fuente: 'comandos/swl', skill: false },
45
+ { runtime: 'rules', fuente: 'reglas', skill: false },
46
+ { runtime: 'skills', fuente: 'habilidades', skill: true },
47
+ ];
48
+
49
+ function expandirTarget(t) {
50
+ if (!t) return path.join(os.homedir(), '.claude');
51
+ if (t === '~' || t.startsWith('~/') || t.startsWith('~\\')) return path.join(os.homedir(), t.slice(1).replace(/^[/\\]/, ''));
52
+ return path.resolve(t);
53
+ }
54
+
55
+ function leerEvolved(content) {
56
+ const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
57
+ if (!m) return { evolved: false };
58
+ const fm = m[1];
59
+ if (!/^evolved:\s*(true|yes)\b/mi.test(fm)) return { evolved: false };
60
+ const from = (/^evolved-from:\s*["']?([^"'\n\r]+)/mi.exec(fm) || [])[1];
61
+ // CRÍTICO (nemesis): extraer evolved-origin — sin esto, la defensa
62
+ // 'evolved-origin: user fuerza A' era letra muerta aquí (ev.origin undefined).
63
+ const origin = (/^evolved-origin:\s*["']?([^"'\n\r]+)/mi.exec(fm) || [])[1];
64
+ return { evolved: true, from: from ? from.trim() : '', origin: origin ? origin.trim() : '' };
65
+ }
66
+
67
+ /** Baseline canónico de un componente en una versión: manifiesto actual o git. */
68
+ function baselineCanonico(manifiesto, from, sourceRel) {
69
+ if (manifiesto && manifiesto[from] && manifiesto[from][sourceRel]) return manifiesto[from][sourceRel];
70
+ if (!from) return null;
71
+ const r = spawnSync('git', ['show', `v${from}:${sourceRel}`], { cwd: RAIZ_PKG, encoding: 'utf8' });
72
+ if (r.status !== 0 || !r.stdout) return null;
73
+ return canonicalHash(r.stdout);
74
+ }
75
+
76
+ /**
77
+ * Conjunto de hashes canónicos que el archivo `sourceRel` tuvo a lo largo de
78
+ * TODA la historia del repo madre (siguiendo renames). Si el cuerpo instalado
79
+ * coincide con cualquiera de ellos, el componente es contenido shipped del repo
80
+ * (ya incorporado en algún momento → B), no una evolución inventada por el
81
+ * usuario (Punto 1: "analizar si ya lo incorporas al proyecto madre").
82
+ * @param {string} sourceRel
83
+ * @param {number} [maxCommits=120]
84
+ * @returns {Set<string>}
85
+ */
86
+ function hashesHistoricos(sourceRel, maxCommits = 120) {
87
+ const set = new Set();
88
+ const GIT_OPTS = { cwd: RAIZ_PKG, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 };
89
+ const log = spawnSync('git', ['log', '--all', '--follow', '--pretty=%H', '--', sourceRel], GIT_OPTS);
90
+ if (log.status !== 0 || !log.stdout) return set;
91
+ const commits = log.stdout.trim().split('\n').filter(Boolean).slice(0, maxCommits);
92
+ for (const c of commits) {
93
+ // status !== 0 es esperado en commits pre-rename (la ruta nueva no existía):
94
+ // se saltan, no son error. Solo se agregan blobs realmente presentes.
95
+ const show = spawnSync('git', ['show', `${c}:${sourceRel}`], GIT_OPTS);
96
+ if (show.status === 0 && show.stdout) set.add(canonicalHash(show.stdout));
97
+ }
98
+ return set;
99
+ }
100
+
101
+ function cargarManifiesto() {
102
+ try { return JSON.parse(fs.readFileSync(path.join(RAIZ_PKG, 'manifiestos', 'canonical-hashes.json'), 'utf8')); }
103
+ catch { return {}; }
104
+ }
105
+
106
+ /** Lista componentes evolved instalados en el target, con su mapeo a fuente. */
107
+ function escanear(target) {
108
+ const items = [];
109
+ for (const { runtime, fuente, skill } of MAP_DIRS) {
110
+ const dir = path.join(target, runtime);
111
+ if (!fs.existsSync(dir)) continue;
112
+ if (skill) {
113
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
114
+ if (!e.isDirectory()) continue;
115
+ const skillMd = path.join(dir, e.name, 'SKILL.md');
116
+ if (fs.existsSync(skillMd)) items.push({ archivo: skillMd, sourceRel: `${fuente}/${e.name}/SKILL.md`, esDir: true, dir: path.join(dir, e.name) });
117
+ }
118
+ } else {
119
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
120
+ if (e.isFile() && e.name.endsWith('.md')) items.push({ archivo: path.join(dir, e.name), sourceRel: `${fuente}/${e.name}`, esDir: false });
121
+ }
122
+ }
123
+ }
124
+ return items;
125
+ }
126
+
127
+ function backupSimple(target, archivo, ts) {
128
+ const rel = path.relative(target, archivo);
129
+ const dest = path.join(target, '.swl-evolved-backups', ts, rel);
130
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
131
+ fs.copyFileSync(archivo, dest);
132
+ return dest;
133
+ }
134
+
135
+ function remediar({ target, apply = false } = {}) {
136
+ target = expandirTarget(target);
137
+ const manifiesto = cargarManifiesto();
138
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
139
+ const reconcileDir = path.join(target, '.planning', 'evolution', 'reconcile');
140
+ const resumen = { target, A: [], B: [], errores: [], reconcileDir };
141
+
142
+ for (const item of escanear(target)) {
143
+ let content;
144
+ try { content = fs.readFileSync(item.archivo, 'utf8'); } catch (e) { resumen.errores.push({ archivo: item.sourceRel, error: e.message }); continue; }
145
+ const ev = leerEvolved(content);
146
+ if (!ev.evolved) continue;
147
+
148
+ const fuenteAbs = path.join(RAIZ_PKG, item.sourceRel);
149
+ const instaladoHash = canonicalHash(content);
150
+
151
+ // Clasificación B (shipped → ya incorporado en el repo madre):
152
+ // (1) marca explícita evolved-origin: user → A definitivo (defensa en profundidad).
153
+ // (2) coincide con baseline del manifiesto / git show v<from>.
154
+ // (3) coincide con CUALQUIER estado histórico del archivo en el repo (Punto 1).
155
+ let esB = false;
156
+ let evidencia = '';
157
+ if ((ev.origin || '').toLowerCase() === 'user') {
158
+ evidencia = 'evolved-origin: user (marca explícita)';
159
+ } else {
160
+ const baseline = baselineCanonico(manifiesto, ev.from, item.sourceRel);
161
+ if (baseline && instaladoHash === baseline) { esB = true; evidencia = `baseline v${ev.from} coincide`; }
162
+ else if (hashesHistoricos(item.sourceRel).has(instaladoHash)) { esB = true; evidencia = 'coincide con un estado histórico del repo madre'; }
163
+ else evidencia = baseline ? 'cuerpo difiere de todo estado del repo (edición del usuario)' : 'sin baseline ni coincidencia histórica';
164
+ }
165
+
166
+ if (!esB) {
167
+ // A — evolución del usuario. PRESERVAR + diff centralizado para revisión upstream.
168
+ let diffRel = null;
169
+ if (apply && fs.existsSync(fuenteAbs)) {
170
+ const m = mergeEvolved(item.archivo, fuenteAbs, 'actual', { diffDir: reconcileDir });
171
+ if (m.merged && m.diffPath) diffRel = path.relative(target, m.diffPath).replace(/\\/g, '/');
172
+ }
173
+ resumen.A.push({ archivo: item.sourceRel, from: ev.from, razon: evidencia, diff: diffRel });
174
+ continue;
175
+ }
176
+
177
+ // B — shipped intacto. Actualizar al canónico actual del paquete.
178
+ if (!fs.existsSync(fuenteAbs)) { resumen.errores.push({ archivo: item.sourceRel, error: 'fuente actual no existe' }); continue; }
179
+ if (apply) {
180
+ try {
181
+ const hashFuente = canonicalHash(fs.readFileSync(fuenteAbs, 'utf8')); // antes del copy
182
+ const backupPath = backupSimple(target, item.archivo, ts); // backup PRECEDE al overwrite
183
+ fs.copyFileSync(fuenteAbs, item.archivo); // fuente limpio (sin evolved-*) → descongelado
184
+ auditarEscritura({ archivo: item.sourceRel, clasificacion: 'B', accion: 'overwrite', hashAntes: instaladoHash, hashDespues: hashFuente, backupPath, evidencia: `remediación: ${evidencia}` });
185
+ } catch (e) { resumen.errores.push({ archivo: item.sourceRel, error: e.message }); continue; }
186
+ }
187
+ resumen.B.push({ archivo: item.sourceRel, from: ev.from, razon: evidencia });
188
+ }
189
+
190
+ if (apply) escribirIndiceReconcile(resumen);
191
+ return resumen;
192
+ }
193
+
194
+ /** Escribe un índice consolidado de reconciliación (superficie de revisión upstream). */
195
+ function escribirIndiceReconcile(resumen) {
196
+ if (resumen.A.length === 0) return;
197
+ try {
198
+ fs.mkdirSync(resumen.reconcileDir, { recursive: true });
199
+ const lineas = [
200
+ '# Reconciliación de evoluciones del usuario (revisión upstream)',
201
+ '',
202
+ 'Estos componentes difieren de todo estado conocido del repo madre — son',
203
+ 'ediciones locales genuinas. Revisa cada diff y decide si incorporarlo al',
204
+ 'repo con `/swl:aprender` o `/swl:autoresearch`, o descartarlo.',
205
+ '',
206
+ '| Componente | evolved-from | Diff | Motivo |',
207
+ '|---|---|---|---|',
208
+ ...resumen.A.map(a => `| ${a.archivo} | v${a.from || '?'} | ${a.diff || '—'} | ${a.razon} |`),
209
+ '',
210
+ ];
211
+ fs.writeFileSync(path.join(resumen.reconcileDir, 'INDEX.md'), lineas.join('\n'), 'utf8');
212
+ } catch { /* best-effort */ }
213
+ }
214
+
215
+ function imprimir(r, apply) {
216
+ process.stdout.write(`\n=== Remediación evolved instaladas — target: ${r.target} ===\n`);
217
+ process.stdout.write(`Modo: ${apply ? 'APPLY (escribe)' : 'DRY-RUN (solo reporta)'}\n\n`);
218
+ process.stdout.write(`B (shipped intacto → ${apply ? 'actualizado' : 'se actualizaría'}): ${r.B.length}\n`);
219
+ for (const b of r.B) process.stdout.write(` ⇪ ${b.archivo} (from v${b.from})\n`);
220
+ process.stdout.write(`\nA (evolución del usuario → PRESERVADO): ${r.A.length}\n`);
221
+ for (const a of r.A) process.stdout.write(` ★ ${a.archivo} (from v${a.from || '?'}) — ${a.razon}\n`);
222
+ if (r.errores.length) {
223
+ process.stdout.write(`\nErrores: ${r.errores.length}\n`);
224
+ for (const e of r.errores) process.stdout.write(` ! ${e.archivo}: ${e.error}\n`);
225
+ }
226
+ if (apply && r.A.length) process.stdout.write(`\nDiffs de reconciliación (revisión upstream): ${path.relative(r.target, r.reconcileDir).replace(/\\/g, '/')}/INDEX.md\n`);
227
+ if (!apply && r.B.length) process.stdout.write(`\n→ Para aplicar: agregar --apply (backups en <target>/.swl-evolved-backups/, diffs centralizados en .planning/evolution/reconcile/).\n`);
228
+ }
229
+
230
+ if (require.main === module) {
231
+ const args = process.argv.slice(2);
232
+ const targetArg = (args.find(a => a.startsWith('--target=')) || '').slice('--target='.length) || undefined;
233
+ const apply = args.includes('--apply');
234
+ const r = remediar({ target: targetArg, apply });
235
+ imprimir(r, apply);
236
+ process.exit(0);
237
+ }
238
+
239
+ module.exports = { remediar, escanear, leerEvolved, baselineCanonico, expandirTarget };
@@ -101,6 +101,20 @@ verificar(
101
101
  : `Cross-scope: ${auditInvoc.violaciones.length} invocación(es) relativa(s) — ${auditInvoc.violaciones.map(v => `${v.comando}.md:${v.linea}`).join(', ')}`
102
102
  );
103
103
 
104
+ // 7c. Gate inverso de evolución (Fase 16, REQ-16-03): el FUENTE no debe portar
105
+ // marcadores evolved (frontmatter evolved:true ni sidecar .evolved.json). Un
106
+ // marcador en el repo madre es shipped-evolved espurio que congela el componente
107
+ // downstream. Ver scripts/lib/evolved-fuente.js y verificar-evolucion.js --gate-inverso.
108
+ const { listarOfensores } = require('./lib/evolved-fuente');
109
+ const ofensoresEvolved = listarOfensores(RAIZ);
110
+ const totalOfensores = ofensoresEvolved.frontmatter.length + ofensoresEvolved.sidecars.length;
111
+ verificar(
112
+ totalOfensores === 0,
113
+ totalOfensores === 0
114
+ ? 'Gate inverso evolved: fuente sin marcadores evolved espurios'
115
+ : `Gate inverso evolved: ${totalOfensores} componente(s) del fuente con evolved (corregir: node scripts/verificar-evolucion.js --gate-inverso --fix)`
116
+ );
117
+
104
118
  // 8. Reglas
105
119
  const reglas = fs.readdirSync(path.join(RAIZ, 'reglas')).filter(f => f.endsWith('.md'));
106
120
  verificar(reglas.length >= 7, `Reglas: ${reglas.length} (mínimo 7)`);
@@ -32,6 +32,7 @@
32
32
  const fs = require('fs');
33
33
  const path = require('path');
34
34
  const { execSync } = require('child_process');
35
+ const { listarOfensores, limpiar } = require('./lib/evolved-fuente');
35
36
 
36
37
  /**
37
38
  * Detecta si `dir` es la raíz del paquete swl-ses (repo madre).
@@ -291,9 +292,43 @@ function imprimirResultado(r) {
291
292
  process.stdout.write('[' + marca + '] ' + r.archivo + ': ' + mensaje + '\n');
292
293
  }
293
294
 
295
+ /**
296
+ * Gate inverso (Fase 16, REQ-16-03): ningún componente del FUENTE (repo madre)
297
+ * puede portar marcador evolved (frontmatter `evolved:true` o sidecar
298
+ * `.evolved.json`). Un marcador en el fuente es shipped-evolved espurio que
299
+ * congela el componente downstream. Corre SIEMPRE en el repo madre — es el
300
+ * complemento exacto del bypass `if (raizPaquete)` de verificarArchivo (que
301
+ * omite los checks evolved-de-usuario; este gate verifica lo contrario).
302
+ *
303
+ * @param {{ fix?: boolean, raiz?: string }} [opts]
304
+ * @returns {number} 0 si limpio, 1 si hay ofensores (sin --fix)
305
+ */
306
+ function gateInverso({ fix = false, raiz = process.cwd() } = {}) {
307
+ if (fix) {
308
+ const acciones = limpiar(raiz);
309
+ for (const a of acciones) process.stdout.write(` [fix] ${a.tipo}: ${a.archivo}\n`);
310
+ process.stdout.write(`[gate-inverso] limpieza aplicada: ${acciones.length} acción(es)\n`);
311
+ }
312
+ const { frontmatter, sidecars } = listarOfensores(raiz);
313
+ const total = frontmatter.length + sidecars.length;
314
+ if (total === 0) {
315
+ process.stdout.write('[gate-inverso] OK — ningún componente del fuente porta marcador evolved.\n');
316
+ return 0;
317
+ }
318
+ process.stdout.write(`[gate-inverso] FALLA — ${total} componente(s) del fuente con marcador evolved (deben limpiarse):\n`);
319
+ for (const f of frontmatter) process.stdout.write(` - frontmatter evolved: ${f}\n`);
320
+ for (const s of sidecars) process.stdout.write(` - sidecar .evolved.json: ${s}\n`);
321
+ process.stdout.write(' → corregir: node scripts/verificar-evolucion.js --gate-inverso --fix\n');
322
+ return 1;
323
+ }
324
+
294
325
  function main() {
295
326
  const args = process.argv.slice(2);
296
327
 
328
+ if (args.includes('--gate-inverso')) {
329
+ process.exit(gateInverso({ fix: args.includes('--fix') }));
330
+ }
331
+
297
332
  if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
298
333
  process.stdout.write(
299
334
  'Uso:\n' +
@@ -351,5 +386,6 @@ module.exports = {
351
386
  leerCampo,
352
387
  obtenerVersionEnHEAD,
353
388
  esRaizDelPaquete,
389
+ gateInverso,
354
390
  main,
355
391
  };
@@ -317,6 +317,18 @@ function main() {
317
317
  // calibración + ADR posterior (mismo patrón que G0/G2/G3).
318
318
  const gateLicencias = ejecutarGateLicencias();
319
319
 
320
+ // Gate de evolución (Fase 16, REQ-16-13): el manifiesto canonical-hashes debe
321
+ // estar al día y el fuente NO debe portar marcadores evolved espurios. Ambos
322
+ // son blocking — un release con manifiesto stale o fuente evolved congelaría
323
+ // componentes downstream.
324
+ const gateEvolved = ejecutarGateEvolved();
325
+ if (!gateEvolved.ok) {
326
+ fallasObligatorias++;
327
+ }
328
+ console.log(gateEvolved.ok
329
+ ? ' [OK] Gate evolución: manifiesto al día + fuente sin evolved espurio'
330
+ : ` [FALLA] Gate evolución: ${gateEvolved.problemas.join('; ')}`);
331
+
320
332
  if (jsonOut) {
321
333
  process.stdout.write(JSON.stringify({
322
334
  version,
@@ -330,6 +342,7 @@ function main() {
330
342
  description_gate: gateDescription,
331
343
  aiisms_gate: aiismsGate,
332
344
  gate_licencias: gateLicencias,
345
+ gate_evolved: gateEvolved,
333
346
  resultados: resultados.map(({ entrada, resultado }) => ({
334
347
  archivo: entrada.archivo,
335
348
  obligatorio: entrada.obligatorio,
@@ -887,6 +900,25 @@ function ejecutarGateDescription(contadoresReales) {
887
900
  * para tests; en producción usa el CWD del proceso).
888
901
  * @returns {{disponible: boolean, hallazgos?: Array, resumen?: object, error?: string}}
889
902
  */
903
+ /**
904
+ * Gate de evolución (Fase 16): manifiesto canonical-hashes al día + fuente sin
905
+ * marcadores evolved espurios. Reusa los scripts existentes vía subproceso.
906
+ * @returns {{ ok: boolean, problemas: string[] }}
907
+ */
908
+ function ejecutarGateEvolved() {
909
+ const { spawnSync } = require('child_process');
910
+ const problemas = [];
911
+ const correr = (args) => spawnSync(process.execPath, args, { cwd: CWD, encoding: 'utf8' }).status;
912
+
913
+ if (correr([path.join(CWD, 'scripts', 'generar-canonical-hashes.js'), '--check']) !== 0) {
914
+ problemas.push('manifiesto canonical-hashes desactualizado (node scripts/generar-canonical-hashes.js)');
915
+ }
916
+ if (correr([path.join(CWD, 'scripts', 'verificar-evolucion.js'), '--gate-inverso']) !== 0) {
917
+ problemas.push('fuente con marcadores evolved espurios (node scripts/verificar-evolucion.js --gate-inverso --fix)');
918
+ }
919
+ return { ok: problemas.length === 0, problemas };
920
+ }
921
+
890
922
  function ejecutarGateLicencias(baseDir = CWD) {
891
923
  try {
892
924
  const { hallazgos, resumen } = evaluarLicencias(baseDir);
@@ -905,4 +937,5 @@ module.exports = {
905
937
  extraerCifrasDescription,
906
938
  ejecutarGateDescription,
907
939
  ejecutarGateLicencias,
940
+ ejecutarGateEvolved,
908
941
  };
@@ -1,9 +0,0 @@
1
- {
2
- "release-manager-swl.md": {
3
- "evolved": true,
4
- "evolvedFrom": "5.10.4",
5
- "evolvedAt": "2026-04-20",
6
- "evolvedBy": "aprender",
7
- "evolvedNote": "gap recurrente en bumps: package-lock nested + .planning/"
8
- }
9
- }
@@ -1,23 +0,0 @@
1
- {
2
- "release.md": {
3
- "evolved": true,
4
- "evolvedFrom": "5.4.0",
5
- "evolvedAt": "2026-04-11",
6
- "evolvedBy": "aprender",
7
- "evolvedNote": "mejora de metodología: checklist obligatoria de archivos de versión en paso 6"
8
- },
9
- "aprender.md": {
10
- "evolved": true,
11
- "evolvedFrom": "5.12.3",
12
- "evolvedAt": "2026-04-25",
13
- "evolvedBy": "aprender",
14
- "evolvedNote": "Paso 2 — filtro crítico obligatorio sobre reportes de sub-agentes Explore para evitar sobre-ingeniería al analizar papers académicos"
15
- },
16
- "verificar.md": {
17
- "evolved": true,
18
- "evolvedFrom": "5.12.3",
19
- "evolvedAt": "2026-04-26",
20
- "evolvedBy": "evolucionar",
21
- "evolvedNote": "flag --until-converge para iterar verificar→corregir→re-verificar hasta 0 hallazgos CRÍTICO+ALTO+MAYOR (max-iter=5, --no-prompt CI, detección adversarial ≥5 hallazgos nuevos)"
22
- }
23
- }
@@ -1,9 +0,0 @@
1
- {
2
- "SKILL.md": {
3
- "evolved": true,
4
- "evolvedFrom": "5.11.1",
5
- "evolvedAt": "2026-04-23",
6
- "evolvedBy": "aprender",
7
- "evolvedNote": "sección nueva: Multitenancy con get_readable_statement (patrón de polar)"
8
- }
9
- }
@@ -1,9 +0,0 @@
1
- {
2
- "SKILL.md": {
3
- "evolved": true,
4
- "evolvedFrom": "5.11.0",
5
- "evolvedAt": "2026-04-22",
6
- "evolvedBy": "aprender",
7
- "evolvedNote": "Gotchas nuevos: registro dual manifiestos para hooks + inventario estimado a mano"
8
- }
9
- }
@@ -1,9 +0,0 @@
1
- {
2
- "SKILL.md": {
3
- "evolved": true,
4
- "evolvedFrom": "5.10.4",
5
- "evolvedAt": "2026-04-20",
6
- "evolvedBy": "aprender",
7
- "evolvedNote": "state file busqueda en 7 ubicaciones"
8
- }
9
- }
@@ -1,9 +0,0 @@
1
- {
2
- "SKILL.md": {
3
- "evolved": true,
4
- "evolvedFrom": "1.1.0",
5
- "evolvedAt": "2026-05-02",
6
- "evolvedBy": "aprender",
7
- "evolvedNote": "Gotcha nueva: try/catch require para módulos opcionales (15 archivos migrados)"
8
- }
9
- }
@@ -1,9 +0,0 @@
1
- {
2
- "SKILL.md": {
3
- "evolved": true,
4
- "evolvedFrom": "5.3.3",
5
- "evolvedAt": "2026-04-09",
6
- "evolvedBy": "aprender",
7
- "evolvedNote": "anti-patrón JSDoc glob, JSONL append-only, webhook fire-and-forget, dual-use scripts"
8
- }
9
- }
@@ -1,9 +0,0 @@
1
- {
2
- "SKILL.md": {
3
- "evolved": true,
4
- "evolvedFrom": "1.0.1",
5
- "evolvedAt": "2026-05-02",
6
- "evolvedBy": "aprender",
7
- "evolvedNote": "Sección nueva: publish a múltiples registries (republish-only + auth GitHub Packages)"
8
- }
9
- }