pumuki-ast-hooks 5.5.50 → 5.5.53

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/README.md CHANGED
@@ -866,6 +866,15 @@ Automates the complete Git Flow cycle: commit → push → PR → merge, plus co
866
866
 
867
867
  For more details, see [MCP_SERVERS.md](./docs/MCP_SERVERS.md).
868
868
 
869
+ #### Troubleshooting
870
+
871
+ If `ai_gate_check` behaves inconsistently (stale branch name, missing rules, or intermittent transport errors), verify you are not running multiple `ast-intelligence-automation` servers across different repositories.
872
+
873
+ - Prefer enabling a single MCP server for the repository you are working on.
874
+ - Verify the active process points to this repository path:
875
+ - `.../ast-intelligence-hooks/scripts/hooks-system/infrastructure/mcp/ast-intelligence-automation.js`
876
+ - If you detect multiple processes, stop the duplicates and restart your IDE/MCP servers.
877
+
869
878
  ---
870
879
 
871
880
  ## API Reference
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki-ast-hooks",
3
- "version": "5.5.50",
3
+ "version": "5.5.53",
4
4
  "description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -110,3 +110,59 @@
110
110
  {"timestamp":1767772971939,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
111
111
  {"timestamp":1767772971939,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
112
112
  {"timestamp":1767772971939,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
113
+ {"timestamp":1767775802167,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
114
+ {"timestamp":1767775802167,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
115
+ {"timestamp":1767775802167,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
116
+ {"timestamp":1767775802167,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
117
+ {"timestamp":1767775833943,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
118
+ {"timestamp":1767775833943,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
119
+ {"timestamp":1767775833943,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
120
+ {"timestamp":1767775833943,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
121
+ {"timestamp":1767776135844,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
122
+ {"timestamp":1767776135844,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
123
+ {"timestamp":1767776135844,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
124
+ {"timestamp":1767776135844,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
125
+ {"timestamp":1767776384649,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
126
+ {"timestamp":1767776384649,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
127
+ {"timestamp":1767776384649,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
128
+ {"timestamp":1767776384649,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
129
+ {"timestamp":1767777653343,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
130
+ {"timestamp":1767777653343,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
131
+ {"timestamp":1767777653343,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
132
+ {"timestamp":1767777653344,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
133
+ {"timestamp":1767778142566,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
134
+ {"timestamp":1767778142567,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
135
+ {"timestamp":1767778142567,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
136
+ {"timestamp":1767778142567,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
137
+ {"timestamp":1767778800954,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
138
+ {"timestamp":1767778800955,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
139
+ {"timestamp":1767778800955,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
140
+ {"timestamp":1767778800955,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
141
+ {"timestamp":1767779528552,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
142
+ {"timestamp":1767779528552,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
143
+ {"timestamp":1767779528552,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
144
+ {"timestamp":1767779528552,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
145
+ {"timestamp":1767779903813,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
146
+ {"timestamp":1767779903813,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
147
+ {"timestamp":1767779903813,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
148
+ {"timestamp":1767779903813,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
149
+ {"timestamp":1767780497468,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
150
+ {"timestamp":1767780497468,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
151
+ {"timestamp":1767780497468,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
152
+ {"timestamp":1767780497468,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
153
+ {"timestamp":1767780655724,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
154
+ {"timestamp":1767780655725,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
155
+ {"timestamp":1767780655725,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
156
+ {"timestamp":1767780655725,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
157
+ {"timestamp":1767782291627,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
158
+ {"timestamp":1767782291627,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
159
+ {"timestamp":1767782291627,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
160
+ {"timestamp":1767782291627,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
161
+ {"timestamp":1767784424360,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
162
+ {"timestamp":1767784424360,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
163
+ {"timestamp":1767784424360,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
164
+ {"timestamp":1767784424360,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
165
+ {"timestamp":1767784524038,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
166
+ {"timestamp":1767784524038,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
167
+ {"timestamp":1767784524038,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
168
+ {"timestamp":1767784524038,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
@@ -31,10 +31,47 @@ class AutonomousOrchestrator {
31
31
  }
32
32
  }
33
33
 
34
+ detectFromBranchKeywords(branchName) {
35
+ try {
36
+ const PlatformHeuristics = require('./platform/PlatformHeuristics');
37
+ const heuristics = new PlatformHeuristics(this.platformDetector);
38
+ return heuristics.detectFromBranchKeywords(branchName);
39
+ } catch (error) {
40
+ const msg = error && error.message ? error.message : String(error);
41
+ this.logger?.debug?.('ORCHESTRATOR_BRANCH_KEYWORDS_DETECTION_ERROR', { error: msg });
42
+ return [];
43
+ }
44
+ }
45
+
46
+ detectFromEvidenceFile() {
47
+ try {
48
+ const PlatformHeuristics = require('./platform/PlatformHeuristics');
49
+ const heuristics = new PlatformHeuristics(this.platformDetector);
50
+ return heuristics.detectFromEvidenceFile();
51
+ } catch (error) {
52
+ const msg = error && error.message ? error.message : String(error);
53
+ this.logger?.debug?.('ORCHESTRATOR_EVIDENCE_FILE_DETECTION_ERROR', { error: msg });
54
+ return [];
55
+ }
56
+ }
57
+
34
58
  detectFromASTSystemFilesLegacy(files) {
35
59
  return this.detectFromASTSystemFiles(files);
36
60
  }
37
61
 
62
+ async scoreConfidence(platforms) {
63
+ try {
64
+ const PlatformAnalysisService = require('./PlatformAnalysisService');
65
+ const analysisService = new PlatformAnalysisService(this.platformDetector);
66
+ const context = await this.contextEngine.detectContext();
67
+ return analysisService.analyzeConfidence(platforms || [], context || {});
68
+ } catch (error) {
69
+ const msg = error && error.message ? error.message : String(error);
70
+ this.logger?.debug?.('ORCHESTRATOR_SCORE_CONFIDENCE_ERROR', { error: msg });
71
+ return [];
72
+ }
73
+ }
74
+
38
75
  async analyzeContext() {
39
76
  const platforms = await this.detectActivePlatforms();
40
77
  const scores = await this.scoreConfidence(platforms);
@@ -130,33 +130,99 @@ class McpConfigurator {
130
130
  }
131
131
  };
132
132
 
133
- const globalConfigPath = this.getGlobalWindsurfConfigPath();
134
- const globalConfigDir = path.dirname(globalConfigPath);
135
- if (!fs.existsSync(globalConfigDir)) {
136
- fs.mkdirSync(globalConfigDir, { recursive: true });
137
- }
133
+ this.configureProjectScoped(mcpConfig, serverId);
134
+ this.cleanupGlobalConfig(serverId);
135
+ }
136
+
137
+ configureProjectScoped(mcpConfig, serverId) {
138
+ const windsurfProjectDir = path.join(this.targetRoot, '.windsurf');
139
+ const windsurfProjectPath = path.join(windsurfProjectDir, 'mcp.json');
138
140
 
139
141
  try {
140
- if (!fs.existsSync(globalConfigPath)) {
141
- fs.writeFileSync(globalConfigPath, JSON.stringify(mcpConfig, null, 2));
142
- this.logSuccess(`Configured global Windsurf MCP at ${globalConfigPath}`);
143
- if (this.logger) this.logger.info('MCP_GLOBAL_CONFIGURED', { path: globalConfigPath });
144
- } else {
145
- const existing = JSON.parse(fs.readFileSync(globalConfigPath, 'utf8'));
142
+ if (!fs.existsSync(windsurfProjectDir)) {
143
+ fs.mkdirSync(windsurfProjectDir, { recursive: true });
144
+ }
145
+
146
+ let finalConfig = mcpConfig;
147
+ if (fs.existsSync(windsurfProjectPath)) {
148
+ const existing = JSON.parse(fs.readFileSync(windsurfProjectPath, 'utf8'));
146
149
  if (!existing.mcpServers) existing.mcpServers = {};
150
+ Object.keys(existing.mcpServers).forEach(id => {
151
+ if (id.startsWith('ast-intelligence-automation-') && id !== serverId) {
152
+ delete existing.mcpServers[id];
153
+ }
154
+ });
155
+ existing.mcpServers[serverId] = mcpConfig.mcpServers[serverId];
156
+ finalConfig = existing;
157
+ }
158
+
159
+ fs.writeFileSync(windsurfProjectPath, JSON.stringify(finalConfig, null, 2));
160
+ this.logSuccess(`Configured project-scoped Windsurf MCP at ${windsurfProjectPath}`);
161
+ if (this.logger) this.logger.info('MCP_PROJECT_CONFIGURED', { path: windsurfProjectPath, serverId });
162
+ } catch (error) {
163
+ this.logWarning(`Failed to configure project-scoped MCP: ${error.message}`);
164
+ if (this.logger) this.logger.warn('MCP_PROJECT_CONFIGURE_FAILED', { error: error.message });
165
+ }
147
166
 
148
- // Prevent duplicate MCP servers for the same repoRoot by disabling legacy entries.
149
- this.disableDuplicateServersForRepo(existing, serverId);
167
+ const cursorProjectDir = path.join(this.targetRoot, '.cursor');
168
+ const cursorProjectPath = path.join(cursorProjectDir, 'mcp.json');
150
169
 
170
+ try {
171
+ if (!fs.existsSync(cursorProjectDir)) {
172
+ fs.mkdirSync(cursorProjectDir, { recursive: true });
173
+ }
174
+
175
+ let finalConfig = mcpConfig;
176
+ if (fs.existsSync(cursorProjectPath)) {
177
+ const existing = JSON.parse(fs.readFileSync(cursorProjectPath, 'utf8'));
178
+ if (!existing.mcpServers) existing.mcpServers = {};
179
+ Object.keys(existing.mcpServers).forEach(id => {
180
+ if (id.startsWith('ast-intelligence-automation-') && id !== serverId) {
181
+ delete existing.mcpServers[id];
182
+ }
183
+ });
151
184
  existing.mcpServers[serverId] = mcpConfig.mcpServers[serverId];
185
+ finalConfig = existing;
186
+ }
187
+
188
+ fs.writeFileSync(cursorProjectPath, JSON.stringify(finalConfig, null, 2));
189
+ this.logSuccess(`Configured project-scoped Cursor MCP at ${cursorProjectPath}`);
190
+ if (this.logger) this.logger.info('MCP_CURSOR_CONFIGURED', { path: cursorProjectPath, serverId });
191
+ } catch (error) {
192
+ this.logWarning(`Failed to configure Cursor MCP: ${error.message}`);
193
+ if (this.logger) this.logger.warn('MCP_CURSOR_CONFIGURE_FAILED', { error: error.message });
194
+ }
195
+ }
196
+
197
+ cleanupGlobalConfig(currentServerId) {
198
+ const globalConfigPath = this.getGlobalWindsurfConfigPath();
152
199
 
200
+ try {
201
+ if (!fs.existsSync(globalConfigPath)) return;
202
+
203
+ const existing = JSON.parse(fs.readFileSync(globalConfigPath, 'utf8'));
204
+ if (!existing.mcpServers) return;
205
+
206
+ let modified = false;
207
+ Object.keys(existing.mcpServers).forEach(id => {
208
+ const server = existing.mcpServers[id];
209
+ if (!server || !server.env) return;
210
+
211
+ if (server.env.REPO_ROOT === this.targetRoot) {
212
+ delete existing.mcpServers[id];
213
+ modified = true;
214
+ this.logInfo(`Removed ${id} from global config (now project-scoped)`);
215
+ }
216
+ });
217
+
218
+ if (modified) {
153
219
  fs.writeFileSync(globalConfigPath, JSON.stringify(existing, null, 2));
154
- this.logSuccess(`Updated global Windsurf MCP at ${globalConfigPath}`);
155
- if (this.logger) this.logger.info('MCP_GLOBAL_UPDATED', { path: globalConfigPath });
220
+ if (this.logger) this.logger.info('MCP_GLOBAL_CLEANUP', { removedForRepo: this.targetRoot });
221
+ }
222
+ } catch (error) {
223
+ if (process.env.DEBUG) {
224
+ process.stderr.write(`[MCP] cleanupGlobalConfig failed: ${error.message}\n`);
156
225
  }
157
- } catch (mergeError) {
158
- this.logWarning(`${globalConfigPath} exists but couldn't be merged, skipping`);
159
- if (this.logger) this.logger.warn('MCP_GLOBAL_MERGE_FAILED', { error: mergeError.message });
160
226
  }
161
227
  }
162
228
 
@@ -576,9 +576,293 @@ function checkBranchChangesCoherence(branchName, uncommittedChanges) {
576
576
  return { isCoherent: true, expectedScope, detectedScope: dominantScope, reason: 'Changes match branch scope' };
577
577
  }
578
578
 
579
+ /**
580
+ * Load platform rules from .cursor/rules or .windsurf/rules
581
+ * Returns the content of the rules file for the detected platform
582
+ */
583
+ async function loadPlatformRules(platforms) {
584
+ const DynamicRulesLoader = require('../../application/services/DynamicRulesLoader');
585
+ const loader = new DynamicRulesLoader();
586
+ const rules = {};
587
+ const criticalRules = [];
588
+
589
+ for (const platform of platforms) {
590
+ try {
591
+ const content = await loader.loadRule(`rules${platform}.mdc`);
592
+ if (content) {
593
+ rules[platform] = content;
594
+ const criticalPatterns = extractCriticalPatterns(content, platform);
595
+ criticalRules.push(...criticalPatterns);
596
+ }
597
+ } catch (error) {
598
+ if (process.env.DEBUG) {
599
+ process.stderr.write(`[MCP] Failed to load rules for ${platform}: ${error.message}\n`);
600
+ }
601
+ }
602
+ }
603
+
604
+ try {
605
+ const goldContent = await loader.loadRule('rulesgold.mdc');
606
+ if (goldContent) {
607
+ rules.gold = goldContent;
608
+ const goldPatterns = extractCriticalPatterns(goldContent, 'gold');
609
+ criticalRules.push(...goldPatterns);
610
+ }
611
+ } catch (error) {
612
+ if (process.env.DEBUG) {
613
+ process.stderr.write(`[MCP] Failed to load gold rules: ${error.message}\n`);
614
+ }
615
+ }
616
+
617
+ return { rules, criticalRules };
618
+ }
619
+
620
+ /**
621
+ * Extract critical patterns from rules content that AI MUST follow
622
+ */
623
+ function extractCriticalPatterns(content, platform) {
624
+ const patterns = [];
625
+ if (!content) return patterns;
626
+
627
+ const lines = content.split('\n');
628
+ for (const line of lines) {
629
+ if (line.includes('❌') || line.includes('NUNCA') || line.includes('PROHIBIDO') || line.includes('NO ')) {
630
+ patterns.push({ platform, rule: line.trim(), severity: 'CRITICAL' });
631
+ }
632
+ if (line.includes('✅') && (line.includes('OBLIGATORIO') || line.includes('SIEMPRE'))) {
633
+ patterns.push({ platform, rule: line.trim(), severity: 'MANDATORY' });
634
+ }
635
+ }
636
+
637
+ if (platform === 'ios') {
638
+ // PROHIBICIONES CRÍTICAS iOS
639
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA usar GCD (DispatchQueue.main.async, DispatchQueue.global) - usar Swift Concurrency (async/await, Task, actor, @MainActor)', severity: 'CRITICAL' });
640
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA usar completion handlers/@escaping callbacks - usar async/await', severity: 'CRITICAL' });
641
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA dejar catch vacíos - siempre gestionar errores (log/rethrow/wrap)', severity: 'CRITICAL' });
642
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA mezclar operaciones de escritura (commands) con lecturas (queries) en el mismo flujo/servicio - aplicar CQRS', severity: 'CRITICAL' });
643
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA usar Singleton - usar Inyección de Dependencias (DI por init/protocols)', severity: 'CRITICAL' });
644
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA usar force unwrap (!) - usar guard let/if let/?? (excepción: IBOutlets)', severity: 'CRITICAL' });
645
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA usar librerías de terceros (Alamofire, Swinject, Quick/Nimble, etc.) - usar APIs nativas (URLSession, DI manual, Swift Testing)', severity: 'CRITICAL' });
646
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA añadir comentarios en el código - nombres autodescriptivos', severity: 'CRITICAL' });
647
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA usar prints/logs ad-hoc - usar Logger del framework os', severity: 'CRITICAL' });
648
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA usar AnyView - afecta performance, usar @ViewBuilder o generics', severity: 'CRITICAL' });
649
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA usar ObservableObject - usar @Observable (iOS 17+)', severity: 'CRITICAL' });
650
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA usar Storyboards/XIBs - usar SwiftUI o Programmatic UI', severity: 'CRITICAL' });
651
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA usar CocoaPods/Carthage - usar Swift Package Manager', severity: 'CRITICAL' });
652
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA usar JSONSerialization - usar Codable', severity: 'CRITICAL' });
653
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA usar OperationQueue para async - usar Task/TaskGroup', severity: 'CRITICAL' });
654
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA usar any (type erasure) - usar generics con protocolos de frontera', severity: 'CRITICAL' });
655
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA usar Localizable.strings - usar String Catalogs (.xcstrings)', severity: 'CRITICAL' });
656
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA ignorar warnings - warnings = errores futuros', severity: 'CRITICAL' });
657
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA crear Massive View Controllers (>300 líneas) - separar lógica en ViewModels', severity: 'CRITICAL' });
658
+ patterns.push({ platform: 'ios', rule: '❌ NUNCA crear retain cycles - usar [weak self] en closures', severity: 'CRITICAL' });
659
+
660
+ // OBLIGATORIOS iOS
661
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar Swift 6.2 con Strict Concurrency Checking en Complete', severity: 'MANDATORY' });
662
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar async/await para TODAS las operaciones asíncronas', severity: 'MANDATORY' });
663
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar SwiftUI (UIKit solo si estrictamente necesario)', severity: 'MANDATORY' });
664
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar @Observable (iOS 17+) con @Bindable para bindings', severity: 'MANDATORY' });
665
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar NavigationStack + NavigationPath para navegación', severity: 'MANDATORY' });
666
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar guard/early returns - evitar pyramid of doom', severity: 'MANDATORY' });
667
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO seguir Clean Architecture (Domain → Application → Infrastructure → Presentation)', severity: 'MANDATORY' });
668
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO aplicar CQRS (Command Query Responsibility Segregation): separar comandos (write) de consultas (read)', severity: 'MANDATORY' });
669
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO verificar SOLID (SRP, OCP, LSP, ISP, DIP)', severity: 'MANDATORY' });
670
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO nombres autodescriptivos en inglés', severity: 'MANDATORY' });
671
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO comprobar que compila (Xcode build) ANTES de sugerir', severity: 'MANDATORY' });
672
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar Sendable conformance para tipos thread-safe', severity: 'MANDATORY' });
673
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar actor para estado compartido thread-safe', severity: 'MANDATORY' });
674
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar @MainActor para código UI', severity: 'MANDATORY' });
675
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar Task {} para lanzar contextos asíncronos desde código síncrono', severity: 'MANDATORY' });
676
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar Keychain nativo para datos sensibles (NO UserDefaults)', severity: 'MANDATORY' });
677
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar SwiftData (iOS 17+) para persistencia (CoreData solo legacy)', severity: 'MANDATORY' });
678
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar Swift Testing framework para tests (XCTest solo legacy)', severity: 'MANDATORY' });
679
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar makeSUT pattern en tests', severity: 'MANDATORY' });
680
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar trackForMemoryLeaks helper en tests', severity: 'MANDATORY' });
681
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar spies en vez de mocks/stubs', severity: 'MANDATORY' });
682
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar protocols para testability (inyectar protocols, no tipos concretos)', severity: 'MANDATORY' });
683
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar struct por defecto, class solo cuando necesites identity/herencia', severity: 'MANDATORY' });
684
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO usar let > var (inmutabilidad por defecto)', severity: 'MANDATORY' });
685
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO cero warnings en el proyecto', severity: 'MANDATORY' });
686
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO seguir flujo BDD → TDD (Feature files → Specs → Implementación)', severity: 'MANDATORY' });
687
+ patterns.push({ platform: 'ios', rule: '✅ OBLIGATORIO en producción todo real contra APIs/persistencia (cero mocks/spies)', severity: 'MANDATORY' });
688
+ }
689
+
690
+ if (platform === 'android') {
691
+ // PROHIBICIONES CRÍTICAS Android
692
+ patterns.push({ platform: 'android', rule: '❌ NUNCA usar Singleton - usar Hilt para Inyección de Dependencias', severity: 'CRITICAL' });
693
+ patterns.push({ platform: 'android', rule: '❌ NUNCA usar XML layouts - usar Jetpack Compose', severity: 'CRITICAL' });
694
+ patterns.push({ platform: 'android', rule: '❌ NUNCA usar Java en código nuevo - usar Kotlin 100%', severity: 'CRITICAL' });
695
+ patterns.push({ platform: 'android', rule: '❌ NUNCA usar LiveData - usar StateFlow/SharedFlow', severity: 'CRITICAL' });
696
+ patterns.push({ platform: 'android', rule: '❌ NUNCA dejar catch vacíos - siempre gestionar errores', severity: 'CRITICAL' });
697
+ patterns.push({ platform: 'android', rule: '❌ NUNCA mezclar operaciones de escritura (commands) con lecturas (queries) en el mismo caso de uso/repository - aplicar CQRS', severity: 'CRITICAL' });
698
+ patterns.push({ platform: 'android', rule: '❌ NUNCA añadir comentarios en el código', severity: 'CRITICAL' });
699
+ patterns.push({ platform: 'android', rule: '❌ NUNCA usar force unwrap (!!) - usar ?, ?:, let, requireNotNull', severity: 'CRITICAL' });
700
+ patterns.push({ platform: 'android', rule: '❌ NUNCA usar AsyncTask (deprecated) - usar Coroutines', severity: 'CRITICAL' });
701
+ patterns.push({ platform: 'android', rule: '❌ NUNCA usar RxJava en código nuevo - usar Flow', severity: 'CRITICAL' });
702
+ patterns.push({ platform: 'android', rule: '❌ NUNCA usar findViewById - usar Compose o View Binding', severity: 'CRITICAL' });
703
+ patterns.push({ platform: 'android', rule: '❌ NUNCA usar Context leaks - no referencias a Activity en objetos long-lived', severity: 'CRITICAL' });
704
+ patterns.push({ platform: 'android', rule: '❌ NUNCA usar God Activities - Single Activity + Composables', severity: 'CRITICAL' });
705
+ patterns.push({ platform: 'android', rule: '❌ NUNCA hardcodear strings - usar strings.xml', severity: 'CRITICAL' });
706
+ patterns.push({ platform: 'android', rule: '❌ NUNCA usar callbacks para async - usar suspend functions', severity: 'CRITICAL' });
707
+
708
+ // OBLIGATORIOS Android
709
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Jetpack Compose + Material 3', severity: 'MANDATORY' });
710
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Hilt para DI (@HiltAndroidApp, @AndroidEntryPoint, @Inject)', severity: 'MANDATORY' });
711
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Kotlin Coroutines + Flow para async', severity: 'MANDATORY' });
712
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar StateFlow para exponer estado del ViewModel', severity: 'MANDATORY' });
713
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar sealed classes para estados (Success, Error, Loading)', severity: 'MANDATORY' });
714
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO seguir Clean Architecture (Domain → Data → Presentation)', severity: 'MANDATORY' });
715
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO aplicar CQRS (Command Query Responsibility Segregation): separar comandos (write) de consultas (read)', severity: 'MANDATORY' });
716
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO verificar SOLID', severity: 'MANDATORY' });
717
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar MVVM + Single Activity', severity: 'MANDATORY' });
718
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Navigation Compose para navegación', severity: 'MANDATORY' });
719
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Room para persistencia local', severity: 'MANDATORY' });
720
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Retrofit + Moshi para networking', severity: 'MANDATORY' });
721
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Coil para carga de imágenes', severity: 'MANDATORY' });
722
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Kotlin DSL (build.gradle.kts)', severity: 'MANDATORY' });
723
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Version Catalogs (libs.versions.toml)', severity: 'MANDATORY' });
724
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar JUnit5 + MockK para testing', severity: 'MANDATORY' });
725
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar Timber para logging (solo en DEBUG)', severity: 'MANDATORY' });
726
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO usar EncryptedSharedPreferences para datos sensibles', severity: 'MANDATORY' });
727
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO nombres autodescriptivos en inglés', severity: 'MANDATORY' });
728
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO comprobar que compila (Gradle build) ANTES de sugerir', severity: 'MANDATORY' });
729
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO seguir flujo BDD → TDD', severity: 'MANDATORY' });
730
+ patterns.push({ platform: 'android', rule: '✅ OBLIGATORIO en producción todo real contra APIs/persistencia', severity: 'MANDATORY' });
731
+ }
732
+
733
+ if (platform === 'backend') {
734
+ // PROHIBICIONES CRÍTICAS Backend
735
+ patterns.push({ platform: 'backend', rule: '❌ NUNCA usar Singleton - usar NestJS DI (@Injectable)', severity: 'CRITICAL' });
736
+ patterns.push({ platform: 'backend', rule: '❌ NUNCA usar console.log/console.error - usar Logger de NestJS', severity: 'CRITICAL' });
737
+ patterns.push({ platform: 'backend', rule: '❌ NUNCA usar any/unknown en TypeScript - usar tipos explícitos o generics', severity: 'CRITICAL' });
738
+ patterns.push({ platform: 'backend', rule: '❌ NUNCA dejar catch vacíos - siempre loggear o propagar (common.error.empty_catch)', severity: 'CRITICAL' });
739
+ patterns.push({ platform: 'backend', rule: '❌ NUNCA mezclar Command y Query en el mismo handler/service - aplicar CQRS', severity: 'CRITICAL' });
740
+ patterns.push({ platform: 'backend', rule: '❌ NUNCA hardcodear secretos - usar variables de entorno (AWS Secrets Manager, Vault)', severity: 'CRITICAL' });
741
+ patterns.push({ platform: 'backend', rule: '❌ NUNCA añadir comentarios en el código', severity: 'CRITICAL' });
742
+ patterns.push({ platform: 'backend', rule: '❌ NUNCA exponer datos sin validar - usar class-validator + DTOs', severity: 'CRITICAL' });
743
+ patterns.push({ platform: 'backend', rule: '❌ NUNCA exponer stack traces en producción', severity: 'CRITICAL' });
744
+ patterns.push({ platform: 'backend', rule: '❌ NUNCA usar mocks en producción - solo datos reales', severity: 'CRITICAL' });
745
+ patterns.push({ platform: 'backend', rule: '❌ NUNCA loggear datos sensibles (passwords, tokens, PII)', severity: 'CRITICAL' });
746
+ patterns.push({ platform: 'backend', rule: '❌ NUNCA usar God classes (servicios >500 líneas)', severity: 'CRITICAL' });
747
+ patterns.push({ platform: 'backend', rule: '❌ NUNCA usar callback hell - usar async/await', severity: 'CRITICAL' });
748
+ patterns.push({ platform: 'backend', rule: '❌ NUNCA poner lógica en controllers - mover a servicios/use cases', severity: 'CRITICAL' });
749
+
750
+ // OBLIGATORIOS Backend
751
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar Repository Pattern (interfaces en domain, impl en infrastructure)', severity: 'MANDATORY' });
752
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar DTOs + class-validator para validación de entrada/salida', severity: 'MANDATORY' });
753
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar Guards para autenticación/autorización (@UseGuards)', severity: 'MANDATORY' });
754
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO paginación en TODOS los endpoints de listado', severity: 'MANDATORY' });
755
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO seguir Clean Architecture (Domain → Application → Infrastructure → Presentation)', severity: 'MANDATORY' });
756
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO aplicar CQRS (Command Query Responsibility Segregation): Commands (write) separados de Queries (read)', severity: 'MANDATORY' });
757
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO verificar SOLID', severity: 'MANDATORY' });
758
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO TypeScript strict mode', severity: 'MANDATORY' });
759
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar Use Cases explícitos (CreateOrderUseCase, etc.)', severity: 'MANDATORY' });
760
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar Exception Filters para manejo global de errores', severity: 'MANDATORY' });
761
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar Swagger/OpenAPI para documentación (@nestjs/swagger)', severity: 'MANDATORY' });
762
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar queries parametrizadas (prevenir SQL injection)', severity: 'MANDATORY' });
763
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar índices en columnas frecuentes en WHERE/JOIN', severity: 'MANDATORY' });
764
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar transacciones para operaciones críticas', severity: 'MANDATORY' });
765
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar soft deletes (deleted_at) por defecto', severity: 'MANDATORY' });
766
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar JWT + refresh tokens para autenticación', severity: 'MANDATORY' });
767
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar rate limiting (@nestjs/throttler)', severity: 'MANDATORY' });
768
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar Helmet para security headers', severity: 'MANDATORY' });
769
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar health checks (/health endpoint)', severity: 'MANDATORY' });
770
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO usar correlation IDs para tracing distribuido', severity: 'MANDATORY' });
771
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO nombres autodescriptivos en inglés', severity: 'MANDATORY' });
772
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO comprobar que compila (npm run build) ANTES de sugerir', severity: 'MANDATORY' });
773
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO seguir flujo BDD → TDD', severity: 'MANDATORY' });
774
+ patterns.push({ platform: 'backend', rule: '✅ OBLIGATORIO coverage >95% en lógica crítica', severity: 'MANDATORY' });
775
+ }
776
+
777
+ if (platform === 'frontend') {
778
+ // PROHIBICIONES CRÍTICAS Frontend
779
+ patterns.push({ platform: 'frontend', rule: '❌ NUNCA usar any/unknown en TypeScript - usar tipos explícitos o generics', severity: 'CRITICAL' });
780
+ patterns.push({ platform: 'frontend', rule: '❌ NUNCA usar class components - usar functional components + hooks', severity: 'CRITICAL' });
781
+ patterns.push({ platform: 'frontend', rule: '❌ NUNCA hardcodear strings - usar i18n (useTranslation)', severity: 'CRITICAL' });
782
+ patterns.push({ platform: 'frontend', rule: '❌ NUNCA dejar catch vacíos - siempre gestionar errores (common.error.empty_catch)', severity: 'CRITICAL' });
783
+ patterns.push({ platform: 'frontend', rule: '❌ NUNCA mezclar operaciones de escritura (commands/mutations) con lecturas (queries) en el mismo hook/service - aplicar CQRS', severity: 'CRITICAL' });
784
+ patterns.push({ platform: 'frontend', rule: '❌ NUNCA añadir comentarios en el código', severity: 'CRITICAL' });
785
+ patterns.push({ platform: 'frontend', rule: '❌ NUNCA usar console.log en producción', severity: 'CRITICAL' });
786
+ patterns.push({ platform: 'frontend', rule: '❌ NUNCA usar Singleton - usar providers/context para DI', severity: 'CRITICAL' });
787
+ patterns.push({ platform: 'frontend', rule: '❌ NUNCA usar índice como key en listas si el orden puede cambiar', severity: 'CRITICAL' });
788
+ patterns.push({ platform: 'frontend', rule: '❌ NUNCA usar prop drilling excesivo - usar Context/Zustand', severity: 'CRITICAL' });
789
+ patterns.push({ platform: 'frontend', rule: '❌ NUNCA renderizar HTML de usuario sin sanitizar (DOMPurify)', severity: 'CRITICAL' });
790
+ patterns.push({ platform: 'frontend', rule: '❌ NUNCA poner tokens en URLs - usar Authorization headers', severity: 'CRITICAL' });
791
+
792
+ // OBLIGATORIOS Frontend
793
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar TypeScript strict mode', severity: 'MANDATORY' });
794
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar React functional components + hooks', severity: 'MANDATORY' });
795
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar TailwindCSS para estilos', severity: 'MANDATORY' });
796
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar Next.js 15 App Router (app/ directory)', severity: 'MANDATORY' });
797
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar Server Components por defecto ("use client" solo cuando necesario)', severity: 'MANDATORY' });
798
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar React Query para server state', severity: 'MANDATORY' });
799
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar Zustand para estado global', severity: 'MANDATORY' });
800
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar React Hook Form + Zod para forms', severity: 'MANDATORY' });
801
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO API client en capa infrastructure (abstraer axios/fetch)', severity: 'MANDATORY' });
802
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO tests con React Testing Library + Playwright', severity: 'MANDATORY' });
803
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO seguir Clean Architecture', severity: 'MANDATORY' });
804
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO aplicar CQRS (Command Query Responsibility Segregation): separar writes (mutations) de reads (queries)', severity: 'MANDATORY' });
805
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO verificar SOLID', severity: 'MANDATORY' });
806
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar Next/Image para imágenes', severity: 'MANDATORY' });
807
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar loading.tsx/error.tsx en cada ruta', severity: 'MANDATORY' });
808
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar semantic HTML + ARIA labels', severity: 'MANDATORY' });
809
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO usar keyboard navigation en todos los interactivos', severity: 'MANDATORY' });
810
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO WCAG AA mínimo (contraste 4.5:1)', severity: 'MANDATORY' });
811
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO nombres autodescriptivos en inglés', severity: 'MANDATORY' });
812
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO comprobar que compila y pasa tests ANTES de sugerir', severity: 'MANDATORY' });
813
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO seguir flujo BDD → TDD', severity: 'MANDATORY' });
814
+ patterns.push({ platform: 'frontend', rule: '✅ OBLIGATORIO en producción todo real contra APIs', severity: 'MANDATORY' });
815
+ }
816
+
817
+ if (platform === 'gold') {
818
+ // PROHIBICIONES CRÍTICAS Gold (comunes a todas las plataformas)
819
+ patterns.push({ platform: 'gold', rule: '❌ NUNCA usar Singleton - usar Inyección de Dependencias', severity: 'CRITICAL' });
820
+ patterns.push({ platform: 'gold', rule: '❌ NUNCA dejar catch vacíos - siempre loggear o propagar (AST: common.error.empty_catch)', severity: 'CRITICAL' });
821
+ patterns.push({ platform: 'gold', rule: '❌ NUNCA añadir comentarios en el código - nombres autodescriptivos', severity: 'CRITICAL' });
822
+ patterns.push({ platform: 'gold', rule: '❌ NUNCA usar mocks/spies en producción - todo real contra BBDD/servicios', severity: 'CRITICAL' });
823
+ patterns.push({ platform: 'gold', rule: '❌ NUNCA usar --no-verify o GIT_BYPASS_HOOK sin autorización explícita verbal', severity: 'CRITICAL' });
824
+ patterns.push({ platform: 'gold', rule: '❌ NUNCA hardcodear secretos en código - usar variables de entorno', severity: 'CRITICAL' });
825
+ patterns.push({ platform: 'gold', rule: '❌ NUNCA silenciar errores - siempre loggear o propagar', severity: 'CRITICAL' });
826
+ patterns.push({ platform: 'gold', rule: '❌ NUNCA modificar librerías locales (node_modules/@pumuki/...) - reportar bugs', severity: 'CRITICAL' });
827
+
828
+ // OBLIGATORIOS Gold (comunes a todas las plataformas)
829
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO responder siempre en español', severity: 'MANDATORY' });
830
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO actuar como Arquitecto de Soluciones y Software Designer', severity: 'MANDATORY' });
831
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO seguir flujo BDD → TDD (Feature files → Specs → Implementación)', severity: 'MANDATORY' });
832
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO seguir Clean Architecture y Clean Code (capas, dependencias hacia adentro)', severity: 'MANDATORY' });
833
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO verificar SOLID (SRP, OCP, LSP, ISP, DIP) en cada cambio', severity: 'MANDATORY' });
834
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO preferir guard/early returns - evitar pyramid of doom', severity: 'MANDATORY' });
835
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO nombres autodescriptivos en inglés (todo el código)', severity: 'MANDATORY' });
836
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO comprobar que compila ANTES de sugerir cualquier cambio', severity: 'MANDATORY' });
837
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO analizar estructura existente antes de implementar', severity: 'MANDATORY' });
838
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO usar makeSUT pattern en tests', severity: 'MANDATORY' });
839
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO usar trackForMemoryLeaks en tests', severity: 'MANDATORY' });
840
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO preferir spies frente a stubs/mocks en tests', severity: 'MANDATORY' });
841
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO validar SIEMPRE entradas de usuario', severity: 'MANDATORY' });
842
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO sanitizar datos (prevenir XSS, SQL Injection)', severity: 'MANDATORY' });
843
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO principio de menor privilegio (permisos mínimos)', severity: 'MANDATORY' });
844
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO paginación en todas las listas', severity: 'MANDATORY' });
845
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO tests como documentación viva (AAA/Given-When-Then)', severity: 'MANDATORY' });
846
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO cobertura mínima 80%, objetivo 95%+ en lógica crítica', severity: 'MANDATORY' });
847
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO commits atómicos con Conventional Commits (feat:, fix:, etc.)', severity: 'MANDATORY' });
848
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO branch naming (feature/, bugfix/, hotfix/, refactor/)', severity: 'MANDATORY' });
849
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO i18n desde día 1 - no hardcodear strings', severity: 'MANDATORY' });
850
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO WCAG 2.1 AA mínimo para accesibilidad', severity: 'MANDATORY' });
851
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO "Measure twice, cut once" - planificar bien antes de implementar', severity: 'MANDATORY' });
852
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO YAGNI - You Ain\'t Gonna Need It (no sobre-ingeniería)', severity: 'MANDATORY' });
853
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO KISS - Keep It Simple, Stupid (solución más simple que funcione)', severity: 'MANDATORY' });
854
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO Fail Fast - validar precondiciones al inicio', severity: 'MANDATORY' });
855
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO inmutabilidad por defecto (const, readonly, let > var)', severity: 'MANDATORY' });
856
+ patterns.push({ platform: 'gold', rule: '✅ OBLIGATORIO composición > herencia', severity: 'MANDATORY' });
857
+ }
858
+
859
+ return patterns;
860
+ }
861
+
579
862
  /**
580
863
  * AI Gate Check - MANDATORY at start of every AI response
581
864
  * Returns BLOCKED or ALLOWED status with auto-fixes applied
865
+ * NOW INCLUDES: mandatory_rules that AI MUST read and follow
582
866
  */
583
867
  async function aiGateCheck() {
584
868
  const startedAt = Date.now();
@@ -683,19 +967,71 @@ async function aiGateCheck() {
683
967
  }
684
968
  }
685
969
 
970
+ let mandatoryRules = null;
971
+ let detectedPlatforms = [];
972
+ try {
973
+ const orchestrator = getCompositionRoot().getOrchestrator();
974
+ const contextDecision = await orchestrator.analyzeContext();
975
+ if (contextDecision && contextDecision.platforms) {
976
+ detectedPlatforms = contextDecision.platforms.map(p => p.platform || p);
977
+ }
978
+ const fallbackPlatforms = ['backend', 'frontend', 'ios', 'android'];
979
+ const platformsForRules = (detectedPlatforms.length > 0 ? detectedPlatforms : fallbackPlatforms)
980
+ .filter(Boolean);
981
+ const normalizedPlatforms = Array.from(new Set(platformsForRules));
982
+ const rulesData = await loadPlatformRules(normalizedPlatforms);
983
+ const rulesSample = rulesData.criticalRules.slice(0, 5).map(r => r.rule || r);
984
+ const rulesCount = rulesData.criticalRules.length;
985
+ mandatoryRules = {
986
+ platforms: normalizedPlatforms,
987
+ criticalRules: rulesData.criticalRules,
988
+ rulesLoaded: Object.keys(rulesData.rules),
989
+ totalRulesCount: rulesCount,
990
+ rulesSample: rulesSample,
991
+ proofOfRead: `✅ VERIFIED: ${rulesCount} critical rules loaded from ${Object.keys(rulesData.rules).join(', ')}`
992
+ };
993
+ } catch (error) {
994
+ if (process.env.DEBUG) {
995
+ process.stderr.write(`[MCP] Failed to load mandatory rules: ${error.message}\n`);
996
+ }
997
+
998
+ const fallbackPlatforms = ['backend', 'frontend', 'ios', 'android'];
999
+ const normalizedPlatforms = Array.from(new Set((detectedPlatforms.length > 0 ? detectedPlatforms : fallbackPlatforms).filter(Boolean)));
1000
+ mandatoryRules = {
1001
+ platforms: normalizedPlatforms,
1002
+ criticalRules: [],
1003
+ rulesLoaded: [],
1004
+ status: 'FAILED_TO_LOAD',
1005
+ error: `Failed to load rules content: ${error && error.message ? error.message : String(error)}`
1006
+ };
1007
+ }
1008
+
1009
+ const rulesLoadedSuccessfully = mandatoryRules &&
1010
+ mandatoryRules.criticalRules &&
1011
+ mandatoryRules.criticalRules.length > 0;
1012
+
1013
+ if (!rulesLoadedSuccessfully) {
1014
+ violations.push('❌ RULES_NOT_LOADED: Critical platform rules could not be loaded. AI cannot proceed without reading mandatory rules.');
1015
+ }
1016
+
1017
+ const finalBlocked = isBlocked || !rulesLoadedSuccessfully;
1018
+
686
1019
  return {
687
- status: isBlocked ? 'BLOCKED' : 'ALLOWED',
1020
+ status: finalBlocked ? 'BLOCKED' : 'ALLOWED',
688
1021
  timestamp: new Date().toISOString(),
689
1022
  branch: currentBranch,
690
1023
  violations,
691
1024
  warnings,
692
1025
  autoFixes,
693
- summary: isBlocked
1026
+ mandatory_rules: rulesLoadedSuccessfully
1027
+ ? { ...mandatoryRules, status: 'LOADED_OK' }
1028
+ : mandatoryRules,
1029
+ summary: finalBlocked
694
1030
  ? `🚫 BLOCKED: ${violations.length} violation(s). Fix before proceeding.`
695
- : `🚦 ALLOWED: Gate passed.${warnings.length > 0 ? ` ${warnings.length} warning(s).` : ''}`,
696
- instructions: isBlocked
1031
+ : `🚦 ALLOWED: Gate passed. ${mandatoryRules.totalRulesCount} critical rules loaded and verified.`,
1032
+ instructions: finalBlocked
697
1033
  ? 'DO NOT proceed with user task. Announce violations and fix them first.'
698
- : 'You may proceed with user task.'
1034
+ : `✅ ${mandatoryRules.totalRulesCount} RULES LOADED. Sample: ${mandatoryRules.rulesSample.slice(0, 2).join(' | ')}... Review ALL rules in mandatory_rules.criticalRules before ANY code generation.`
699
1035
  };
700
1036
  };
701
1037
 
@@ -712,11 +1048,59 @@ async function aiGateCheck() {
712
1048
  violations: ['❌ GATE_TIMEOUT: AI gate check timed out. Retry or run ai-start manually.'],
713
1049
  warnings: [],
714
1050
  autoFixes: [],
1051
+ mandatory_rules: {
1052
+ platforms: ['backend', 'frontend', 'ios', 'android'],
1053
+ criticalRules: [],
1054
+ rulesLoaded: [],
1055
+ warning: '⚠️ AI MUST read and follow these rules before ANY code generation or modification',
1056
+ error: 'Rules could not be loaded due to timeout'
1057
+ },
715
1058
  summary: '🚫 BLOCKED: Gate check timed out.',
716
1059
  instructions: 'DO NOT proceed with user task. Retry the gate check.'
717
1060
  };
718
1061
  }
719
1062
 
1063
+ /**
1064
+ * Read platform rules handler - returns critical rules for a specific platform
1065
+ */
1066
+ async function readPlatformRulesHandler(params) {
1067
+ const platform = params.platform;
1068
+ if (!platform) {
1069
+ return {
1070
+ success: false,
1071
+ error: 'Platform is required. Use: ios, android, backend, or frontend'
1072
+ };
1073
+ }
1074
+
1075
+ try {
1076
+ const rulesData = await loadPlatformRules([platform]);
1077
+ const DynamicRulesLoader = require('../../application/services/DynamicRulesLoader');
1078
+ const loader = new DynamicRulesLoader();
1079
+ const fullContent = await loader.loadRule(`rules${platform}.mdc`);
1080
+
1081
+ return {
1082
+ success: true,
1083
+ platform,
1084
+ rulesLoaded: true,
1085
+ criticalRules: rulesData.criticalRules,
1086
+ fullRulesContent: fullContent,
1087
+ warning: `⚠️ YOU MUST FOLLOW ALL THESE RULES. Violations will block commits.`,
1088
+ instructions: [
1089
+ `❌ NEVER violate any rule marked with ❌ or NUNCA/PROHIBIDO`,
1090
+ `✅ ALWAYS follow rules marked with ✅ or OBLIGATORIO/SIEMPRE`,
1091
+ `🚨 If you violate these rules, the commit will be BLOCKED`,
1092
+ `📝 Read the fullRulesContent carefully before generating ANY code`
1093
+ ]
1094
+ };
1095
+ } catch (error) {
1096
+ return {
1097
+ success: false,
1098
+ platform,
1099
+ error: `Failed to load rules: ${error.message}`
1100
+ };
1101
+ }
1102
+ }
1103
+
720
1104
  /**
721
1105
  * Validate and fix common issues
722
1106
  */
@@ -1010,6 +1394,21 @@ async function handleMcpMessage(message) {
1010
1394
  name: 'ai_gate_check',
1011
1395
  description: '🚦 MANDATORY gate check',
1012
1396
  inputSchema: { type: 'object', properties: {} }
1397
+ },
1398
+ {
1399
+ name: 'read_platform_rules',
1400
+ description: '📚 MANDATORY: Read platform-specific rules BEFORE any code generation. Returns critical rules that AI MUST follow.',
1401
+ inputSchema: {
1402
+ type: 'object',
1403
+ properties: {
1404
+ platform: {
1405
+ type: 'string',
1406
+ enum: ['ios', 'android', 'backend', 'frontend'],
1407
+ description: 'Platform to load rules for'
1408
+ }
1409
+ },
1410
+ required: ['platform']
1411
+ }
1013
1412
  }
1014
1413
  ]
1015
1414
  }
@@ -1043,6 +1442,9 @@ async function handleMcpMessage(message) {
1043
1442
  case 'ai_gate_check':
1044
1443
  result = await aiGateCheck();
1045
1444
  break;
1445
+ case 'read_platform_rules':
1446
+ result = await readPlatformRulesHandler(toolParams);
1447
+ break;
1046
1448
  default:
1047
1449
  return {
1048
1450
  jsonrpc: '2.0',
@@ -25,7 +25,6 @@ AUTO_MERGE_PR=${GITFLOW_AUTO_MERGE:-false}
25
25
  PR_BASE_BRANCH=${GITFLOW_PR_BASE:-develop}
26
26
  STRICT_ATOMIC=${GITFLOW_STRICT_ATOMIC:-true}
27
27
  REQUIRE_TEST_RELATIONS=${GITFLOW_REQUIRE_TESTS:-true}
28
- STRICT_CHECK=${GITFLOW_STRICT_CHECK:-false}
29
28
 
30
29
  print_section() {
31
30
  printf "${BLUE}═══════════════════════════════════════════════════════════════${NC}\n"
@@ -168,17 +167,16 @@ verify_atomic_commit() {
168
167
  return 0
169
168
  fi
170
169
 
171
- local files=()
170
+ local -a files=()
172
171
  while IFS= read -r file; do
173
- [[ -z "$file" ]] && continue
174
- files+=("$file")
172
+ [[ -n "$file" ]] && files+=("$file")
175
173
  done < <($GIT_BIN diff --name-only "${commit}^..${commit}")
176
174
  if [[ "${#files[@]}" -eq 0 ]]; then
177
175
  return 0
178
176
  fi
179
177
 
180
- local roots_list
181
- roots_list=()
178
+ local roots_list=""
179
+ local root_count=0
182
180
  for file in "${files[@]}"; do
183
181
  local root="${file%%/*}"
184
182
  if [[ "$root" == "$file" ]]; then
@@ -193,36 +191,24 @@ verify_atomic_commit() {
193
191
  ;;
194
192
  esac
195
193
 
196
- local seen=0
197
- local existing
198
- if (( ${#roots_list[@]:-0} > 0 )); then
199
- for existing in "${roots_list[@]}"; do
200
- if [[ "$existing" == "$root" ]]; then
201
- seen=1
202
- break
203
- fi
204
- done
205
- fi
206
- if [[ "$seen" -eq 0 ]]; then
207
- roots_list+=("$root")
194
+ if [[ " $roots_list " != *" $root "* ]]; then
195
+ roots_list="${roots_list}${root} "
196
+ root_count=$((root_count + 1))
208
197
  fi
209
198
  done
210
199
 
211
- local root_count=${#roots_list[@]}
212
200
  if (( root_count > 1 )); then
213
201
  local has_scripts=0
214
202
  local has_tests=0
215
- for root in "${roots_list[@]}"; do
203
+ for root in $roots_list; do
216
204
  [[ "$root" == "scripts" ]] && has_scripts=1
217
205
  [[ "$root" == "tests" ]] && has_tests=1
218
206
  done
219
-
220
207
  if [[ $has_scripts -eq 1 && $has_tests -eq 1 && $root_count -eq 2 ]]; then
221
208
  printf "${GREEN}✅ Commit %s toca scripts + tests (permitido para bugfixes/features con tests).${NC}\n" "$commit"
222
209
  return 0
223
210
  fi
224
-
225
- printf "${RED}❌ Commit %s toca múltiples raíces (%s). Divide los cambios en commits atómicos.${NC}\n" "$commit" "$(printf "%s " "${roots_list[@]}")"
211
+ printf "${RED}❌ Commit %s toca múltiples raíces (%s). Divide los cambios en commits atómicos.${NC}\n" "$commit" "$roots_list"
226
212
  return 1
227
213
  fi
228
214
  if (( root_count == 0 )); then
@@ -230,7 +216,7 @@ verify_atomic_commit() {
230
216
  return 0
231
217
  fi
232
218
  local root_name
233
- for root_name in "${roots_list[@]}"; do
219
+ for root_name in $roots_list; do
234
220
  printf "${GREEN}✅ Commit %s cumple atomicidad (raíz %s).${NC}\n" "$commit" "$root_name"
235
221
  done
236
222
  return 0
@@ -250,10 +236,9 @@ verify_pending_commits_atomic() {
250
236
  return $?
251
237
  fi
252
238
 
253
- local commits=()
239
+ local -a commits=()
254
240
  while IFS= read -r commit; do
255
- [[ -z "$commit" ]] && continue
256
- commits+=("$commit")
241
+ [[ -n "$commit" ]] && commits+=("$commit")
257
242
  done < <($GIT_BIN rev-list "${base_ref}..${branch}")
258
243
  local failed=0
259
244
  for commit in "${commits[@]}"; do
@@ -384,50 +369,22 @@ cmd_check() {
384
369
  local branch
385
370
  branch=$(current_branch)
386
371
  printf "${CYAN}📍 Rama actual: %s${NC}\n" "$branch"
387
- local failed=0
388
-
389
- if [[ "${STRICT_CHECK}" == "true" ]]; then
390
- ensure_evidence_fresh || failed=1
391
- lint_hooks_system || failed=1
392
- run_mobile_checks || failed=1
393
- else
394
- ensure_evidence_fresh || true
395
- lint_hooks_system || true
396
- run_mobile_checks || true
397
- fi
372
+ ensure_evidence_fresh || true
373
+ lint_hooks_system || true
374
+ run_mobile_checks || true
398
375
  print_sync_table
399
376
  print_cleanup_candidates
400
- if [[ "${STRICT_CHECK}" == "true" ]]; then
401
- verify_atomic_commit "HEAD" || failed=1
402
- if [[ "$REQUIRE_TEST_RELATIONS" == "true" ]]; then
403
- verify_related_files_commit "HEAD" || failed=1
404
- fi
405
- if ! verify_pending_commits_atomic "$branch"; then
406
- failed=1
407
- fi
408
- if [[ "$REQUIRE_TEST_RELATIONS" == "true" ]]; then
409
- if ! verify_pending_commits_related "$branch"; then
410
- failed=1
411
- fi
412
- fi
413
- else
414
- verify_atomic_commit "HEAD" || true
415
- if [[ "$REQUIRE_TEST_RELATIONS" == "true" ]]; then
416
- verify_related_files_commit "HEAD" || true
417
- fi
377
+ verify_atomic_commit "HEAD" || true
378
+ if [[ "$REQUIRE_TEST_RELATIONS" == "true" ]]; then
379
+ verify_related_files_commit "HEAD" || true
418
380
  fi
419
381
  local pending
420
382
  pending=$(unpushed_commits "$branch")
421
383
  if [[ "$pending" != "0" ]]; then
422
384
  printf "${YELLOW}⚠️ Commits sin subir (${pending}). Ejecuta git push.${NC}\n"
423
- if [[ "${STRICT_CHECK}" == "true" ]]; then
424
- failed=1
425
- fi
426
385
  else
427
386
  printf "${GREEN}✅ No hay commits pendientes de push.${NC}\n"
428
387
  fi
429
-
430
- return $failed
431
388
  }
432
389
 
433
390
  cmd_cycle() {
@@ -580,7 +537,6 @@ main() {
580
537
  ;;
581
538
  esac
582
539
  }
583
-
584
540
  is_test_file() {
585
541
  local file="$1"
586
542
  case "$file" in
@@ -681,14 +637,12 @@ verify_pending_commits_related() {
681
637
  local base_ref="origin/${branch}"
682
638
 
683
639
  if ! $GIT_BIN show-ref --verify --quiet "refs/remotes/origin/${branch}"; then
684
- verify_related_files_commit "HEAD"
685
- return $?
640
+ return verify_related_files_commit "HEAD"
686
641
  fi
687
642
 
688
- local commits=()
643
+ local -a commits=()
689
644
  while IFS= read -r commit; do
690
- [[ -z "$commit" ]] && continue
691
- commits+=("$commit")
645
+ [[ -n "$commit" ]] && commits+=("$commit")
692
646
  done < <($GIT_BIN rev-list "${base_ref}..${branch}")
693
647
  local failed=0
694
648
  local commit