crackerjack 0.32.0__py3-none-any.whl → 0.33.0__py3-none-any.whl

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.

Potentially problematic release.


This version of crackerjack might be problematic. Click here for more details.

Files changed (34) hide show
  1. crackerjack/core/enhanced_container.py +67 -0
  2. crackerjack/core/phase_coordinator.py +183 -44
  3. crackerjack/core/workflow_orchestrator.py +459 -138
  4. crackerjack/managers/publish_manager.py +22 -5
  5. crackerjack/managers/test_command_builder.py +4 -2
  6. crackerjack/managers/test_manager.py +15 -4
  7. crackerjack/mcp/server_core.py +162 -34
  8. crackerjack/mcp/tools/core_tools.py +1 -1
  9. crackerjack/mcp/tools/execution_tools.py +8 -3
  10. crackerjack/mixins/__init__.py +5 -0
  11. crackerjack/mixins/error_handling.py +214 -0
  12. crackerjack/models/config.py +9 -0
  13. crackerjack/models/protocols.py +69 -0
  14. crackerjack/models/task.py +3 -0
  15. crackerjack/security/__init__.py +1 -1
  16. crackerjack/security/audit.py +92 -78
  17. crackerjack/services/config.py +3 -2
  18. crackerjack/services/config_merge.py +11 -5
  19. crackerjack/services/coverage_ratchet.py +22 -0
  20. crackerjack/services/git.py +37 -24
  21. crackerjack/services/initialization.py +25 -9
  22. crackerjack/services/memory_optimizer.py +477 -0
  23. crackerjack/services/parallel_executor.py +474 -0
  24. crackerjack/services/performance_benchmarks.py +292 -577
  25. crackerjack/services/performance_cache.py +443 -0
  26. crackerjack/services/performance_monitor.py +633 -0
  27. crackerjack/services/security.py +63 -0
  28. crackerjack/services/security_logger.py +9 -1
  29. crackerjack/services/terminal_utils.py +0 -0
  30. {crackerjack-0.32.0.dist-info → crackerjack-0.33.0.dist-info}/METADATA +2 -2
  31. {crackerjack-0.32.0.dist-info → crackerjack-0.33.0.dist-info}/RECORD +34 -27
  32. {crackerjack-0.32.0.dist-info → crackerjack-0.33.0.dist-info}/WHEEL +0 -0
  33. {crackerjack-0.32.0.dist-info → crackerjack-0.33.0.dist-info}/entry_points.txt +0 -0
  34. {crackerjack-0.32.0.dist-info → crackerjack-0.33.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,3 +1,4 @@
1
+ import asyncio
1
2
  import time
2
3
  import typing as t
3
4
  from pathlib import Path
@@ -13,6 +14,12 @@ from crackerjack.services.logging import (
13
14
  get_logger,
14
15
  setup_structured_logging,
15
16
  )
17
+ from crackerjack.services.memory_optimizer import get_memory_optimizer, memory_optimized
18
+ from crackerjack.services.performance_cache import get_performance_cache
19
+ from crackerjack.services.performance_monitor import (
20
+ get_performance_monitor,
21
+ phase_monitor,
22
+ )
16
23
 
17
24
  from .phase_coordinator import PhaseCoordinator
18
25
  from .session_coordinator import SessionCoordinator
@@ -45,6 +52,11 @@ class WorkflowPipeline:
45
52
  self.logger = get_logger("crackerjack.pipeline")
46
53
  self._debugger = None
47
54
 
55
+ # Performance optimization services
56
+ self._performance_monitor = get_performance_monitor()
57
+ self._memory_optimizer = get_memory_optimizer()
58
+ self._cache = get_performance_cache()
59
+
48
60
  @property
49
61
  def debugger(self):
50
62
  if self._debugger is None:
@@ -56,7 +68,16 @@ class WorkflowPipeline:
56
68
 
57
69
  return os.environ.get("AI_AGENT_DEBUG", "0") == "1"
58
70
 
71
+ @memory_optimized
59
72
  async def run_complete_workflow(self, options: OptionsProtocol) -> bool:
73
+ workflow_id = f"workflow_{int(time.time())}"
74
+
75
+ # Start performance monitoring
76
+ self._performance_monitor.start_workflow(workflow_id)
77
+
78
+ # Start cache service if not already running
79
+ await self._cache.start()
80
+
60
81
  with LoggingContext(
61
82
  "workflow_execution",
62
83
  testing=getattr(options, "test", False),
@@ -66,17 +87,34 @@ class WorkflowPipeline:
66
87
  self._initialize_workflow_session(options)
67
88
 
68
89
  try:
69
- success = await self._execute_workflow_with_timing(options, start_time)
90
+ success = await self._execute_workflow_with_timing(
91
+ options, start_time, workflow_id
92
+ )
93
+
94
+ # Finalize performance monitoring
95
+ workflow_perf = self._performance_monitor.end_workflow(
96
+ workflow_id, success
97
+ )
98
+ self.logger.info(
99
+ f"Workflow performance: {workflow_perf.performance_score:.1f} score, "
100
+ f"{workflow_perf.total_duration_seconds:.2f}s duration"
101
+ )
102
+
70
103
  return success
71
104
 
72
105
  except KeyboardInterrupt:
106
+ self._performance_monitor.end_workflow(workflow_id, False)
73
107
  return self._handle_user_interruption()
74
108
 
75
109
  except Exception as e:
110
+ self._performance_monitor.end_workflow(workflow_id, False)
76
111
  return self._handle_workflow_exception(e)
77
112
 
78
113
  finally:
79
114
  self.session.cleanup_resources()
115
+ # Optimize memory after workflow completion
116
+ self._memory_optimizer.optimize_memory()
117
+ await self._cache.stop()
80
118
 
81
119
  def _initialize_workflow_session(self, options: OptionsProtocol) -> None:
82
120
  self.session.initialize_session_tracking(options)
@@ -113,9 +151,9 @@ class WorkflowPipeline:
113
151
  )
114
152
 
115
153
  async def _execute_workflow_with_timing(
116
- self, options: OptionsProtocol, start_time: float
154
+ self, options: OptionsProtocol, start_time: float, workflow_id: str
117
155
  ) -> bool:
118
- success = await self._execute_workflow_phases(options)
156
+ success = await self._execute_workflow_phases(options, workflow_id)
119
157
  self.session.finalize_session(start_time, success)
120
158
 
121
159
  duration = time.time() - start_time
@@ -161,62 +199,144 @@ class WorkflowPipeline:
161
199
  )
162
200
  return False
163
201
 
164
- async def _execute_workflow_phases(self, options: OptionsProtocol) -> bool:
202
+ async def _execute_workflow_phases(
203
+ self, options: OptionsProtocol, workflow_id: str
204
+ ) -> bool:
205
+ """Execute all workflow phases with proper security gates and performance monitoring."""
165
206
  success = True
166
- self.phases.run_configuration_phase(options)
167
207
 
168
- # Code cleaning is now integrated into the quality phase
169
- # to run after fast hooks but before comprehensive hooks
170
- quality_success = await self._execute_quality_phase(options)
208
+ # Configuration phase with monitoring
209
+ with phase_monitor(workflow_id, "configuration"):
210
+ config_success = self.phases.run_configuration_phase(options)
211
+ success = success and config_success
212
+
213
+ # Execute quality phase (includes testing and comprehensive checks)
214
+ quality_success = await self._execute_quality_phase(options, workflow_id)
171
215
  if not quality_success:
172
216
  success = False
173
- # Don't return early - continue to publishing/commit phases if requested
174
- # This allows -p and -c flags to work even when quality checks fail
175
- if not (options.publish or options.all or options.commit):
176
- # Only exit early if no publishing/commit operations are requested
177
- return False
217
+ # For publishing workflows, enforce security gates
218
+ if self._is_publishing_workflow(options):
219
+ return False # Exit early - publishing requires ALL quality checks
178
220
 
179
- if not self.phases.run_publishing_phase(options):
221
+ # Execute publishing workflow if requested
222
+ if not await self._execute_publishing_workflow(options, workflow_id):
180
223
  success = False
181
- self.session.fail_task("workflow", "Publishing failed")
182
224
  return False
183
- if not self.phases.run_commit_phase(options):
225
+
226
+ # Execute commit workflow if requested
227
+ if not await self._execute_commit_workflow(options, workflow_id):
184
228
  success = False
185
229
 
186
230
  return success
187
231
 
188
- async def _execute_quality_phase(self, options: OptionsProtocol) -> bool:
232
+ def _is_publishing_workflow(self, options: OptionsProtocol) -> bool:
233
+ """Check if this is a publishing workflow that requires strict security gates."""
234
+ return bool(options.publish or options.all or options.commit)
235
+
236
+ async def _execute_publishing_workflow(
237
+ self, options: OptionsProtocol, workflow_id: str
238
+ ) -> bool:
239
+ """Execute publishing workflow with proper error handling and monitoring."""
240
+ if not options.publish and not options.all:
241
+ return True
242
+
243
+ with phase_monitor(workflow_id, "publishing"):
244
+ if not self.phases.run_publishing_phase(options):
245
+ self.session.fail_task("workflow", "Publishing failed")
246
+ return False
247
+ return True
248
+
249
+ async def _execute_commit_workflow(
250
+ self, options: OptionsProtocol, workflow_id: str
251
+ ) -> bool:
252
+ """Execute commit workflow with proper error handling and monitoring."""
253
+ if not options.commit:
254
+ return True
255
+
256
+ with phase_monitor(workflow_id, "commit"):
257
+ if not self.phases.run_commit_phase(options):
258
+ return False
259
+ return True
260
+
261
+ async def _execute_quality_phase(
262
+ self, options: OptionsProtocol, workflow_id: str
263
+ ) -> bool:
189
264
  if hasattr(options, "fast") and options.fast:
190
- return self._run_fast_hooks_phase(options)
265
+ return await self._run_fast_hooks_phase_monitored(options, workflow_id)
191
266
  if hasattr(options, "comp") and options.comp:
192
- return self._run_comprehensive_hooks_phase(options)
267
+ return await self._run_comprehensive_hooks_phase_monitored(
268
+ options, workflow_id
269
+ )
193
270
  if getattr(options, "test", False):
194
- return await self._execute_test_workflow(options)
195
- return self._execute_standard_hooks_workflow(options)
271
+ return await self._execute_test_workflow(options, workflow_id)
272
+ return await self._execute_standard_hooks_workflow_monitored(
273
+ options, workflow_id
274
+ )
196
275
 
197
- async def _execute_test_workflow(self, options: OptionsProtocol) -> bool:
276
+ async def _execute_test_workflow(
277
+ self, options: OptionsProtocol, workflow_id: str
278
+ ) -> bool:
198
279
  iteration = self._start_iteration_tracking(options)
199
280
 
200
- if not self._run_initial_fast_hooks(options, iteration):
281
+ # Execute initial phases (fast hooks + optional cleaning)
282
+ if not await self._execute_initial_phases(options, workflow_id, iteration):
201
283
  return False
202
284
 
203
- # Run code cleaning after fast hooks but before comprehensive hooks
204
- if getattr(options, "clean", False):
205
- if not self._run_code_cleaning_phase(options):
206
- return False
207
- # Run fast hooks again after cleaning for sanity check
208
- if not self._run_post_cleaning_fast_hooks(options):
285
+ # Run main quality phases
286
+ (
287
+ testing_passed,
288
+ comprehensive_passed,
289
+ ) = await self._run_main_quality_phases_async(options, workflow_id)
290
+
291
+ # Handle workflow completion based on agent mode
292
+ return await self._handle_workflow_completion(
293
+ options, iteration, testing_passed, comprehensive_passed, workflow_id
294
+ )
295
+
296
+ async def _execute_initial_phases(
297
+ self, options: OptionsProtocol, workflow_id: str, iteration: int
298
+ ) -> bool:
299
+ """Execute fast hooks and optional code cleaning phases."""
300
+ # Fast hooks with performance monitoring
301
+ with phase_monitor(workflow_id, "fast_hooks") as monitor:
302
+ if not await self._run_initial_fast_hooks_async(
303
+ options, iteration, monitor
304
+ ):
209
305
  return False
210
- self._mark_code_cleaning_complete()
211
306
 
212
- testing_passed, comprehensive_passed = self._run_main_quality_phases(options)
307
+ # Run code cleaning if enabled
308
+ return self._execute_optional_cleaning_phase(options)
309
+
310
+ def _execute_optional_cleaning_phase(self, options: OptionsProtocol) -> bool:
311
+ """Execute code cleaning phase if enabled."""
312
+ if not getattr(options, "clean", False):
313
+ return True
314
+
315
+ if not self._run_code_cleaning_phase(options):
316
+ return False
317
+
318
+ # Run fast hooks again after cleaning for sanity check
319
+ if not self._run_post_cleaning_fast_hooks(options):
320
+ return False
321
+
322
+ self._mark_code_cleaning_complete()
323
+ return True
213
324
 
325
+ async def _handle_workflow_completion(
326
+ self,
327
+ options: OptionsProtocol,
328
+ iteration: int,
329
+ testing_passed: bool,
330
+ comprehensive_passed: bool,
331
+ workflow_id: str = "unknown",
332
+ ) -> bool:
333
+ """Handle workflow completion based on agent mode."""
214
334
  if options.ai_agent:
215
335
  return await self._handle_ai_agent_workflow(
216
- options, iteration, testing_passed, comprehensive_passed
336
+ options, iteration, testing_passed, comprehensive_passed, workflow_id
217
337
  )
218
338
 
219
- return self._handle_standard_workflow(
339
+ return await self._handle_standard_workflow(
220
340
  options, iteration, testing_passed, comprehensive_passed
221
341
  )
222
342
 
@@ -234,9 +354,38 @@ class WorkflowPipeline:
234
354
  return False
235
355
  return True
236
356
 
237
- def _run_main_quality_phases(self, options: OptionsProtocol) -> tuple[bool, bool]:
238
- testing_passed = self._run_testing_phase(options)
239
- comprehensive_passed = self._run_comprehensive_hooks_phase(options)
357
+ async def _run_main_quality_phases_async(
358
+ self, options: OptionsProtocol, workflow_id: str
359
+ ) -> tuple[bool, bool]:
360
+ # Run testing and comprehensive phases in parallel where possible
361
+ testing_task = asyncio.create_task(
362
+ self._run_testing_phase_async(options, workflow_id)
363
+ )
364
+ comprehensive_task = asyncio.create_task(
365
+ self._run_comprehensive_hooks_phase_monitored(options, workflow_id)
366
+ )
367
+
368
+ results = await asyncio.gather(
369
+ testing_task, comprehensive_task, return_exceptions=True
370
+ )
371
+
372
+ # Handle exceptions and ensure boolean types
373
+ testing_result, comprehensive_result = results
374
+
375
+ if isinstance(testing_result, Exception):
376
+ self.logger.error(f"Testing phase failed with exception: {testing_result}")
377
+ testing_passed = False
378
+ else:
379
+ testing_passed = bool(testing_result)
380
+
381
+ if isinstance(comprehensive_result, Exception):
382
+ self.logger.error(
383
+ f"Comprehensive hooks failed with exception: {comprehensive_result}"
384
+ )
385
+ comprehensive_passed = False
386
+ else:
387
+ comprehensive_passed = bool(comprehensive_result)
388
+
240
389
  return testing_passed, comprehensive_passed
241
390
 
242
391
  async def _handle_ai_agent_workflow(
@@ -245,53 +394,90 @@ class WorkflowPipeline:
245
394
  iteration: int,
246
395
  testing_passed: bool,
247
396
  comprehensive_passed: bool,
397
+ workflow_id: str = "unknown",
248
398
  ) -> bool:
249
- # Check security gates for publishing operations
399
+ # Handle security gates first
400
+ if not await self._process_security_gates(options):
401
+ return False
402
+
403
+ # Determine if AI fixing is needed
404
+ needs_ai_fixing = self._determine_ai_fixing_needed(
405
+ testing_passed, comprehensive_passed, bool(options.publish or options.all)
406
+ )
407
+
408
+ if needs_ai_fixing:
409
+ return await self._execute_ai_fixing_workflow(options, iteration)
410
+
411
+ # Handle success case without AI fixing
412
+ return self._finalize_ai_workflow_success(
413
+ options, iteration, testing_passed, comprehensive_passed
414
+ )
415
+
416
+ async def _process_security_gates(self, options: OptionsProtocol) -> bool:
417
+ """Process security gates for publishing operations."""
250
418
  publishing_requested, security_blocks = (
251
419
  self._check_security_gates_for_publishing(options)
252
420
  )
253
421
 
254
- if publishing_requested and security_blocks:
255
- # Try AI fixing for security issues, then re-check
256
- security_fix_result = await self._handle_security_gate_failure(
257
- options, allow_ai_fixing=True
258
- )
259
- if not security_fix_result:
260
- return False
261
- # If AI fixing resolved security issues, continue with normal flow
422
+ if not (publishing_requested and security_blocks):
423
+ return True
262
424
 
263
- # Determine if we need AI fixing based on publishing requirements
264
- needs_ai_fixing = self._determine_ai_fixing_needed(
265
- testing_passed, comprehensive_passed, publishing_requested
425
+ # Try AI fixing for security issues, then re-check
426
+ security_fix_result = await self._handle_security_gate_failure(
427
+ options, allow_ai_fixing=True
266
428
  )
429
+ return security_fix_result
267
430
 
268
- if needs_ai_fixing:
269
- success = await self._run_ai_agent_fixing_phase(options)
270
- if self._should_debug():
271
- self.debugger.log_iteration_end(iteration, success)
272
- return success
431
+ async def _execute_ai_fixing_workflow(
432
+ self, options: OptionsProtocol, iteration: int
433
+ ) -> bool:
434
+ """Execute AI fixing workflow and handle debugging."""
435
+ success = await self._run_ai_agent_fixing_phase(options)
436
+ if self._should_debug():
437
+ self.debugger.log_iteration_end(iteration, success)
438
+ return success
439
+
440
+ def _finalize_ai_workflow_success(
441
+ self,
442
+ options: OptionsProtocol,
443
+ iteration: int,
444
+ testing_passed: bool,
445
+ comprehensive_passed: bool,
446
+ ) -> bool:
447
+ """Finalize AI workflow when no fixing is needed."""
448
+ publishing_requested = bool(options.publish or options.all)
273
449
 
274
- # Determine final success based on publishing requirements
275
450
  final_success = self._determine_workflow_success(
276
- testing_passed,
277
- comprehensive_passed,
278
- publishing_requested,
279
- workflow_type="ai",
451
+ testing_passed, comprehensive_passed, publishing_requested
280
452
  )
281
453
 
282
- # Show security audit warning for partial success in publishing workflows
283
- if (
284
- publishing_requested
285
- and final_success
286
- and not (testing_passed and comprehensive_passed)
287
- ):
288
- self._show_security_audit_warning()
454
+ self._show_partial_success_warning_if_needed(
455
+ publishing_requested, final_success, testing_passed, comprehensive_passed
456
+ )
289
457
 
290
458
  if self._should_debug():
291
459
  self.debugger.log_iteration_end(iteration, final_success)
460
+
292
461
  return final_success
293
462
 
294
- def _handle_standard_workflow(
463
+ def _show_partial_success_warning_if_needed(
464
+ self,
465
+ publishing_requested: bool,
466
+ final_success: bool,
467
+ testing_passed: bool,
468
+ comprehensive_passed: bool,
469
+ ) -> None:
470
+ """Show security audit warning for partial success in publishing workflows."""
471
+ should_show_warning = (
472
+ publishing_requested
473
+ and final_success
474
+ and not (testing_passed and comprehensive_passed)
475
+ )
476
+
477
+ if should_show_warning:
478
+ self._show_security_audit_warning()
479
+
480
+ async def _handle_standard_workflow(
295
481
  self,
296
482
  options: OptionsProtocol,
297
483
  iteration: int,
@@ -305,14 +491,13 @@ class WorkflowPipeline:
305
491
 
306
492
  if publishing_requested and security_blocks:
307
493
  # Standard workflow cannot bypass security gates
308
- return self._handle_security_gate_failure(options, allow_ai_fixing=False)
494
+ return await self._handle_security_gate_failure(options)
309
495
 
310
496
  # Determine success based on publishing requirements
311
497
  success = self._determine_workflow_success(
312
498
  testing_passed,
313
499
  comprehensive_passed,
314
500
  publishing_requested,
315
- workflow_type="standard",
316
501
  )
317
502
 
318
503
  # Show security audit warning for partial success in publishing workflows
@@ -324,7 +509,7 @@ class WorkflowPipeline:
324
509
  self._show_security_audit_warning()
325
510
  elif publishing_requested and not success:
326
511
  self.console.print(
327
- "[red]❌ Both tests and comprehensive hooks failed - cannot proceed to publishing[/red]"
512
+ "[red]❌ Quality checks failed - cannot proceed to publishing[/red]"
328
513
  )
329
514
 
330
515
  # Show verbose failure details if requested
@@ -494,36 +679,67 @@ class WorkflowPipeline:
494
679
  self._mcp_state_manager.update_stage_status("comprehensive", "completed")
495
680
 
496
681
  async def _run_ai_agent_fixing_phase(self, options: OptionsProtocol) -> bool:
497
- self._update_mcp_status("ai_fixing", "running")
498
- self.logger.info("Starting AI agent fixing phase")
499
- self._log_debug_phase_start()
682
+ self._initialize_ai_fixing_phase(options)
500
683
 
501
684
  try:
502
- # If code cleaning is enabled and hasn't run yet, run it first
503
- # to provide cleaner, more standardized code for the AI agents
504
- if getattr(options, "clean", False) and not self._has_code_cleaning_run():
505
- self.console.print(
506
- "\n[bold yellow]🤖 AI agents recommend running code cleaning first for better results...[/bold yellow]"
507
- )
508
- if self._run_code_cleaning_phase(options):
509
- # Run fast hooks sanity check after cleaning
510
- self._run_post_cleaning_fast_hooks(options)
511
- self._mark_code_cleaning_complete()
685
+ # Prepare environment for AI agents
686
+ self._prepare_ai_fixing_environment(options)
512
687
 
513
- agent_coordinator = self._setup_agent_coordinator()
514
- issues = await self._collect_issues_from_failures()
688
+ # Setup coordinator and collect issues
689
+ agent_coordinator, issues = await self._setup_ai_fixing_workflow()
515
690
 
516
691
  if not issues:
517
692
  return self._handle_no_issues_found()
518
693
 
519
- self.logger.info(f"AI agents will attempt to fix {len(issues)} issues")
520
- fix_result = await agent_coordinator.handle_issues(issues)
521
-
522
- return await self._process_fix_results(options, fix_result)
694
+ # Execute AI fixing
695
+ return await self._execute_ai_fixes(options, agent_coordinator, issues)
523
696
 
524
697
  except Exception as e:
525
698
  return self._handle_fixing_phase_error(e)
526
699
 
700
+ def _initialize_ai_fixing_phase(self, options: OptionsProtocol) -> None:
701
+ """Initialize the AI fixing phase with status updates and logging."""
702
+ self._update_mcp_status("ai_fixing", "running")
703
+ self.logger.info("Starting AI agent fixing phase")
704
+ self._log_debug_phase_start()
705
+
706
+ def _prepare_ai_fixing_environment(self, options: OptionsProtocol) -> None:
707
+ """Prepare the environment for AI agents by running optional code cleaning."""
708
+ should_run_cleaning = (
709
+ getattr(options, "clean", False) and not self._has_code_cleaning_run()
710
+ )
711
+
712
+ if not should_run_cleaning:
713
+ return
714
+
715
+ self.console.print(
716
+ "\n[bold yellow]🤖 AI agents recommend running code cleaning first for better results...[/bold yellow]"
717
+ )
718
+
719
+ if self._run_code_cleaning_phase(options):
720
+ # Run fast hooks sanity check after cleaning
721
+ self._run_post_cleaning_fast_hooks(options)
722
+ self._mark_code_cleaning_complete()
723
+
724
+ async def _setup_ai_fixing_workflow(
725
+ self,
726
+ ) -> tuple[AgentCoordinator, list[t.Any]]:
727
+ """Setup agent coordinator and collect issues to fix."""
728
+ agent_coordinator = self._setup_agent_coordinator()
729
+ issues = await self._collect_issues_from_failures()
730
+ return agent_coordinator, issues
731
+
732
+ async def _execute_ai_fixes(
733
+ self,
734
+ options: OptionsProtocol,
735
+ agent_coordinator: AgentCoordinator,
736
+ issues: list[t.Any],
737
+ ) -> bool:
738
+ """Execute AI fixes and process results."""
739
+ self.logger.info(f"AI agents will attempt to fix {len(issues)} issues")
740
+ fix_result = await agent_coordinator.handle_issues(issues)
741
+ return await self._process_fix_results(options, fix_result)
742
+
527
743
  def _log_debug_phase_start(self) -> None:
528
744
  if self._should_debug():
529
745
  self.debugger.log_workflow_phase(
@@ -1142,26 +1358,23 @@ class WorkflowPipeline:
1142
1358
  ) -> bool:
1143
1359
  """Determine if AI fixing is needed based on test results and publishing requirements."""
1144
1360
  if publishing_requested:
1145
- # For publish/commit workflows, only trigger AI fixing if both fail
1146
- return not testing_passed and not comprehensive_passed
1147
- else:
1148
- # For regular workflows, trigger AI fixing if either fails
1361
+ # For publish/commit workflows, trigger AI fixing if either fails (since both must pass)
1149
1362
  return not testing_passed or not comprehensive_passed
1363
+ # For regular workflows, trigger AI fixing if either fails
1364
+ return not testing_passed or not comprehensive_passed
1150
1365
 
1151
1366
  def _determine_workflow_success(
1152
1367
  self,
1153
1368
  testing_passed: bool,
1154
1369
  comprehensive_passed: bool,
1155
1370
  publishing_requested: bool,
1156
- workflow_type: str,
1157
1371
  ) -> bool:
1158
1372
  """Determine workflow success based on test results and workflow type."""
1159
1373
  if publishing_requested:
1160
- # For publishing workflows, either test or comprehensive passing is sufficient
1161
- return testing_passed or comprehensive_passed
1162
- else:
1163
- # For regular workflows, both must pass
1374
+ # For publishing workflows, ALL quality checks (tests AND comprehensive hooks) must pass
1164
1375
  return testing_passed and comprehensive_passed
1376
+ # For regular workflows, both must pass as well
1377
+ return testing_passed and comprehensive_passed
1165
1378
 
1166
1379
  def _show_verbose_failure_details(
1167
1380
  self, testing_passed: bool, comprehensive_passed: bool
@@ -1212,60 +1425,68 @@ class WorkflowPipeline:
1212
1425
 
1213
1426
  def _get_recent_fast_hook_results(self) -> list[t.Any]:
1214
1427
  """Get recent fast hook results from session tracker."""
1215
- results = []
1216
-
1217
1428
  # Try to get results from session tracker
1218
- if hasattr(self.session, "session_tracker") and self.session.session_tracker:
1219
- for task_id, task_data in self.session.session_tracker.tasks.items():
1220
- if task_id == "fast_hooks" and hasattr(task_data, "hook_results"):
1221
- results.extend(task_data.hook_results)
1429
+ results = self._extract_hook_results_from_session("fast_hooks")
1222
1430
 
1223
1431
  # If no results from session, create mock failed results for critical hooks
1224
- # This ensures we fail securely when we can't determine actual status
1225
1432
  if not results:
1226
- critical_fast_hooks = ["gitleaks"]
1227
- for hook_name in critical_fast_hooks:
1228
- # Create a mock result that appears to have failed
1229
- # This will trigger security blocking if we can't determine actual status
1230
- mock_result = type(
1231
- "MockResult",
1232
- (),
1233
- {
1234
- "name": hook_name,
1235
- "status": "unknown", # Unknown status = fail securely
1236
- "output": "Unable to determine hook status",
1237
- },
1238
- )()
1239
- results.append(mock_result)
1433
+ results = self._create_mock_hook_results(["gitleaks"])
1240
1434
 
1241
1435
  return results
1242
1436
 
1243
- def _get_recent_comprehensive_hook_results(self) -> list[t.Any]:
1244
- """Get recent comprehensive hook results from session tracker."""
1437
+ def _extract_hook_results_from_session(self, hook_type: str) -> list[t.Any]:
1438
+ """Extract hook results from session tracker for given hook type."""
1245
1439
  results = []
1246
1440
 
1247
- # Try to get results from session tracker
1248
- if hasattr(self.session, "session_tracker") and self.session.session_tracker:
1249
- for task_id, task_data in self.session.session_tracker.tasks.items():
1250
- if task_id == "comprehensive_hooks" and hasattr(
1251
- task_data, "hook_results"
1252
- ):
1441
+ session_tracker = self._get_session_tracker()
1442
+ if not session_tracker:
1443
+ return results
1444
+
1445
+ for task_id, task_data in session_tracker.tasks.items():
1446
+ if task_id == hook_type and hasattr(task_data, "hook_results"):
1447
+ if task_data.hook_results:
1253
1448
  results.extend(task_data.hook_results)
1254
1449
 
1450
+ return results
1451
+
1452
+ def _get_session_tracker(self) -> t.Any | None:
1453
+ """Get session tracker if available."""
1454
+ return (
1455
+ getattr(self.session, "session_tracker", None)
1456
+ if hasattr(self.session, "session_tracker")
1457
+ else None
1458
+ )
1459
+
1460
+ def _create_mock_hook_results(self, critical_hooks: list[str]) -> list[t.Any]:
1461
+ """Create mock failed results for critical hooks to fail securely."""
1462
+ results = []
1463
+
1464
+ for hook_name in critical_hooks:
1465
+ mock_result = self._create_mock_hook_result(hook_name)
1466
+ results.append(mock_result)
1467
+
1468
+ return results
1469
+
1470
+ def _create_mock_hook_result(self, hook_name: str) -> t.Any:
1471
+ """Create a mock result that appears to have failed for security purposes."""
1472
+ return type(
1473
+ "MockResult",
1474
+ (),
1475
+ {
1476
+ "name": hook_name,
1477
+ "status": "unknown", # Unknown status = fail securely
1478
+ "output": "Unable to determine hook status",
1479
+ },
1480
+ )()
1481
+
1482
+ def _get_recent_comprehensive_hook_results(self) -> list[t.Any]:
1483
+ """Get recent comprehensive hook results from session tracker."""
1484
+ # Try to get results from session tracker
1485
+ results = self._extract_hook_results_from_session("comprehensive_hooks")
1486
+
1255
1487
  # If no results from session, create mock failed results for critical hooks
1256
1488
  if not results:
1257
- critical_comprehensive_hooks = ["bandit", "pyright"]
1258
- for hook_name in critical_comprehensive_hooks:
1259
- mock_result = type(
1260
- "MockResult",
1261
- (),
1262
- {
1263
- "name": hook_name,
1264
- "status": "unknown", # Unknown status = fail securely
1265
- "output": "Unable to determine hook status",
1266
- },
1267
- )()
1268
- results.append(mock_result)
1489
+ results = self._create_mock_hook_results(["bandit", "pyright"])
1269
1490
 
1270
1491
  return results
1271
1492
 
@@ -1325,6 +1546,106 @@ class WorkflowPipeline:
1325
1546
  "[yellow]⚠️ Some non-critical quality checks failed - consider reviewing before production deployment[/yellow]"
1326
1547
  )
1327
1548
 
1549
+ # Performance-optimized async methods
1550
+ async def _run_initial_fast_hooks_async(
1551
+ self, options: OptionsProtocol, iteration: int, monitor: t.Any
1552
+ ) -> bool:
1553
+ """Run initial fast hooks asynchronously with monitoring."""
1554
+ monitor.record_sequential_op() # Fast hooks run sequentially for safety
1555
+ fast_hooks_passed = self._run_fast_hooks_phase(options)
1556
+ if not fast_hooks_passed:
1557
+ if options.ai_agent and self._should_debug():
1558
+ self.debugger.log_iteration_end(iteration, False)
1559
+ return False
1560
+ return True
1561
+
1562
+ async def _run_fast_hooks_phase_monitored(
1563
+ self, options: OptionsProtocol, workflow_id: str
1564
+ ) -> bool:
1565
+ """Run fast hooks phase with performance monitoring."""
1566
+ with phase_monitor(workflow_id, "fast_hooks") as monitor:
1567
+ monitor.record_sequential_op()
1568
+ return self._run_fast_hooks_phase(options)
1569
+
1570
+ async def _run_comprehensive_hooks_phase_monitored(
1571
+ self, options: OptionsProtocol, workflow_id: str
1572
+ ) -> bool:
1573
+ """Run comprehensive hooks phase with performance monitoring."""
1574
+ with phase_monitor(workflow_id, "comprehensive_hooks") as monitor:
1575
+ monitor.record_sequential_op()
1576
+ return self._run_comprehensive_hooks_phase(options)
1577
+
1578
+ async def _run_testing_phase_async(
1579
+ self, options: OptionsProtocol, workflow_id: str
1580
+ ) -> bool:
1581
+ """Run testing phase asynchronously with monitoring."""
1582
+ with phase_monitor(workflow_id, "testing") as monitor:
1583
+ monitor.record_sequential_op()
1584
+ return self._run_testing_phase(options)
1585
+
1586
+ async def _execute_standard_hooks_workflow_monitored(
1587
+ self, options: OptionsProtocol, workflow_id: str
1588
+ ) -> bool:
1589
+ """Execute standard hooks workflow with performance monitoring."""
1590
+ with phase_monitor(workflow_id, "hooks") as monitor:
1591
+ self._update_hooks_status_running()
1592
+
1593
+ # Execute fast hooks phase
1594
+ fast_hooks_success = self._execute_monitored_fast_hooks_phase(
1595
+ options, monitor
1596
+ )
1597
+ if not fast_hooks_success:
1598
+ self._handle_hooks_completion(False)
1599
+ return False
1600
+
1601
+ # Execute optional cleaning phase
1602
+ if not self._execute_monitored_cleaning_phase(options):
1603
+ self._handle_hooks_completion(False)
1604
+ return False
1605
+
1606
+ # Execute comprehensive hooks phase
1607
+ comprehensive_success = self._execute_monitored_comprehensive_phase(
1608
+ options, monitor
1609
+ )
1610
+
1611
+ # Complete workflow
1612
+ hooks_success = fast_hooks_success and comprehensive_success
1613
+ self._handle_hooks_completion(hooks_success)
1614
+ return hooks_success
1615
+
1616
+ def _execute_monitored_fast_hooks_phase(
1617
+ self, options: OptionsProtocol, monitor: t.Any
1618
+ ) -> bool:
1619
+ """Execute fast hooks phase with monitoring."""
1620
+ fast_hooks_success = self._run_fast_hooks_phase(options)
1621
+ if fast_hooks_success:
1622
+ monitor.record_sequential_op()
1623
+ return fast_hooks_success
1624
+
1625
+ def _execute_monitored_cleaning_phase(self, options: OptionsProtocol) -> bool:
1626
+ """Execute optional code cleaning phase."""
1627
+ if not getattr(options, "clean", False):
1628
+ return True
1629
+
1630
+ if not self._run_code_cleaning_phase(options):
1631
+ return False
1632
+
1633
+ # Run fast hooks again after cleaning for sanity check
1634
+ if not self._run_post_cleaning_fast_hooks(options):
1635
+ return False
1636
+
1637
+ self._mark_code_cleaning_complete()
1638
+ return True
1639
+
1640
+ def _execute_monitored_comprehensive_phase(
1641
+ self, options: OptionsProtocol, monitor: t.Any
1642
+ ) -> bool:
1643
+ """Execute comprehensive hooks phase with monitoring."""
1644
+ comprehensive_success = self._run_comprehensive_hooks_phase(options)
1645
+ if comprehensive_success:
1646
+ monitor.record_sequential_op()
1647
+ return comprehensive_success
1648
+
1328
1649
 
1329
1650
  class WorkflowOrchestrator:
1330
1651
  def __init__(