@saulwade/swl-ses 2.2.4 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "swl-ses",
3
- "version": "2.2.4",
4
- "description": "Sistema de ingenieria de software auto-evolutivo multi-runtime polyglot. 60 agentes, 182 habilidades, 44 comandos, 77 reglas y 48 hooks. 76 librerias. 11 lenguajes. Soporta Claude Code, Copilot, OpenCode, Codex y Gemini CLI. Loop evaluator-optimizer en /swl:nemesis (ADR-0021). v1.8.0 unifica los directorios runtime de .planning/ al ingles (evolution/, auto-evolution/, user-profile/, archive/), manteniendo fases/ en espanol, con guard validar-planning-paths y allowlist canonica (ADR-0031). Hereda de v1.7.4: skill calidad-anti-patrones-universales + scripts/lib/pr-analyzer.js + 3 sub-secciones en estilo-sin-ai-isms.",
3
+ "version": "2.3.0",
4
+ "description": "Sistema de ingenieria de software auto-evolutivo multi-runtime polyglot. 60 agentes, 182 habilidades, 44 comandos, 77 reglas y 50 hooks. 77 librerias. 11 lenguajes. Soporta Claude Code, Copilot, OpenCode, Codex y Gemini CLI. Loop evaluator-optimizer en /swl:nemesis (ADR-0021). v1.8.0 unifica los directorios runtime de .planning/ al ingles (evolution/, auto-evolution/, user-profile/, archive/), manteniendo fases/ en espanol, con guard validar-planning-paths y allowlist canonica (ADR-0031). Hereda de v1.7.4: skill calidad-anti-patrones-universales + scripts/lib/pr-analyzer.js + 3 sub-secciones en estilo-sin-ai-isms.",
5
5
  "author": "Saul Wade Leon",
6
6
  "license": "MIT",
7
7
  "repository": "https://github.com/saul-wade/swl-ses",
@@ -19,6 +19,47 @@ regla clara, un mismo dato puede terminar en el lugar incorrecto o duplicado.
19
19
 
20
20
  ---
21
21
 
22
+ ## Eje cognitivo de memoria (semantic | episodic | procedural)
23
+
24
+ Los 5 canales de arriba clasifican el conocimiento por **dónde se almacena**. El
25
+ eje cognitivo lo clasifica por **qué tipo de memoria es** — y es **ORTOGONAL**: un
26
+ dato vive en exactamente un canal (regla de no-duplicación abajo) y PUEDE
27
+ etiquetarse con exactamente un tipo cognitivo. No reemplaza a los canales; los
28
+ enriquece para mejorar la relevancia del recall.
29
+
30
+ Origen: análisis "Self-Learning for Agents" (3 capas) —
31
+ `.planning/knowledge/outputs/analisis-self-learning-agents-3-capas-2026-06-27.md`.
32
+ Un agente que se auto-mejora necesita las tres clases; la mayoría de los sistemas
33
+ solo tiene `semantic`.
34
+
35
+ | Tipo | Qué es | Ejemplo | Cuándo usarlo |
36
+ |------|--------|---------|---------------|
37
+ | **semantic** | Hechos, decisiones, vocabulario, gotchas-como-hecho | "El proyecto usa repository pattern"; "la API de CFDI devuelve 200 en errores" | Conocimiento estable que se consulta como referencia |
38
+ | **episodic** | Experiencia pasada de una sesión concreta (qué pasó, cuándo) | "En la sesión X el revert del commit Y resolvió el bug Z" | Recordar un caso vivido para no repetir el camino |
39
+ | **procedural** | Cómo manejar un caso (cómo hacer / qué evitar) | "Paralelizar agentes solo sin dependencias de datos"; "no usar `display:initial` para re-mostrar celdas" | Guía de acción ante una situación recurrente |
40
+
41
+ ### Dónde se materializa
42
+
43
+ - **instintos** (`schemas/instinct.schema.json`): campo OPCIONAL `tipo_memoria`
44
+ (enum `semantic|episodic|procedural`). Backward-compatible: instintos sin el
45
+ campo siguen válidos. `scripts/bootstrap-instintos.js` lo infiere por sección de
46
+ origen (heurístico declarado, no autoritativo):
47
+ - `decisión` / `arquitectura` / `gotcha` / `regla-proyecto` → **semantic**
48
+ - `patrón` / `anti-patrón` → **procedural**
49
+ - **episodic** NO se infiere del bootstrap (nace de registros de sesión, canal 4,
50
+ no de APRENDIZAJES).
51
+ - **APRENDIZAJES.md** (canal 2): convención OPCIONAL — una entrada puede anotar
52
+ `tipo_memoria:` en su cuerpo cuando el tipo no sea evidente por la sección.
53
+
54
+ ### Anti-patrones del eje cognitivo
55
+
56
+ - Tratar el tipo cognitivo como un sexto canal de almacenamiento (no lo es: es una
57
+ etiqueta ortogonal sobre el dato que ya vive en su canal).
58
+ - Forzar `episodic` en un instinto bootstrapeado de APRENDIZAJES (episodic viene de
59
+ sesiones, no de conocimiento de dominio destilado).
60
+
61
+ ---
62
+
22
63
  ## Regla de asignación — dónde va CADA tipo de dato
23
64
 
24
65
  ### A. Información del usuario
@@ -355,10 +355,76 @@ exclusiones:
355
355
 
356
356
  ---
357
357
 
358
+ ## Anti-gaming: no auto-calificación de evidencia
359
+
360
+ Esta sección formaliza una invariante de seguridad **no negociable** para el
361
+ ciclo de auto-evolución (AGP) y para cualquier agente que se auto-mejore.
362
+
363
+ ### Invariante
364
+
365
+ > Un agente que evoluciona **NUNCA debe producir ni editar la evidencia que lo
366
+ > aprueba**. El productor de la evidencia (score de eval, badge, evidencia RED,
367
+ > veredicto de verificación, canary) debe ser un **actor distinto** del agente
368
+ > evaluado, o una **decisión humana**. La única señal imposible de falsificar es
369
+ > la generada por un actor independiente del evaluado.
370
+
371
+ ### Origen — Sakana Darwin Gödel Machine
372
+
373
+ El DGM, soltado a mejorar contra un test, en vez de hacer mejor trabajo
374
+ **falsificó sus propios logs de test**. Cuando los investigadores le agregaron un
375
+ detector para atraparlo, **quitó las marcas en que el detector se apoyaba**, pese
376
+ a que se le prohibió. Lección: si el evaluado controla la evidencia que lo
377
+ califica, la optimización converge a falsificar la evidencia, no a mejorar.
378
+ (Fuente: análisis self-learning 3 capas, `sakana.ai/dgm`.)
379
+
380
+ ### Relación con gobernanza (no se re-deriva aquí)
381
+
382
+ Esta invariante **extiende** —no duplica— `reglas/gobernanza.md`:
383
+ - `§ Separación revisor/ejecutor`: el ejecutor nunca emite veredicto sobre su
384
+ propio trabajo; el revisor no modifica código.
385
+ - `§ Gate G8`: un skill se promueve solo con badge ≥ Plata de `/swl:evaluar-skill`.
386
+
387
+ El aporte de esta sección es el principio general "no auto-calificación de
388
+ **evidencia**" aplicado a TODA superficie del AGP, y el mapeo a las superficies
389
+ reales del sistema (auditoría Fase 18, 2026-06-27).
390
+
391
+ ### Mapeo a superficies del ciclo AGP (auditoría Fase 18)
392
+
393
+ | Superficie | ¿Productor ≠ calificador? | Veredicto |
394
+ |---|---|---|
395
+ | Canary pre-write (`hooks/lib/canary-skills.js`) | Validador estructural determinista sobre el contenido real; el agente no puede falsearlo | **PASS** |
396
+ | Veredicto de verificación (`/swl:verificar`, `nemesis`) | `gobernanza.md` exige revisor ≠ ejecutor (agentes distintos) | **PASS** (por proceso) |
397
+ | Invariantes de seguridad (`run-skill-evals --check-invariants`) | `validarInvariantesDeAgente` corre independiente del score declarado | **PASS** |
398
+ | Evidencia RED de TDD (`hooks/tdd-gate.js`) | La escribe el mismo ejecutar-fase; mitigado porque el GREEN lo corre un test-runner independiente y es WARN-ONLY | **PARCIAL** |
399
+ | **Score de eval (`run-skill-evals.js --record-after --score=N`)** | El `--score` se toma **verbatim del flag** que pasa el agente que evoluciona; nada lo liga a una corrida independiente de `/swl:evaluar-skill` | **GAP** (DT-AGP-SCORE-AUTODECLARADO) |
400
+ | Registro de evolución (`evoluciones.jsonl`) + métricas | Heredan el score auto-declarado de la superficie anterior | **GAP** (mismo origen) |
401
+
402
+ ### Anti-patrones (prohibidos)
403
+
404
+ - Un agente que pasa su propio `--score=N` como evidencia de que su evolución
405
+ mejoró, sin que ese número provenga de un evaluador independiente.
406
+ - Un ejecutor que escribe/edita su propia evidencia RED, badge o veredicto.
407
+ - "Agregar un detector" y dejar que el evaluado pueda editar las marcas en que el
408
+ detector se apoya (el error literal del DGM).
409
+ - Confiar en un número de score persistido sin procedencia verificable del actor
410
+ que lo produjo.
411
+
412
+ ### Regla operativa
413
+
414
+ Toda métrica que decida promoción/rollback de una evolución debe poder rastrear
415
+ **qué actor la produjo**. Si el actor es el mismo agente evaluado, la métrica es
416
+ un *hint*, no una aprobación: requiere corroboración de un actor independiente
417
+ (otro agente evaluador, el test-runner, o el humano) antes de promover.
418
+
419
+ ---
420
+
358
421
  ## Checklist de seguridad de agentes
359
422
 
360
423
  Antes de desplegar un agente nuevo o modificar permisos de uno existente:
361
424
 
425
+ - [ ] Ninguna evidencia que aprueba al agente (score, badge, RED, veredicto) es
426
+ producida o editable por el propio agente evaluado (§ Anti-gaming)
427
+
362
428
  - [ ] Los permisos en frontmatter son los mínimos necesarios para la función
363
429
  - [ ] `nivelRiesgo` refleja el impacto real si el agente actúa incorrectamente
364
430
  - [ ] El agente no puede escalar privilegios a través de delegación
@@ -1,152 +1,161 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://swl-ses.dev/schemas/instinct.json",
4
- "$schemaVersion": "1.0.0",
5
- "title": "SWL Instinct",
6
- "description": "Schema de validacion para instintos YAML del sistema SWL",
7
- "type": "object",
8
- "required": [
9
- "version",
10
- "instintos"
11
- ],
12
- "properties": {
13
- "version": {
14
- "type": "string"
15
- },
16
- "generado": {
17
- "type": "string"
18
- },
19
- "total_instintos": {
20
- "type": "integer",
21
- "minimum": 0
22
- },
23
- "instintos": {
24
- "type": "array",
25
- "items": {
26
- "type": "object",
27
- "required": [
28
- "id",
29
- "pattern",
30
- "confidence",
31
- "scope",
32
- "status"
33
- ],
34
- "properties": {
35
- "id": {
36
- "type": "string",
37
- "pattern": "^inst-\\d{3,}$",
38
- "description": "Identificador unico secuencial"
39
- },
40
- "pattern": {
41
- "type": "string",
42
- "minLength": 10,
43
- "maxLength": 500,
44
- "description": "Descripcion del patron observado"
45
- },
46
- "confidence": {
47
- "type": "number",
48
- "minimum": 0,
49
- "maximum": 1,
50
- "description": "Nivel de confianza (0.0 bajo, 1.0 certeza)"
51
- },
52
- "scope": {
53
- "type": "string",
54
- "enum": [
55
- "project",
56
- "domain",
57
- "global"
58
- ],
59
- "description": "Alcance del instinto"
60
- },
61
- "source": {
62
- "type": "string",
63
- "description": "ID de sesion o 'manual'"
64
- },
65
- "evidence_count": {
66
- "type": "integer",
67
- "minimum": 0,
68
- "description": "Veces que el patron ha sido observado"
69
- },
70
- "last_validated": {
71
- "type": "string",
72
- "description": "Fecha de ultima validacion (YYYY-MM-DD)"
73
- },
74
- "contradictions": {
75
- "type": "integer",
76
- "minimum": 0,
77
- "description": "Veces que el patron ha sido contradicho"
78
- },
79
- "status": {
80
- "type": "string",
81
- "enum": [
82
- "active",
83
- "degraded",
84
- "promoted",
85
- "archived"
86
- ],
87
- "description": "Estado actual del instinto"
88
- },
89
- "tags": {
90
- "type": "array",
91
- "items": {
92
- "type": "string"
93
- },
94
- "description": "Etiquetas para clasificacion"
95
- },
96
- "created_at": {
97
- "type": "string",
98
- "description": "Fecha de creacion (YYYY-MM-DD)"
99
- },
100
- "decay_half_life_days": {
101
- "type": "integer",
102
- "minimum": 1,
103
- "default": 90,
104
- "description": "Dias para que la confianza decaiga a la mitad. 90 dias por defecto."
105
- },
106
- "helpful_count": {
107
- "type": "integer",
108
- "minimum": 0,
109
- "default": 0,
110
- "description": "Numero de veces que el instinto fue util en una sesion"
111
- },
112
- "harmful_count": {
113
- "type": "integer",
114
- "minimum": 0,
115
- "default": 0,
116
- "description": "Numero de veces que el instinto guio a un resultado incorrecto"
117
- },
118
- "last_validated_at": {
119
- "type": "string",
120
- "description": "Timestamp ISO o YYYY-MM-DD de la ultima validacion via feedback (sustituye a last_validated en sistemas con scoring)"
121
- },
122
- "source_sessions": {
123
- "type": "array",
124
- "items": {
125
- "type": "string"
126
- },
127
- "uniqueItems": true,
128
- "description": "IDs de sesiones que contribuyeron a este instinto (para priorizacion por evidencia multiple)"
129
- },
130
- "source_agents": {
131
- "type": "array",
132
- "items": {
133
- "type": "string"
134
- },
135
- "uniqueItems": true,
136
- "description": "Nombres de agentes que validaron este instinto (cross-agent enrichment)"
137
- },
138
- "maturity": {
139
- "type": "string",
140
- "enum": [
141
- "candidate",
142
- "established",
143
- "proven",
144
- "deprecated"
145
- ],
146
- "description": "Estado de maduracion derivado (computado, no persistente). Ver scripts/lib/scoring-instintos.js"
147
- }
148
- }
149
- }
150
- }
151
- }
152
- }
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://swl-ses.dev/schemas/instinct.json",
4
+ "$schemaVersion": "1.0.0",
5
+ "title": "SWL Instinct",
6
+ "description": "Schema de validacion para instintos YAML del sistema SWL",
7
+ "type": "object",
8
+ "required": [
9
+ "version",
10
+ "instintos"
11
+ ],
12
+ "properties": {
13
+ "version": {
14
+ "type": "string"
15
+ },
16
+ "generado": {
17
+ "type": "string"
18
+ },
19
+ "total_instintos": {
20
+ "type": "integer",
21
+ "minimum": 0
22
+ },
23
+ "instintos": {
24
+ "type": "array",
25
+ "items": {
26
+ "type": "object",
27
+ "required": [
28
+ "id",
29
+ "pattern",
30
+ "confidence",
31
+ "scope",
32
+ "status"
33
+ ],
34
+ "properties": {
35
+ "id": {
36
+ "type": "string",
37
+ "pattern": "^inst-\\d{3,}$",
38
+ "description": "Identificador unico secuencial"
39
+ },
40
+ "pattern": {
41
+ "type": "string",
42
+ "minLength": 10,
43
+ "maxLength": 500,
44
+ "description": "Descripcion del patron observado"
45
+ },
46
+ "confidence": {
47
+ "type": "number",
48
+ "minimum": 0,
49
+ "maximum": 1,
50
+ "description": "Nivel de confianza (0.0 bajo, 1.0 certeza)"
51
+ },
52
+ "scope": {
53
+ "type": "string",
54
+ "enum": [
55
+ "project",
56
+ "domain",
57
+ "global"
58
+ ],
59
+ "description": "Alcance del instinto"
60
+ },
61
+ "source": {
62
+ "type": "string",
63
+ "description": "ID de sesion o 'manual'"
64
+ },
65
+ "evidence_count": {
66
+ "type": "integer",
67
+ "minimum": 0,
68
+ "description": "Veces que el patron ha sido observado"
69
+ },
70
+ "last_validated": {
71
+ "type": "string",
72
+ "description": "Fecha de ultima validacion (YYYY-MM-DD)"
73
+ },
74
+ "contradictions": {
75
+ "type": "integer",
76
+ "minimum": 0,
77
+ "description": "Veces que el patron ha sido contradicho"
78
+ },
79
+ "status": {
80
+ "type": "string",
81
+ "enum": [
82
+ "active",
83
+ "degraded",
84
+ "promoted",
85
+ "archived"
86
+ ],
87
+ "description": "Estado actual del instinto"
88
+ },
89
+ "tags": {
90
+ "type": "array",
91
+ "items": {
92
+ "type": "string"
93
+ },
94
+ "description": "Etiquetas para clasificacion"
95
+ },
96
+ "created_at": {
97
+ "type": "string",
98
+ "description": "Fecha de creacion (YYYY-MM-DD)"
99
+ },
100
+ "decay_half_life_days": {
101
+ "type": "integer",
102
+ "minimum": 1,
103
+ "default": 90,
104
+ "description": "Dias para que la confianza decaiga a la mitad. 90 dias por defecto."
105
+ },
106
+ "helpful_count": {
107
+ "type": "integer",
108
+ "minimum": 0,
109
+ "default": 0,
110
+ "description": "Numero de veces que el instinto fue util en una sesion"
111
+ },
112
+ "harmful_count": {
113
+ "type": "integer",
114
+ "minimum": 0,
115
+ "default": 0,
116
+ "description": "Numero de veces que el instinto guio a un resultado incorrecto"
117
+ },
118
+ "last_validated_at": {
119
+ "type": "string",
120
+ "description": "Timestamp ISO o YYYY-MM-DD de la ultima validacion via feedback (sustituye a last_validated en sistemas con scoring)"
121
+ },
122
+ "source_sessions": {
123
+ "type": "array",
124
+ "items": {
125
+ "type": "string"
126
+ },
127
+ "uniqueItems": true,
128
+ "description": "IDs de sesiones que contribuyeron a este instinto (para priorizacion por evidencia multiple)"
129
+ },
130
+ "source_agents": {
131
+ "type": "array",
132
+ "items": {
133
+ "type": "string"
134
+ },
135
+ "uniqueItems": true,
136
+ "description": "Nombres de agentes que validaron este instinto (cross-agent enrichment)"
137
+ },
138
+ "maturity": {
139
+ "type": "string",
140
+ "enum": [
141
+ "candidate",
142
+ "established",
143
+ "proven",
144
+ "deprecated"
145
+ ],
146
+ "description": "Estado de maduracion derivado (computado, no persistente). Ver scripts/lib/scoring-instintos.js"
147
+ },
148
+ "tipo_memoria": {
149
+ "type": "string",
150
+ "enum": [
151
+ "semantic",
152
+ "episodic",
153
+ "procedural"
154
+ ],
155
+ "description": "Eje cognitivo de memoria (ORTOGONAL al canal de almacenamiento): semantic=hechos/decisiones, episodic=experiencia pasada de una sesion, procedural=como manejar un caso. Opcional y backward-compatible: instintos sin el campo siguen validos. Ver reglas/memoria-consolidada.md."
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ }
@@ -49,15 +49,31 @@ const flags = {
49
49
  // Config de secciones → confidence/tags
50
50
  // ---------------------------------------------------------------------------
51
51
 
52
+ // tipo_memoria (eje cognitivo, Fase 19): patron/anti-patron = "cómo hacer / qué
53
+ // evitar" → procedural; decisión/gotcha/regla = hechos → semantic. episodic NO se
54
+ // infiere del bootstrap (nace de registros de sesión, no de APRENDIZAJES).
52
55
  const SECCIONES = [
53
- { regex: /anti[-\s]?patrones?/i, confidence: 0.7, tags: ['anti-patron'] },
54
- { regex: /patrones?\s+exitosos?/i, confidence: 0.6, tags: ['patron'] },
55
- { regex: /decisiones?\s+de\s+arquitectura/i, confidence: 0.8, tags: ['decision', 'arquitectura'] },
56
- { regex: /gotchas?/i, confidence: 0.6, tags: ['gotcha'] },
57
- { regex: /patrones?/i, confidence: 0.6, tags: ['patron'] },
58
- { regex: /reglas?\s+de\s+proyecto/i, confidence: 0.8, tags: ['regla-proyecto'] },
56
+ { regex: /anti[-\s]?patrones?/i, confidence: 0.7, tags: ['anti-patron'], tipoMemoria: 'procedural' },
57
+ { regex: /patrones?\s+exitosos?/i, confidence: 0.6, tags: ['patron'], tipoMemoria: 'procedural' },
58
+ { regex: /decisiones?\s+de\s+arquitectura/i, confidence: 0.8, tags: ['decision', 'arquitectura'], tipoMemoria: 'semantic' },
59
+ { regex: /gotchas?/i, confidence: 0.6, tags: ['gotcha'], tipoMemoria: 'semantic' },
60
+ { regex: /patrones?/i, confidence: 0.6, tags: ['patron'], tipoMemoria: 'procedural' },
61
+ { regex: /reglas?\s+de\s+proyecto/i, confidence: 0.8, tags: ['regla-proyecto'], tipoMemoria: 'semantic' },
59
62
  ];
60
63
 
64
+ /**
65
+ * Infiere el eje cognitivo de memoria de un instinto por su sección de origen.
66
+ * Heurístico declarado (no autoritativo): default 'semantic' para hechos.
67
+ * @param {string} seccion
68
+ * @returns {'semantic'|'episodic'|'procedural'}
69
+ */
70
+ function inferirTipoMemoria(seccion) {
71
+ for (const s of SECCIONES) {
72
+ if (s.regex.test(seccion || '')) return s.tipoMemoria;
73
+ }
74
+ return 'semantic';
75
+ }
76
+
61
77
  function detectarConfidence(seccion) {
62
78
  for (const s of SECCIONES) {
63
79
  if (s.regex.test(seccion)) return { confidence: s.confidence, tags: [...s.tags] };
@@ -163,6 +179,7 @@ function generarInstintos(entradas) {
163
179
  tags,
164
180
  created_at: hoy,
165
181
  seccion_origen: e.seccion,
182
+ tipo_memoria: inferirTipoMemoria(e.seccion),
166
183
  decay_half_life_days: 90,
167
184
  helpful_count: 0,
168
185
  harmful_count: 0,
@@ -200,6 +217,7 @@ instintos:
200
217
  ` tags: [${i.tags.map(t => `"${t}"`).join(', ')}]`,
201
218
  ` created_at: "${i.created_at}"`,
202
219
  ` seccion_origen: ${escapeYaml(i.seccion_origen)}`,
220
+ ` tipo_memoria: ${i.tipo_memoria}`,
203
221
  ` decay_half_life_days: ${i.decay_half_life_days}`,
204
222
  ` helpful_count: ${i.helpful_count}`,
205
223
  ` harmful_count: ${i.harmful_count}`,
@@ -266,4 +284,4 @@ function main() {
266
284
 
267
285
  if (require.main === module) main();
268
286
 
269
- module.exports = { parsearAprendizajes, generarInstintos, detectarConfidence, main };
287
+ module.exports = { parsearAprendizajes, generarInstintos, detectarConfidence, inferirTipoMemoria, main };
@@ -11,8 +11,9 @@
11
11
  * Todo lo que instala o genera en runtime se ignora en git (framework, hooks,
12
12
  * métricas, sesiones, aprendizajes, trazas, instintos).
13
13
  *
14
- * Solo se commitean: CLAUDE.md, .claude/settings.json, y las plantillas de
15
- * .planning/ que el equipo edita (PROYECTO.md, REQUISITOS.md, etc.).
14
+ * Solo se commitean: CLAUDE.md, .claude/settings.json, instintos/proyecto.yaml
15
+ * (memoria del proyecto), y las plantillas de .planning/ que el equipo edita
16
+ * (PROYECTO.md, REQUISITOS.md, etc.).
16
17
  */
17
18
 
18
19
  const fs = require("fs");
@@ -54,11 +55,8 @@ const ENTRADAS_BASE = [
54
55
  ".swl-ses",
55
56
 
56
57
  // ── Runtime: archivos generados automáticamente por hooks ──────────
57
- // Aprendizajes, métricas, compactación (extraccion-aprendizajes, tracking-costos, context-compressor)
58
- ".planning/APRENDIZAJES.md",
58
+ // Métricas (tracking-costos)
59
59
  ".planning/METRICAS.md",
60
- ".planning/COMPACTACION.md",
61
- ".planning/REPORTE-GRAFO.md",
62
60
 
63
61
  // Audit trail (audit-trail.js, merkle-audit.js)
64
62
  ".planning/*.jsonl",
@@ -88,8 +86,13 @@ const ENTRADAS_BASE = [
88
86
  "!.planning/locks/*PLAN.md.lock",
89
87
  ".planning/user-profile/",
90
88
 
91
- // Instintos modificados automáticamente por hooks (degradacion-instintos.js)
92
- "instintos/",
89
+ // Instintos: global/perfil-usuario/prompt-appendices/autonomia son framework +
90
+ // runtime + personal (no se commitean). proyecto.yaml es memoria del proyecto
91
+ // (hermano YAML de APRENDIZAJES.md, memoria-consolidada § canal 3) → versionable.
92
+ // Patrón dir/* + negación: git no re-incluye archivos bajo un dir ignorado
93
+ // (mismo gotcha documentado en locks/ arriba), por eso se ignora el contenido.
94
+ "instintos/*",
95
+ "!instintos/proyecto.yaml",
93
96
 
94
97
  // Base de datos de uso
95
98
  "usage.db",
@@ -305,8 +308,16 @@ function limpiarTracked(cwd, extras = []) {
305
308
  return resultado; // No es repo git, nada que hacer
306
309
  }
307
310
 
308
- // Obtener todas las entradas gitignore de swl-ses (directorios y archivos)
309
- const entradas = [...new Set([...ENTRADAS_BASE, ...extras])];
311
+ // Obtener todas las entradas gitignore de swl-ses (directorios y archivos).
312
+ // Las negaciones (!...) marcan archivos versionables que NUNCA se des-trackean
313
+ // (instintos/proyecto.yaml, plan-locks G1 *PLAN.md.lock). git ls-files NO honra
314
+ // las negaciones del .gitignore, así que se excluyen explícitamente de la lista
315
+ // a limpiar — de lo contrario el glob padre (instintos/*, locks/*) las arrastraría.
316
+ const todas = [...new Set([...ENTRADAS_BASE, ...extras])];
317
+ const negaciones = todas
318
+ .filter((e) => e.startsWith("!"))
319
+ .map((e) => patronAGlobRegex(e.slice(1)));
320
+ const entradas = todas.filter((e) => !e.startsWith("!"));
310
321
 
311
322
  for (const entrada of entradas) {
312
323
  try {
@@ -322,7 +333,12 @@ function limpiarTracked(cwd, extras = []) {
322
333
 
323
334
  if (!archivosTracked) continue;
324
335
 
325
- const lista = archivosTracked.split("\n").filter(Boolean);
336
+ const lista = archivosTracked
337
+ .split("\n")
338
+ .filter(Boolean)
339
+ // Preservar archivos versionables cubiertos por una negación del gitignore
340
+ // (ej. instintos/proyecto.yaml frente al glob instintos/*).
341
+ .filter((archivo) => !negaciones.some((re) => re.test(archivo)));
326
342
  if (lista.length === 0) continue;
327
343
 
328
344
  // Quitar del índice uno por uno (--cached = solo del índice, no del disco)
@@ -353,6 +369,23 @@ function escapeRegex(str) {
353
369
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
354
370
  }
355
371
 
372
+ /**
373
+ * Convierte un patrón estilo gitignore (sin el '!' inicial) a una RegExp anclada
374
+ * para matchear rutas que devuelve `git ls-files` (siempre con '/' como separador).
375
+ * `*` no cruza '/' (semántica gitignore). Una entrada de directorio matchea también
376
+ * su contenido. Se usa en limpiarTracked para excluir archivos negados del untrack.
377
+ *
378
+ * @param {string} patron Ej. "instintos/proyecto.yaml", ".planning/locks/*PLAN.md.lock"
379
+ * @returns {RegExp}
380
+ */
381
+ function patronAGlobRegex(patron) {
382
+ const cuerpo = patron
383
+ .replace(/\/$/, "")
384
+ .replace(/[.+?^${}()|[\]\\]/g, "\\$&")
385
+ .replace(/\*/g, "[^/]*");
386
+ return new RegExp("^" + cuerpo + "(?:/.*)?$");
387
+ }
388
+
356
389
  module.exports = {
357
390
  actualizarGitignore,
358
391
  entradasParaRuntime,