@saulwade/swl-ses 1.6.8 → 1.7.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.
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Hook: claudemd-duplicacion-detector.js
6
+ * Tipo: PostToolUse (aplica a: Write, Edit, MultiEdit)
7
+ *
8
+ * Detecta duplicación de reglas globales (`~/.claude/rules/`) inline en
9
+ * `CLAUDE.md` de proyecto recién modificados. Consume el detector
10
+ * `scripts/lib/detector-reglas-duplicadas.js` que lee el catálogo
11
+ * declarativo `scripts/lib/reglas-globales-conocidas.json`.
12
+ *
13
+ * Aplica la regla `reglas/sin-duplicacion-reglas-globales.md`:
14
+ * el `CLAUDE.md` del proyecto NO debe duplicar reglas que ya viven en
15
+ * `~/.claude/rules/`. La regla global es fuente de verdad; el proyecto
16
+ * referencia, no duplica.
17
+ *
18
+ * Opt-out: SWL_CLAUDEMD_DUPLICACION=0 desactiva completamente el hook.
19
+ *
20
+ * Comportamiento:
21
+ * - Nunca bloquea operaciones (exit code 0 siempre)
22
+ * - Solo emite nudge cuando hay duplicaciones detectadas
23
+ * - Solo se dispara con archivos cuyo basename sea exactamente CLAUDE.md
24
+ * - Respeta exclusiones: temp/, node_modules/, respositorios-git/
25
+ * - NO evalúa user-level (~/.claude/CLAUDE.md) — ahí sí pueden
26
+ * declararse preferencias personales
27
+ *
28
+ * Formato del nudge:
29
+ * {
30
+ * id: string,
31
+ * kind: "claudemd-duplicacion-reglas",
32
+ * target: "documentador-swl",
33
+ * source: "hooks/claudemd-duplicacion-detector.js",
34
+ * message: "...",
35
+ * data: { archivo, duplicaciones: [...] },
36
+ * ts: ISO,
37
+ * accionado: false
38
+ * }
39
+ */
40
+
41
+ const fs = require('fs');
42
+ const path = require('path');
43
+ const os = require('os');
44
+ const crypto = require('crypto');
45
+
46
+ // ─── Opt-out global ───────────────────────────────────────────────────────
47
+ if (process.env.SWL_CLAUDEMD_DUPLICACION === '0') {
48
+ process.exit(0);
49
+ }
50
+
51
+ let hookInput = '';
52
+ try {
53
+ hookInput = fs.readFileSync(0, 'utf-8');
54
+ } catch (_) {
55
+ process.exit(0);
56
+ }
57
+
58
+ let evento;
59
+ try {
60
+ evento = JSON.parse(hookInput);
61
+ } catch (_) {
62
+ process.exit(0);
63
+ }
64
+
65
+ const toolName = evento?.tool_name;
66
+ const toolInput = evento?.tool_input;
67
+
68
+ if (!toolName || !['Write', 'Edit', 'MultiEdit'].includes(toolName)) {
69
+ process.exit(0);
70
+ }
71
+
72
+ const filePath = toolInput?.file_path;
73
+ if (!filePath) {
74
+ process.exit(0);
75
+ }
76
+
77
+ // Solo CLAUDE.md (basename exacto, case-sensitive)
78
+ const basename = path.basename(filePath);
79
+ if (basename !== 'CLAUDE.md') {
80
+ process.exit(0);
81
+ }
82
+
83
+ const pathNormalized = filePath.replace(/\\/g, '/');
84
+ const RUTAS_EXCLUIDAS = [
85
+ '/temp/',
86
+ '/node_modules/',
87
+ '/respositorios-git/',
88
+ '/.planning/',
89
+ ];
90
+ if (RUTAS_EXCLUIDAS.some((excluida) => pathNormalized.includes(excluida))) {
91
+ process.exit(0);
92
+ }
93
+
94
+ // User-level: no evaluar (puede declarar preferencias personales)
95
+ const homeClaudeDir = path.resolve(path.join(os.homedir(), '.claude'));
96
+ const rutaAbs = path.resolve(filePath);
97
+ const esUserLevel = rutaAbs.startsWith(homeClaudeDir + path.sep) || rutaAbs === path.join(homeClaudeDir, 'CLAUDE.md');
98
+
99
+ // El archivo debe existir
100
+ if (!fs.existsSync(filePath)) {
101
+ process.exit(0);
102
+ }
103
+
104
+ // ─── Cargar detector ──────────────────────────────────────────────────────
105
+ const CWD = process.cwd();
106
+ const detectorPath = path.join(CWD, 'scripts', 'lib', 'detector-reglas-duplicadas.js');
107
+ if (!fs.existsSync(detectorPath)) {
108
+ // No hay detector instalado en este destino; salir silenciosamente
109
+ process.exit(0);
110
+ }
111
+
112
+ let resultado;
113
+ try {
114
+ const { detectarDuplicaciones } = require(detectorPath);
115
+ const contenido = fs.readFileSync(filePath, 'utf8');
116
+ resultado = detectarDuplicaciones(contenido, null, { esUserLevel });
117
+ } catch (_) {
118
+ // Cualquier error del detector: salir silenciosamente
119
+ process.exit(0);
120
+ }
121
+
122
+ // Solo emitir nudge si hay duplicaciones detectadas
123
+ if (!resultado || !resultado.evaluado || resultado.duplicaciones.length === 0) {
124
+ process.exit(0);
125
+ }
126
+
127
+ // ─── Construir nudge ──────────────────────────────────────────────────────
128
+ const rutaRelativa = path.relative(CWD, filePath).replace(/\\/g, '/');
129
+ const topDuplicaciones = resultado.duplicaciones
130
+ .slice(0, 5)
131
+ .map((d) => ` - [${d.severidad}] ${d.id} (regla global: ${d.regla_global}, línea ~${d.linea_aproximada})`)
132
+ .join('\n');
133
+
134
+ const nudge = {
135
+ id: crypto.randomBytes(8).toString('hex'),
136
+ kind: 'claudemd-duplicacion-reglas',
137
+ target: 'documentador-swl',
138
+ source: 'hooks/claudemd-duplicacion-detector.js',
139
+ message:
140
+ `[claudemd-dup] ${rutaRelativa} duplica ${resultado.duplicaciones.length} regla(s) global(es):\n` +
141
+ topDuplicaciones + '\n' +
142
+ ` Ejecutar \`/swl:claudemd audit\` para detalle, ` +
143
+ `\`/swl:claudemd refactor\` para sugerencias de reemplazo.\n` +
144
+ ` Ver regla: \`reglas/sin-duplicacion-reglas-globales.md\`.`,
145
+ data: {
146
+ archivo: rutaRelativa,
147
+ total_evaluadas: resultado.total_reglas_evaluadas,
148
+ detectadas: resultado.duplicaciones.length,
149
+ ids: resultado.duplicaciones.map((d) => d.id),
150
+ reglas_globales: [...new Set(resultado.duplicaciones.map((d) => d.regla_global))],
151
+ },
152
+ ts: new Date().toISOString(),
153
+ accionado: false,
154
+ accionado_ts: null,
155
+ accionado_por: null,
156
+ };
157
+
158
+ // ─── Persistir a nudges.jsonl ─────────────────────────────────────────────
159
+ try {
160
+ const nudgesPath = path.join(CWD, '.planning', 'evolucion', 'nudges.jsonl');
161
+ const nudgesDir = path.dirname(nudgesPath);
162
+ if (!fs.existsSync(nudgesDir)) {
163
+ fs.mkdirSync(nudgesDir, { recursive: true });
164
+ }
165
+ fs.appendFileSync(nudgesPath, JSON.stringify(nudge) + '\n', 'utf-8');
166
+ } catch (_) {
167
+ // No fallar el hook por error de escritura
168
+ }
169
+
170
+ process.exit(0);
@@ -356,6 +356,15 @@
356
356
  "maxConsecutiveFailures": 5,
357
357
  "degradeOnFailure": "skip"
358
358
  },
359
+ "claudemd-duplicacion-detector.js": {
360
+ "event": "PostToolUse",
361
+ "matcher": "Write|Edit|MultiEdit",
362
+ "description": "Detecta duplicación de reglas globales (~/.claude/rules/) inline en CLAUDE.md de proyecto. Consume scripts/lib/detector-reglas-duplicadas.js + catálogo scripts/lib/reglas-globales-conocidas.json. Aplica regla reglas/sin-duplicacion-reglas-globales.md. Emite nudge a .planning/evolucion/nudges.jsonl si hay duplicaciones detectadas. No bloquea — kind: claudemd-duplicacion-reglas. Excluye temp/, node_modules/, respositorios-git/, .planning/, ~/.claude/ (user-level). Opt-out: SWL_CLAUDEMD_DUPLICACION=0.",
363
+ "blocking": false,
364
+ "async": true,
365
+ "maxConsecutiveFailures": 5,
366
+ "degradeOnFailure": "skip"
367
+ },
359
368
  "validar-intent-spec.js": {
360
369
  "event": "PostToolUse",
361
370
  "matcher": "Write|Edit|MultiEdit",
@@ -905,7 +905,9 @@
905
905
  "reglas/registro-componentes-nuevos.md",
906
906
  "reglas/auditorias-documentales-estructurales.md",
907
907
  "reglas/intent-engineering.md",
908
- "reglas/tests-cleanup.md"
908
+ "reglas/tests-cleanup.md",
909
+ "reglas/verificar-citas-temporales.md",
910
+ "reglas/sin-duplicacion-reglas-globales.md"
909
911
  ],
910
912
  "targets": [
911
913
  "claude",
@@ -1024,6 +1026,7 @@
1024
1026
  "hooks/inbox-aviso.js",
1025
1027
  "hooks/aiisms-detector.js",
1026
1028
  "hooks/claudemd-bloat-detector.js",
1029
+ "hooks/claudemd-duplicacion-detector.js",
1027
1030
  "hooks/validar-intent-spec.js",
1028
1031
  "hooks/sugerir-regenerar-inventario.js",
1029
1032
  "hooks/sugerir-contribuir.js",
@@ -1219,6 +1222,8 @@
1219
1222
  "tipo": "scripts",
1220
1223
  "archivos": [
1221
1224
  "scripts/auditar-claudemd.js",
1225
+ "scripts/lib/detector-reglas-duplicadas.js",
1226
+ "scripts/lib/reglas-globales-conocidas.json",
1222
1227
  "docs/variables-entorno.md"
1223
1228
  ],
1224
1229
  "targets": [
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "lockfileVersion": 1,
3
- "generatedAt": "2026-05-22T17:02:33.598Z",
3
+ "generatedAt": "2026-05-22T23:28:47.439Z",
4
4
  "skillsCount": 177,
5
- "lockHash": "sha256:5d644809f2e8c7741a4553f1f6f493d309d4913782c9560bf06bd735eed1701e",
5
+ "lockHash": "sha256:d0306679d924ecbbd0071f532df9f2cfb499817eb42b0bb5e6e5ebcdacb941f2",
6
6
  "skills": [
7
7
  {
8
8
  "nombre": "accesibilidad-a11y",
@@ -1099,9 +1099,9 @@
1099
1099
  {
1100
1100
  "nombre": "swl-claudemd",
1101
1101
  "path": "habilidades/swl-claudemd/SKILL.md",
1102
- "hash": "sha256:dd8ef172d38668e261fc88ff22e614559a05a4f13b2c612c2da3ea2388494377",
1103
- "bytes": 13666,
1104
- "version": "\"1.0.2\""
1102
+ "hash": "sha256:0f5eed29f3fedb3bca8036ae5680901712520b31190ad416700c732b2b5897e4",
1103
+ "bytes": 21518,
1104
+ "version": "\"1.2.0\""
1105
1105
  },
1106
1106
  {
1107
1107
  "nombre": "swl-dashboard",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@saulwade/swl-ses",
3
- "version": "1.6.8",
4
- "description": "Sistema de ingenieria de software auto-evolutivo multi-runtime polyglot con 61 agentes, 177 habilidades, 44 comandos, 69 reglas y 42 hooks. Soporta 11 lenguajes y 7 runtimes: Claude Code, OpenClaude, OpenCode, Gemini CLI, Cursor, Codex CLI (soporte completo); GitHub Copilot (soporte parcial). 100% en espanol (Mexico). Multi-target install (--target CSV / --all-runtimes), autoconfig MCP en Cursor/Codex con --with-mcp, agentes Codex en TOML, hooks Cursor (17 eventos) y Codex (6 eventos). Gateway bidireccional con relay Telegram y auditoria profunda Nemesis con loop evaluator-optimizer opt-in (ADR-0021) y 8 tools ejecutables. v1.6.5 integra 3 patrones de awesome-codex-skills (ComposioHQ, MIT) agent-deep-links + changelog-generator + gh-fix-ci-swl ADR-0029, y promueve 3 evoluciones SIGAF al sistema global (D1 nemesis SendMessage + D2 verificar smoke frontend + L2 alineacion veredicto).",
3
+ "version": "1.7.1",
4
+ "description": "Sistema de ingenieria de software auto-evolutivo multi-runtime polyglot con 61 agentes, 177 habilidades, 44 comandos, 71 reglas y 43 hooks. Soporta 11 lenguajes y 7 runtimes: Claude Code, OpenClaude, OpenCode, Gemini CLI, Cursor, Codex CLI (soporte completo); GitHub Copilot (soporte parcial). 100% en espanol (Mexico). Multi-target install (--target CSV / --all-runtimes), autoconfig MCP en Cursor/Codex con --with-mcp, agentes Codex en TOML, hooks Cursor (17 eventos) y Codex (6 eventos). Gateway bidireccional con relay Telegram y auditoria profunda Nemesis con loop evaluator-optimizer opt-in (ADR-0021) y 8 tools ejecutables. v1.7.1 introduce dimension 8 del auditor CLAUDE.md (deteccion de duplicacion de reglas globales) + hook PostToolUse claudemd-duplicacion-detector + regla sin-duplicacion-reglas-globales.md + catalogo declarativo reglas-globales-conocidas.json con 6 reglas (idioma, brevedad, git-coauthor, arreglar-al-detectar, debatir, context7).",
5
5
  "bin": {
6
6
  "swl-ses": "bin/swl-ses.js",
7
7
  "swl-telegram-bot": "bin/swl-telegram-bot.js",
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "swl-ses",
3
- "version": "1.6.8",
4
- "description": "Sistema de ingenieria de software auto-evolutivo multi-runtime polyglot. 61 agentes, 177 habilidades, 44 comandos, 69 reglas y 42 hooks. 62 librerias. 11 lenguajes. Soporta Claude Code, Copilot, OpenCode, Codex y Gemini CLI. Loop evaluator-optimizer en /swl:nemesis (ADR-0021). 3 patrones de awesome-codex-skills (ComposioHQ, MIT) adoptados (ADR-0029) agent-deep-links + changelog-generator + gh-fix-ci-swl. Promueve 3 evoluciones SIGAF (D1 nemesis SendMessage, D2 verificar smoke frontend, L2 alineacion veredicto).",
3
+ "version": "1.7.1",
4
+ "description": "Sistema de ingenieria de software auto-evolutivo multi-runtime polyglot. 61 agentes, 177 habilidades, 44 comandos, 71 reglas y 43 hooks. 62 librerias. 11 lenguajes. Soporta Claude Code, Copilot, OpenCode, Codex y Gemini CLI. Loop evaluator-optimizer en /swl:nemesis (ADR-0021). v1.7.1 introduce dimension 8 del auditor CLAUDE.md (deteccion de duplicacion de reglas globales), hook PostToolUse claudemd-duplicacion-detector, regla sin-duplicacion-reglas-globales y catalogo declarativo de 6 reglas globales conocidas (idioma, brevedad, git-coauthor, arreglar-al-detectar, debatir, context7).",
5
5
  "author": "Saul Wade Leon",
6
6
  "license": "MIT",
7
7
  "repository": "https://github.com/saul-wade/swl-ses",
@@ -0,0 +1,182 @@
1
+ # Regla: Sin duplicación de reglas globales en CLAUDE.md de proyecto
2
+
3
+ Esta regla es **OBLIGATORIA** y aplica al diseño/auditoría de cualquier
4
+ `CLAUDE.md` de proyecto donde el sistema SWL esté instalado. Extiende el
5
+ contrato canónico de `swl-claudemd` con un sub-caso específico: la
6
+ duplicación inline de reglas que ya viven en `~/.claude/rules/`.
7
+
8
+ ---
9
+
10
+ ## Principio
11
+
12
+ > Cuando una regla ya está documentada en `~/.claude/rules/` (carga global
13
+ > automática en todas las sesiones del usuario), **el `CLAUDE.md` del
14
+ > proyecto NO debe duplicarla inline**. La duplicación viola DRY a nivel
15
+ > de gobernanza, genera drift cuando la regla global se actualiza, y
16
+ > erosiona el contrato canónico de `CLAUDE.md` (≤200 LOC).
17
+
18
+ La regla global es **la fuente de verdad**. El `CLAUDE.md` del proyecto
19
+ puede referenciarla con `@~/.claude/rules/<archivo>.md` o agregar
20
+ **matices propios del proyecto**, pero NUNCA re-derivar el principio.
21
+
22
+ ---
23
+
24
+ ## Cuándo aplicar
25
+
26
+ OBLIGATORIO al:
27
+
28
+ - Crear un `CLAUDE.md` de proyecto nuevo (`/swl:claudemd init-project`,
29
+ `/swl:nuevo-proyecto`, `/swl:adoptar-proyecto`).
30
+ - Modificar `CLAUDE.md` de proyecto inline (`/swl:aprender` Paso 6 Tipo A,
31
+ edición manual del usuario, `Edit`/`Write` programático).
32
+ - Revisar `CLAUDE.md` (`/swl:claudemd audit`, `/swl:claudemd refactor`,
33
+ hook `claudemd-bloat-detector`, hook `claudemd-duplicacion-detector`).
34
+
35
+ NO aplica al:
36
+
37
+ - `~/.claude/CLAUDE.md` (user-level) — ahí SÍ pueden declararse
38
+ preferencias personales que parafrasean o complementan reglas globales.
39
+ - Documentación dentro del proyecto que no es `CLAUDE.md` (READMEs,
40
+ ADRs, docs de feature) — ahí no aplica el contrato canónico.
41
+
42
+ ---
43
+
44
+ ## Catálogo de reglas globales conocidas
45
+
46
+ Las siguientes reglas globales se cargan automáticamente cada sesión y
47
+ NO deben duplicarse en `CLAUDE.md` de proyecto:
48
+
49
+ | Regla global | Sección canónica | Referencia para citar |
50
+ |---|---|---|
51
+ | `brevedad-output.md` | Idioma obligatorio: español de México | `@~/.claude/rules/brevedad-output.md § Idioma obligatorio` |
52
+ | `brevedad-output.md` | Brevedad y eficiencia de output | `@~/.claude/rules/brevedad-output.md § Brevedad` |
53
+ | `git-coauthor.md` | Sin co-autores en commits | `@~/.claude/rules/git-coauthor.md` |
54
+ | `arreglar-al-detectar.md` | Detectar → Informar → Arreglar | `@~/.claude/rules/arreglar-al-detectar.md` |
55
+ | `debatir-antes-de-aceptar.md` | Debatir decisiones que chocan con reglas | `@~/.claude/rules/debatir-antes-de-aceptar.md` |
56
+ | `usar-context7.md` | Consultar Context7 antes de dependencias | `@~/.claude/rules/usar-context7.md` |
57
+
58
+ Catálogo declarativo completo:
59
+ `scripts/lib/reglas-globales-conocidas.json`. El catálogo es la fuente
60
+ de verdad consumida por el auditor (`scripts/auditar-claudemd.js`) y el
61
+ hook (`hooks/claudemd-duplicacion-detector.js`).
62
+
63
+ ---
64
+
65
+ ## Qué SÍ es legítimo en CLAUDE.md de proyecto
66
+
67
+ Lo siguiente NO se considera duplicación y es bienvenido:
68
+
69
+ 1. **Matiz local específico**: "Convenciones locales: identificadores
70
+ técnicos en inglés (rutas, comandos), prosa en español." — el matiz
71
+ es del proyecto, no la regla del idioma.
72
+ 2. **Override explícito documentado**: "Override de
73
+ `~/.claude/rules/brevedad-output.md` § Brevedad: este proyecto usa
74
+ docstrings extendidos por requerimiento legal." — el override es
75
+ explícito y nombra la regla global.
76
+ 3. **Excepción acotada con justificación**: "Excepción a
77
+ `~/.claude/rules/arreglar-al-detectar.md`: bugs cosméticos en el
78
+ módulo legacy `X/` se difieren a Q3 por congelación." — la excepción
79
+ nombra la regla y justifica.
80
+ 4. **Referencia `@`**: `@~/.claude/rules/<archivo>.md` — incluir la
81
+ regla por referencia, no inline.
82
+
83
+ ---
84
+
85
+ ## Cómo detectarlo
86
+
87
+ ### Manual
88
+
89
+ ```bash
90
+ node scripts/auditar-claudemd.js
91
+ # Buscar en el output: "duplicacion-reglas-globales"
92
+ ```
93
+
94
+ ### Automático
95
+
96
+ - **Auditor síncrono**: `scripts/auditar-claudemd.js` (dimensión 7,
97
+ severidad WARN, no bloquea).
98
+ - **Hook async**: `hooks/claudemd-duplicacion-detector.js` (PostToolUse,
99
+ emite nudge a `.planning/evolucion/nudges.jsonl`).
100
+ - **Comando**: `/swl:claudemd audit` reporta el conteo; `/swl:claudemd
101
+ refactor` propone el reemplazo concreto.
102
+
103
+ ---
104
+
105
+ ## Cómo remediar
106
+
107
+ 1. **Identificar el bloque** señalado por el auditor (línea aproximada).
108
+ 2. **Decidir**:
109
+ - ¿El bloque es 100% paráfrasis de la regla global? → **eliminar**.
110
+ - ¿El bloque agrega matiz local? → **reescribir como matiz corto**
111
+ (≤3 líneas) que nombra la regla global ("Convenciones locales:
112
+ <matiz>. Ver `@~/.claude/rules/<archivo>.md`.").
113
+ 3. **Verificar**: re-ejecutar `node scripts/auditar-claudemd.js` hasta
114
+ que la duplicación desaparezca.
115
+
116
+ ---
117
+
118
+ ## Anti-patrones
119
+
120
+ - **Copiar contenido de reglas globales "para que esté visible"**: la
121
+ regla global YA está visible — se carga automáticamente. Copiarla en
122
+ cada proyecto es deuda silenciosa.
123
+ - **Re-derivar el principio con palabras propias**: si dices "todo
124
+ contenido generado debe ser en español de México con acentos
125
+ correctos…" estás re-derivando `brevedad-output.md § Idioma`.
126
+ - **Bloque "Reglas de máxima prioridad" que repite reglas globales**:
127
+ las reglas globales SON prioritarias por construcción — duplicarlas
128
+ no aumenta su prioridad.
129
+ - **Idioma "Language" en inglés que dice "español"**: paradójico y
130
+ duplica la regla.
131
+ - **Override implícito sin nombrar la regla global**: "este proyecto
132
+ usa docstrings extendidos" sin decir que es override de qué.
133
+ - **Ignorar el WARN del auditor argumentando "es para claridad"**: el
134
+ auditor lleva razón. La regla global es suficiente claridad.
135
+
136
+ ---
137
+
138
+ ## Relación con otras reglas
139
+
140
+ - `~/.claude/rules/memoria-consolidada.md § Reglas de no-duplicación`:
141
+ principio general "un dato vive en exactamente un canal". Esta regla
142
+ lo aplica al canal CLAUDE.md.
143
+ - `~/.claude/rules/fragmentos-compartidos.md`: la analogía a nivel de
144
+ agentes (fragmentos `_*.md` se incrustan, no se duplican).
145
+ - `~/.claude/rules/skills-estandar.md`: la analogía a nivel de skills
146
+ (skill referencia otro skill, no duplica su contenido).
147
+ - `reglas/auditorias-documentales-estructurales.md § Para prosa
148
+ cuantificada en campos descriptivos`: hermana — verifica drift
149
+ cross-manifest. Esta regla verifica drift cross-fuente.
150
+
151
+ ---
152
+
153
+ ## Origen de esta regla
154
+
155
+ Sesión 2026-05-22, swl-ses v1.7.0. El usuario pegó 4 fragmentos de
156
+ `CLAUDE.md` de distintos proyectos que repetían la misma regla de idioma
157
+ "español de México" en formas distintas (lista con bullets, H3 inline,
158
+ lista numerada, sección "Language" en inglés). El análisis reveló:
159
+
160
+ - La regla ya vivía en `~/.claude/rules/brevedad-output.md § Idioma
161
+ obligatorio: español de México` desde sesiones previas.
162
+ - Cada proyecto la había re-derivado por iteraciones de
163
+ `/swl:aprender` Tipo A sin que ningún check detectara la
164
+ duplicación.
165
+ - El bloat acumulado en cada `CLAUDE.md` consumía 7-15 líneas que
166
+ podían eliminarse sin perder semántica.
167
+
168
+ Resolución: crear el catálogo declarativo + detector + dimensión 7 del
169
+ auditor + hook PostToolUse + esta regla. Release v1.7.0 MINOR.
170
+
171
+ ---
172
+
173
+ ## Checklist al modificar CLAUDE.md de proyecto
174
+
175
+ - [ ] ¿El bloque que voy a agregar parafrasea una regla global de
176
+ `~/.claude/rules/`?
177
+ - [ ] Si sí: ¿agrega matiz local genuino, o solo repite el principio?
178
+ - [ ] Si solo repite: NO agregar — la regla global ya aplica.
179
+ - [ ] Si agrega matiz: redactar en ≤3 líneas que **nombran la regla
180
+ global** (referencia con `@~/.claude/rules/<archivo>.md`).
181
+ - [ ] Tras escribir: ejecutar `node scripts/auditar-claudemd.js` y
182
+ confirmar 0 hallazgos `duplicacion-reglas-globales`.
@@ -0,0 +1,139 @@
1
+ # Regla: Comentarios temporales en código NO son verificación
2
+
3
+ Esta regla es OBLIGATORIA para todo trabajo de Claude en swl-ses. Extiende la
4
+ regla global `~/.claude/rules/verificar-citas-normativas.md § Familia 2` con
5
+ el sub-caso específico de **comentarios temporales como `// Alineado con
6
+ commits X+Y` o `// Refactorizado a vN.M.0`**.
7
+
8
+ ---
9
+
10
+ ## Principio
11
+
12
+ > Un comentario en código que afirma alineación con otro commit, versión o
13
+ > contrato (`// Alineado con commits X+Y`, `// Refactorizado a vN.M.0`,
14
+ > `# Sincronizado con backend`, `<!-- Coherente con schema X -->`) **NO es
15
+ > verificación** — es afirmación temporal sin prueba. Antes de actuar
16
+ > sobre el comentario como si fuera evidencia, verificar contra la fuente
17
+ > real con `grep` / `Read` / `git log`.
18
+
19
+ El comentario puede haberse desactualizado silenciosamente si el commit
20
+ referenciado se revirtió, nunca se aplicó, o si el refactor citado cambió
21
+ después de escribir el comentario.
22
+
23
+ ---
24
+
25
+ ## Cuándo aplicar
26
+
27
+ OBLIGATORIO verificar antes de aceptar como evidencia accionable:
28
+
29
+ - Comentarios `// Alineado con commits <SHA>+<SHA>` en código
30
+ - Comentarios `// Refactorizado a v<N.M.0>` o `// Migrado a <framework>`
31
+ - Comentarios `# Sincronizado con backend` o `# Espejo del schema X`
32
+ - Comentarios `<!-- Tipos coherentes con API vN -->`
33
+ - Cualquier afirmación de cross-stack alignment en comentario inline
34
+
35
+ NO aplica cuando:
36
+
37
+ - El comentario describe la INTENCIÓN del código (no afirma alineación)
38
+ - El comentario es una nota pendiente (`T‍ODO` / `FIXME`) con ticket asociado
39
+ - El comentario es una explicación del POR QUÉ (no del QUÉ histórico)
40
+
41
+ ---
42
+
43
+ ## Cómo verificar
44
+
45
+ Cuando un comentario afirma "alineado con X":
46
+
47
+ 1. Identificar **qué se afirma alineado**: archivo, commit, versión, schema.
48
+ 2. Localizar la fuente real:
49
+ ```bash
50
+ # Si afirma alineación con commit:
51
+ git show <SHA> -- <archivo-citado>
52
+
53
+ # Si afirma alineación con schema/types:
54
+ grep -rn "<campo-clave>" backend/ frontend/ schemas/
55
+ ```
56
+ 3. Comparar campo por campo. Si hay drift, NO confiar en el comentario.
57
+ 4. Actualizar el comentario o eliminarlo (un comentario stale es peor que
58
+ ningún comentario).
59
+
60
+ ---
61
+
62
+ ## Caso de origen — sesión SIGAF 2026-05-22
63
+
64
+ `frontend/.../contratos/models/contrato.models.ts` línea 2 decía:
65
+
66
+ ```typescript
67
+ // Alineados con el backend refactorizado (commits 284df74 + 5869ac9)
68
+ ```
69
+
70
+ Pero el backend nunca aplicó ese refactor. El frontend enviaba 5 campos
71
+ (`monto_total`, `fecha_inicio`, `fecha_termino`, `porcentaje_iva`,
72
+ `tipo_contrato_id`) que Pydantic con `extra=ignore` descartaba
73
+ silenciosamente. Los contratos se creaban sin monto ni vigencia durante
74
+ semanas, hasta que se detectó por triangulación cross-stack (modelo
75
+ SQLAlchemy + schema Pydantic + types TypeScript).
76
+
77
+ El comentario funcionó como **falso anclaje de confianza**: un reviewer
78
+ posterior lo leyó, asumió validez, y no verificó. La causa raíz era el
79
+ comentario stale, no el código.
80
+
81
+ ---
82
+
83
+ ## Anti-patrones específicos
84
+
85
+ - **Confiar en el comentario en code review**: el reviewer ve
86
+ `// Alineado con commits X+Y` y asume válido sin hacer `git show`.
87
+ - **Propagar el comentario en refactors**: al copiar código, el comentario
88
+ viaja con él y eventualmente se vuelve falso sin que nadie lo note.
89
+ - **No actualizar el comentario al revertir el commit citado**: el `git
90
+ revert` no borra comentarios — el comentario sobrevive afirmando
91
+ alineación con un commit que ya no existe en la línea de desarrollo.
92
+ - **Usar el comentario como "prueba" en debates técnicos**: "ya está
93
+ alineado con X" cuando el comentario es la única evidencia es razonar
94
+ desde una afirmación no verificada.
95
+
96
+ ---
97
+
98
+ ## Alternativas seguras a los comentarios temporales
99
+
100
+ Cuando se necesita marcar alineación cross-stack:
101
+
102
+ | En vez de | Usar |
103
+ |-----------|------|
104
+ | `// Alineado con commits X+Y` | Test de contrato que valide el shape real en CI |
105
+ | `// Refactorizado a v2.3.0` | Versión semántica en el archivo + lock |
106
+ | `# Sincronizado con backend` | Tipos generados (OpenAPI codegen, Prisma) |
107
+ | `<!-- Coherente con schema X -->` | Validador automatizado en pre-commit |
108
+
109
+ Si el comentario es la única opción (no hay test/codegen disponible),
110
+ incluir **fecha y SHA** explícitos para que la revisión periódica detecte
111
+ drift:
112
+
113
+ ```typescript
114
+ // Alineado con backend commit 284df74 al 2026-05-22.
115
+ // Re-verificar si el endpoint POST /contratos cambia su contrato.
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Relación con otras reglas
121
+
122
+ - `~/.claude/rules/verificar-citas-normativas.md § Familia 2` — regla
123
+ global hermana. Esta regla extiende el principio al sub-caso "comentarios
124
+ temporales en código".
125
+ - `reglas/arreglar-al-detectar.md` — un comentario stale detectado es
126
+ trabajo a resolver en mismo turno (actualizarlo o eliminarlo).
127
+ - `~/.claude/rules/usar-sistema-swl.md` — invocar `revisor-codigo-swl`
128
+ cuando se sospecha drift acumulado de comentarios temporales en un
129
+ módulo.
130
+
131
+ ---
132
+
133
+ ## Checklist al detectar un comentario temporal
134
+
135
+ - [ ] ¿El comentario afirma alineación con algo verificable (commit, schema, versión)?
136
+ - [ ] ¿Verifiqué la fuente real con `grep`/`Read`/`git show` ANTES de actuar?
137
+ - [ ] Si hay drift detectado, ¿actualicé/eliminé el comentario en el mismo commit?
138
+ - [ ] Si voy a propagar el código (copy-paste, refactor), ¿el comentario sigue siendo válido?
139
+ - [ ] Si el comentario es la única opción, ¿incluye fecha y SHA explícitos?
@@ -29,6 +29,17 @@ const fs = require('fs');
29
29
  const path = require('path');
30
30
  const os = require('os');
31
31
 
32
+ // Lazy: el detector vive en scripts/lib/ y puede no estar presente en
33
+ // destinos legacy. Si falta, la auditoría sigue con las 7 dimensiones
34
+ // originales sin la dimensión 8.
35
+ function cargarDetectorReglasDuplicadas() {
36
+ try {
37
+ return require('./lib/detector-reglas-duplicadas').detectarDuplicaciones;
38
+ } catch (_) {
39
+ return null;
40
+ }
41
+ }
42
+
32
43
  // ─── Config ───────────────────────────────────────────────────────────────
33
44
  const MAX_LINES = parseInt(process.env.SWL_CLAUDEMD_MAX_LINES, 10) || 200;
34
45
  const MAX_BULLET_CHARS =
@@ -154,6 +165,29 @@ function auditar(rutaClaudeMd) {
154
165
  });
155
166
  }
156
167
 
168
+ // 7. Duplicación de reglas globales (~/.claude/rules/) en CLAUDE.md de proyecto.
169
+ // Solo aplica a project-level (user-level puede declarar preferencias).
170
+ let resultadoDuplicaciones = { evaluado: false, duplicaciones: [], total_reglas_evaluadas: 0 };
171
+ const detectarDuplicaciones = cargarDetectorReglasDuplicadas();
172
+ if (detectarDuplicaciones) {
173
+ try {
174
+ resultadoDuplicaciones = detectarDuplicaciones(contenido, null, {
175
+ esUserLevel: !esProjectLevel,
176
+ });
177
+ for (const dup of resultadoDuplicaciones.duplicaciones) {
178
+ hallazgos.push({
179
+ severidad: dup.severidad || 'WARN',
180
+ regla: 'duplicacion-reglas-globales',
181
+ mensaje: `Bloque duplica regla global \`~/.claude/rules/${dup.regla_global}\` (línea ~${dup.linea_aproximada})`,
182
+ sugerencia: dup.remediacion,
183
+ referencia_canonica: dup.referencia_canonica,
184
+ });
185
+ }
186
+ } catch (e) {
187
+ // Si el catálogo está corrupto o falla, no bloqueamos la auditoría.
188
+ }
189
+ }
190
+
157
191
  // ─── Veredicto ───────────────────────────────────────────────────────────
158
192
  const tieneError = hallazgos.some(h => h.severidad === 'ERROR');
159
193
  const tieneWarn = hallazgos.some(h => h.severidad === 'WARN');
@@ -172,6 +206,12 @@ function auditar(rutaClaudeMd) {
172
206
  tiene_at_references: /@[a-zA-Z][a-zA-Z0-9_\-./]+\.md/.test(contenido),
173
207
  tiene_referencia_karpathy: tieneReferenciaKarpathy,
174
208
  es_project_level: esProjectLevel,
209
+ duplicaciones_reglas_globales: {
210
+ evaluado: resultadoDuplicaciones.evaluado,
211
+ total_reglas_evaluadas: resultadoDuplicaciones.total_reglas_evaluadas,
212
+ detectadas: resultadoDuplicaciones.duplicaciones.length,
213
+ ids: resultadoDuplicaciones.duplicaciones.map(d => d.id),
214
+ },
175
215
  },
176
216
  hallazgos,
177
217
  };
@@ -299,6 +339,10 @@ function imprimirReporte(resultado) {
299
339
  if (m.es_project_level) {
300
340
  console.log(` - Referencia Karpathy: ${m.tiene_referencia_karpathy ? 'sí' : 'no'}`);
301
341
  }
342
+ if (m.duplicaciones_reglas_globales && m.duplicaciones_reglas_globales.evaluado) {
343
+ const d = m.duplicaciones_reglas_globales;
344
+ console.log(` - Duplicaciones de reglas globales: ${d.detectadas}/${d.total_reglas_evaluadas} reglas duplicadas${d.detectadas > 0 ? ' (' + d.ids.join(', ') + ')' : ''}`);
345
+ }
302
346
  console.log('');
303
347
  }
304
348
 
@@ -354,4 +398,6 @@ module.exports = {
354
398
  MAX_LINES,
355
399
  MAX_BULLET_CHARS,
356
400
  KARPATHY_MIN_LINES,
401
+ // Re-export lazy del detector (puede ser null si lib/ no está disponible):
402
+ cargarDetectorReglasDuplicadas,
357
403
  };