crackerjack 0.37.9__py3-none-any.whl → 0.45.2__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.
- crackerjack/README.md +19 -0
- crackerjack/__init__.py +30 -1
- crackerjack/__main__.py +342 -1263
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +27 -5
- crackerjack/adapters/_output_paths.py +167 -0
- crackerjack/adapters/_qa_adapter_base.py +309 -0
- crackerjack/adapters/_tool_adapter_base.py +706 -0
- crackerjack/adapters/ai/README.md +65 -0
- crackerjack/adapters/ai/__init__.py +5 -0
- crackerjack/adapters/ai/claude.py +853 -0
- crackerjack/adapters/complexity/README.md +53 -0
- crackerjack/adapters/complexity/__init__.py +10 -0
- crackerjack/adapters/complexity/complexipy.py +641 -0
- crackerjack/adapters/dependency/__init__.py +22 -0
- crackerjack/adapters/dependency/pip_audit.py +418 -0
- crackerjack/adapters/format/README.md +72 -0
- crackerjack/adapters/format/__init__.py +11 -0
- crackerjack/adapters/format/mdformat.py +313 -0
- crackerjack/adapters/format/ruff.py +516 -0
- crackerjack/adapters/lint/README.md +47 -0
- crackerjack/adapters/lint/__init__.py +11 -0
- crackerjack/adapters/lint/codespell.py +273 -0
- crackerjack/adapters/lsp/README.md +49 -0
- crackerjack/adapters/lsp/__init__.py +27 -0
- crackerjack/adapters/{rust_tool_manager.py → lsp/_manager.py} +3 -3
- crackerjack/adapters/{skylos_adapter.py → lsp/skylos.py} +59 -7
- crackerjack/adapters/{zuban_adapter.py → lsp/zuban.py} +3 -6
- crackerjack/adapters/refactor/README.md +59 -0
- crackerjack/adapters/refactor/__init__.py +12 -0
- crackerjack/adapters/refactor/creosote.py +318 -0
- crackerjack/adapters/refactor/refurb.py +406 -0
- crackerjack/adapters/refactor/skylos.py +494 -0
- crackerjack/adapters/sast/README.md +132 -0
- crackerjack/adapters/sast/__init__.py +32 -0
- crackerjack/adapters/sast/_base.py +201 -0
- crackerjack/adapters/sast/bandit.py +423 -0
- crackerjack/adapters/sast/pyscn.py +405 -0
- crackerjack/adapters/sast/semgrep.py +241 -0
- crackerjack/adapters/security/README.md +111 -0
- crackerjack/adapters/security/__init__.py +17 -0
- crackerjack/adapters/security/gitleaks.py +339 -0
- crackerjack/adapters/type/README.md +52 -0
- crackerjack/adapters/type/__init__.py +12 -0
- crackerjack/adapters/type/pyrefly.py +402 -0
- crackerjack/adapters/type/ty.py +402 -0
- crackerjack/adapters/type/zuban.py +522 -0
- crackerjack/adapters/utility/README.md +51 -0
- crackerjack/adapters/utility/__init__.py +10 -0
- crackerjack/adapters/utility/checks.py +884 -0
- crackerjack/agents/README.md +264 -0
- crackerjack/agents/__init__.py +40 -12
- crackerjack/agents/base.py +1 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +49 -53
- crackerjack/agents/dry_agent.py +187 -3
- crackerjack/agents/enhanced_coordinator.py +279 -0
- crackerjack/agents/enhanced_proactive_agent.py +185 -0
- crackerjack/agents/error_middleware.py +53 -0
- crackerjack/agents/formatting_agent.py +6 -8
- crackerjack/agents/helpers/__init__.py +9 -0
- crackerjack/agents/helpers/performance/__init__.py +22 -0
- crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
- crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
- crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
- crackerjack/agents/helpers/refactoring/__init__.py +22 -0
- crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
- crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
- crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
- crackerjack/agents/helpers/test_creation/__init__.py +19 -0
- crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
- crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
- crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
- crackerjack/agents/performance_agent.py +121 -1152
- crackerjack/agents/refactoring_agent.py +156 -655
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +19 -1605
- crackerjack/api.py +5 -7
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +1 -1
- crackerjack/cli/cache_handlers.py +23 -18
- crackerjack/cli/cache_handlers_enhanced.py +1 -4
- crackerjack/cli/facade.py +70 -8
- crackerjack/cli/formatting.py +13 -0
- crackerjack/cli/handlers/__init__.py +85 -0
- crackerjack/cli/handlers/advanced.py +103 -0
- crackerjack/cli/handlers/ai_features.py +62 -0
- crackerjack/cli/handlers/analytics.py +479 -0
- crackerjack/cli/handlers/changelog.py +271 -0
- crackerjack/cli/handlers/config_handlers.py +16 -0
- crackerjack/cli/handlers/coverage.py +84 -0
- crackerjack/cli/handlers/documentation.py +280 -0
- crackerjack/cli/handlers/main_handlers.py +497 -0
- crackerjack/cli/handlers/monitoring.py +371 -0
- crackerjack/cli/handlers.py +249 -49
- crackerjack/cli/interactive.py +8 -5
- crackerjack/cli/options.py +203 -110
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +60 -24
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +256 -0
- crackerjack/config/global_lock_config.py +191 -54
- crackerjack/config/hooks.py +188 -16
- crackerjack/config/loader.py +239 -0
- crackerjack/config/settings.py +141 -0
- crackerjack/config/tool_commands.py +331 -0
- crackerjack/core/README.md +393 -0
- crackerjack/core/async_workflow_orchestrator.py +79 -53
- crackerjack/core/autofix_coordinator.py +22 -9
- crackerjack/core/container.py +10 -9
- crackerjack/core/enhanced_container.py +9 -9
- crackerjack/core/performance.py +1 -1
- crackerjack/core/performance_monitor.py +5 -3
- crackerjack/core/phase_coordinator.py +1018 -634
- crackerjack/core/proactive_workflow.py +3 -3
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +167 -23
- crackerjack/core/session_coordinator.py +187 -382
- crackerjack/core/timeout_manager.py +161 -44
- crackerjack/core/workflow/__init__.py +21 -0
- crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
- crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
- crackerjack/core/workflow/workflow_issue_parser.py +714 -0
- crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
- crackerjack/core/workflow/workflow_security_gates.py +400 -0
- crackerjack/core/workflow_orchestrator.py +1247 -953
- crackerjack/data/README.md +11 -0
- crackerjack/data/__init__.py +8 -0
- crackerjack/data/models.py +79 -0
- crackerjack/data/repository.py +210 -0
- crackerjack/decorators/README.md +180 -0
- crackerjack/decorators/__init__.py +35 -0
- crackerjack/decorators/error_handling.py +649 -0
- crackerjack/decorators/error_handling_decorators.py +334 -0
- crackerjack/decorators/helpers.py +58 -0
- crackerjack/decorators/patterns.py +281 -0
- crackerjack/decorators/utils.py +58 -0
- crackerjack/docs/README.md +11 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +1 -1
- crackerjack/documentation/README.md +11 -0
- crackerjack/documentation/ai_templates.py +1 -1
- crackerjack/documentation/dual_output_generator.py +11 -9
- crackerjack/documentation/reference_generator.py +104 -59
- crackerjack/dynamic_config.py +52 -61
- crackerjack/errors.py +1 -1
- crackerjack/events/README.md +11 -0
- crackerjack/events/__init__.py +16 -0
- crackerjack/events/telemetry.py +175 -0
- crackerjack/events/workflow_bus.py +346 -0
- crackerjack/exceptions/README.md +301 -0
- crackerjack/exceptions/__init__.py +5 -0
- crackerjack/exceptions/config.py +4 -0
- crackerjack/exceptions/tool_execution_error.py +245 -0
- crackerjack/executors/README.md +591 -0
- crackerjack/executors/__init__.py +2 -0
- crackerjack/executors/async_hook_executor.py +539 -77
- crackerjack/executors/cached_hook_executor.py +3 -3
- crackerjack/executors/hook_executor.py +967 -102
- crackerjack/executors/hook_lock_manager.py +31 -22
- crackerjack/executors/individual_hook_executor.py +66 -32
- crackerjack/executors/lsp_aware_hook_executor.py +136 -57
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +23 -7
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +8 -9
- crackerjack/intelligence/README.md +557 -0
- crackerjack/interactive.py +37 -10
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/async_hook_manager.py +41 -57
- crackerjack/managers/hook_manager.py +449 -79
- crackerjack/managers/publish_manager.py +81 -36
- crackerjack/managers/test_command_builder.py +290 -12
- crackerjack/managers/test_executor.py +93 -8
- crackerjack/managers/test_manager.py +1082 -75
- crackerjack/managers/test_progress.py +118 -26
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/cache.py +25 -2
- crackerjack/mcp/client_runner.py +35 -18
- crackerjack/mcp/context.py +9 -9
- crackerjack/mcp/dashboard.py +24 -8
- crackerjack/mcp/enhanced_progress_monitor.py +34 -23
- crackerjack/mcp/file_monitor.py +27 -6
- crackerjack/mcp/progress_components.py +45 -34
- crackerjack/mcp/progress_monitor.py +6 -9
- crackerjack/mcp/rate_limiter.py +11 -7
- crackerjack/mcp/server.py +2 -0
- crackerjack/mcp/server_core.py +187 -55
- crackerjack/mcp/service_watchdog.py +12 -9
- crackerjack/mcp/task_manager.py +2 -2
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +2 -0
- crackerjack/mcp/tools/core_tools.py +75 -52
- crackerjack/mcp/tools/execution_tools.py +87 -31
- crackerjack/mcp/tools/intelligence_tools.py +2 -2
- crackerjack/mcp/tools/proactive_tools.py +1 -1
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +180 -132
- crackerjack/mcp/tools/workflow_executor.py +87 -46
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/app.py +11 -1
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +27 -4
- crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
- crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
- crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
- crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
- crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
- crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
- crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
- crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
- crackerjack/mcp/websocket/monitoring/factory.py +109 -0
- crackerjack/mcp/websocket/monitoring/filters.py +10 -0
- crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
- crackerjack/mcp/websocket/monitoring/models.py +90 -0
- crackerjack/mcp/websocket/monitoring/utils.py +171 -0
- crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
- crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
- crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
- crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
- crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
- crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
- crackerjack/mcp/websocket/monitoring_endpoints.py +16 -2930
- crackerjack/mcp/websocket/server.py +1 -3
- crackerjack/mcp/websocket/websocket_handler.py +107 -6
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +10 -1
- crackerjack/models/config.py +639 -22
- crackerjack/models/config_adapter.py +6 -6
- crackerjack/models/protocols.py +1167 -23
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +19 -3
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/ai_agent_watchdog.py +5 -4
- crackerjack/monitoring/metrics_collector.py +4 -3
- crackerjack/monitoring/regression_prevention.py +4 -3
- crackerjack/monitoring/websocket_server.py +4 -241
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +20 -67
- crackerjack/orchestration/cache/README.md +312 -0
- crackerjack/orchestration/cache/__init__.py +37 -0
- crackerjack/orchestration/cache/memory_cache.py +338 -0
- crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
- crackerjack/orchestration/config.py +297 -0
- crackerjack/orchestration/coverage_improvement.py +13 -6
- crackerjack/orchestration/execution_strategies.py +6 -6
- crackerjack/orchestration/hook_orchestrator.py +1398 -0
- crackerjack/orchestration/strategies/README.md +401 -0
- crackerjack/orchestration/strategies/__init__.py +39 -0
- crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
- crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
- crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
- crackerjack/orchestration/test_progress_streamer.py +1 -1
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/hooks.py +3 -2
- crackerjack/plugins/loader.py +3 -3
- crackerjack/plugins/managers.py +1 -1
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +8 -21
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/{contextual_ai_assistant.py → ai/contextual_ai_assistant.py} +5 -3
- crackerjack/services/ai/embeddings.py +444 -0
- crackerjack/services/ai/intelligent_commit.py +328 -0
- crackerjack/services/ai/predictive_analytics.py +510 -0
- crackerjack/services/api_extractor.py +5 -3
- crackerjack/services/bounded_status_operations.py +45 -5
- crackerjack/services/cache.py +249 -318
- crackerjack/services/changelog_automation.py +7 -3
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +83 -39
- crackerjack/services/config_merge.py +9 -6
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +13 -26
- crackerjack/services/coverage_badge_service.py +6 -4
- crackerjack/services/coverage_ratchet.py +53 -27
- crackerjack/services/debug.py +18 -7
- crackerjack/services/dependency_analyzer.py +4 -4
- crackerjack/services/dependency_monitor.py +13 -13
- crackerjack/services/documentation_generator.py +4 -2
- crackerjack/services/documentation_service.py +62 -33
- crackerjack/services/enhanced_filesystem.py +81 -27
- crackerjack/services/enterprise_optimizer.py +1 -1
- crackerjack/services/error_pattern_analyzer.py +10 -10
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +5 -7
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +80 -109
- crackerjack/services/git.py +99 -5
- crackerjack/services/health_metrics.py +4 -6
- crackerjack/services/heatmap_generator.py +12 -3
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +101 -49
- crackerjack/services/log_manager.py +2 -2
- crackerjack/services/logging.py +120 -68
- crackerjack/services/lsp_client.py +12 -12
- crackerjack/services/memory_optimizer.py +27 -22
- crackerjack/services/monitoring/README.md +30 -0
- crackerjack/services/monitoring/__init__.py +9 -0
- crackerjack/services/monitoring/dependency_monitor.py +678 -0
- crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
- crackerjack/services/monitoring/health_metrics.py +716 -0
- crackerjack/services/monitoring/metrics.py +587 -0
- crackerjack/services/{performance_benchmarks.py → monitoring/performance_benchmarks.py} +100 -14
- crackerjack/services/{performance_cache.py → monitoring/performance_cache.py} +21 -15
- crackerjack/services/{performance_monitor.py → monitoring/performance_monitor.py} +10 -6
- crackerjack/services/parallel_executor.py +166 -55
- crackerjack/services/patterns/__init__.py +142 -0
- crackerjack/services/patterns/agents.py +107 -0
- crackerjack/services/patterns/code/__init__.py +15 -0
- crackerjack/services/patterns/code/detection.py +118 -0
- crackerjack/services/patterns/code/imports.py +107 -0
- crackerjack/services/patterns/code/paths.py +159 -0
- crackerjack/services/patterns/code/performance.py +119 -0
- crackerjack/services/patterns/code/replacement.py +36 -0
- crackerjack/services/patterns/core.py +212 -0
- crackerjack/services/patterns/documentation/__init__.py +14 -0
- crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
- crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
- crackerjack/services/patterns/documentation/docstrings.py +89 -0
- crackerjack/services/patterns/formatting.py +226 -0
- crackerjack/services/patterns/operations.py +339 -0
- crackerjack/services/patterns/security/__init__.py +23 -0
- crackerjack/services/patterns/security/code_injection.py +122 -0
- crackerjack/services/patterns/security/credentials.py +190 -0
- crackerjack/services/patterns/security/path_traversal.py +221 -0
- crackerjack/services/patterns/security/unsafe_operations.py +216 -0
- crackerjack/services/patterns/templates.py +62 -0
- crackerjack/services/patterns/testing/__init__.py +18 -0
- crackerjack/services/patterns/testing/error_patterns.py +107 -0
- crackerjack/services/patterns/testing/pytest_output.py +126 -0
- crackerjack/services/patterns/tool_output/__init__.py +16 -0
- crackerjack/services/patterns/tool_output/bandit.py +72 -0
- crackerjack/services/patterns/tool_output/other.py +97 -0
- crackerjack/services/patterns/tool_output/pyright.py +67 -0
- crackerjack/services/patterns/tool_output/ruff.py +44 -0
- crackerjack/services/patterns/url_sanitization.py +114 -0
- crackerjack/services/patterns/utilities.py +42 -0
- crackerjack/services/patterns/utils.py +339 -0
- crackerjack/services/patterns/validation.py +46 -0
- crackerjack/services/patterns/versioning.py +62 -0
- crackerjack/services/predictive_analytics.py +21 -8
- crackerjack/services/profiler.py +280 -0
- crackerjack/services/quality/README.md +415 -0
- crackerjack/services/quality/__init__.py +11 -0
- crackerjack/services/quality/anomaly_detector.py +392 -0
- crackerjack/services/quality/pattern_cache.py +333 -0
- crackerjack/services/quality/pattern_detector.py +479 -0
- crackerjack/services/quality/qa_orchestrator.py +491 -0
- crackerjack/services/{quality_baseline.py → quality/quality_baseline.py} +163 -2
- crackerjack/services/{quality_baseline_enhanced.py → quality/quality_baseline_enhanced.py} +4 -1
- crackerjack/services/{quality_intelligence.py → quality/quality_intelligence.py} +180 -16
- crackerjack/services/regex_patterns.py +58 -2987
- crackerjack/services/regex_utils.py +55 -29
- crackerjack/services/secure_status_formatter.py +42 -15
- crackerjack/services/secure_subprocess.py +35 -2
- crackerjack/services/security.py +16 -8
- crackerjack/services/server_manager.py +40 -51
- crackerjack/services/smart_scheduling.py +46 -6
- crackerjack/services/status_authentication.py +3 -3
- crackerjack/services/thread_safe_status_collector.py +1 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +9 -5
- crackerjack/services/unified_config.py +43 -351
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +6 -4
- crackerjack/services/version_checker.py +14 -8
- crackerjack/services/zuban_lsp_service.py +5 -4
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/init.md +2 -12
- crackerjack/slash_commands/run.md +84 -50
- crackerjack/tools/README.md +11 -0
- crackerjack/tools/__init__.py +30 -0
- crackerjack/tools/_git_utils.py +105 -0
- crackerjack/tools/check_added_large_files.py +139 -0
- crackerjack/tools/check_ast.py +105 -0
- crackerjack/tools/check_json.py +103 -0
- crackerjack/tools/check_jsonschema.py +297 -0
- crackerjack/tools/check_toml.py +103 -0
- crackerjack/tools/check_yaml.py +110 -0
- crackerjack/tools/codespell_wrapper.py +72 -0
- crackerjack/tools/end_of_file_fixer.py +202 -0
- crackerjack/tools/format_json.py +128 -0
- crackerjack/tools/mdformat_wrapper.py +114 -0
- crackerjack/tools/trailing_whitespace.py +198 -0
- crackerjack/tools/validate_regex_patterns.py +7 -3
- crackerjack/ui/README.md +11 -0
- crackerjack/ui/dashboard_renderer.py +28 -0
- crackerjack/ui/templates/README.md +11 -0
- crackerjack/utils/console_utils.py +13 -0
- crackerjack/utils/dependency_guard.py +230 -0
- crackerjack/utils/retry_utils.py +275 -0
- crackerjack/workflows/README.md +590 -0
- crackerjack/workflows/__init__.py +46 -0
- crackerjack/workflows/actions.py +811 -0
- crackerjack/workflows/auto_fix.py +444 -0
- crackerjack/workflows/container_builder.py +499 -0
- crackerjack/workflows/definitions.py +443 -0
- crackerjack/workflows/engine.py +177 -0
- crackerjack/workflows/event_bridge.py +242 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/METADATA +678 -98
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack/managers/test_manager_backup.py +0 -1075
- crackerjack/mcp/tools/execution_tools_backup.py +0 -1011
- crackerjack/mixins/__init__.py +0 -3
- crackerjack/mixins/error_handling.py +0 -145
- crackerjack/services/config.py +0 -358
- crackerjack/ui/server_panels.py +0 -125
- crackerjack-0.37.9.dist-info/RECORD +0 -231
- /crackerjack/adapters/{rust_tool_adapter.py → lsp/_base.py} +0 -0
- /crackerjack/adapters/{lsp_client.py → lsp/_client.py} +0 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/entry_points.txt +0 -0
- {crackerjack-0.37.9.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
> Crackerjack Docs: [Main](<../../README.md>) | [CLAUDE.md](../../docs/guides/CLAUDE.md) | [Managers](<./README.md>)
|
|
2
|
+
|
|
3
|
+
# Managers
|
|
4
|
+
|
|
5
|
+
Manager classes that coordinate components, resources, and state for quality enforcement, testing, and publishing workflows.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
The managers package provides high-level coordination classes that sit between the orchestration layer and services/adapters. Managers handle domain-specific coordination (hooks, tests, publishing) with protocol-based dependency injection and standardized lifecycle management.
|
|
10
|
+
|
|
11
|
+
## Core Components
|
|
12
|
+
|
|
13
|
+
### Hook Management
|
|
14
|
+
|
|
15
|
+
- **hook_manager.py**: Synchronous hook execution manager
|
|
16
|
+
|
|
17
|
+
- Direct adapter invocation (no pre-commit wrapper)
|
|
18
|
+
- Hook lifecycle management (init, execute, cleanup)
|
|
19
|
+
- Result aggregation and reporting
|
|
20
|
+
- Retry logic for formatting hooks
|
|
21
|
+
- Fast (~5s) and comprehensive (~30s) hook stages
|
|
22
|
+
- Protocol-based dependency injection
|
|
23
|
+
|
|
24
|
+
- **async_hook_manager.py**: Asynchronous hook execution manager
|
|
25
|
+
|
|
26
|
+
- Async-first architecture for parallel execution
|
|
27
|
+
- Up to 11 concurrent adapters
|
|
28
|
+
- Non-blocking I/O operations
|
|
29
|
+
- Real-time progress streaming
|
|
30
|
+
- WebSocket integration for monitoring
|
|
31
|
+
- Event-driven coordination
|
|
32
|
+
|
|
33
|
+
### Publishing
|
|
34
|
+
|
|
35
|
+
- **publish_manager.py**: PyPI publishing and version management
|
|
36
|
+
- Semantic versioning (patch, minor, major, auto)
|
|
37
|
+
- UV-based publishing workflow
|
|
38
|
+
- Secure authentication (keyring, environment variables)
|
|
39
|
+
- Git tagging and release management
|
|
40
|
+
- Changelog integration
|
|
41
|
+
- Pre-publish validation
|
|
42
|
+
|
|
43
|
+
## Architecture
|
|
44
|
+
|
|
45
|
+
### Manager Layer Position
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
CLI Handlers (90% compliant)
|
|
49
|
+
↓
|
|
50
|
+
Managers (80% compliant) ← You are here
|
|
51
|
+
↓
|
|
52
|
+
Services (95% compliant) + Adapters (varies)
|
|
53
|
+
↓
|
|
54
|
+
External Tools (ruff, zuban, pytest, etc.)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### ACB Compliance Status
|
|
58
|
+
|
|
59
|
+
Based on Phase 2-4 refactoring audit:
|
|
60
|
+
|
|
61
|
+
| Manager | Compliance | Status | Notes |
|
|
62
|
+
|---------|-----------|--------|-------|
|
|
63
|
+
| HookManager | 85% | ✅ Good | Protocol-based injection, some manual instantiation |
|
|
64
|
+
| AsyncHookManager | 80% | ✅ Good | Async patterns, needs protocol standardization |
|
|
65
|
+
| PublishManager | 75% | ⚠️ Mixed | Some legacy patterns, mostly good |
|
|
66
|
+
|
|
67
|
+
**Common Improvements Needed:**
|
|
68
|
+
|
|
69
|
+
- Remove manual service instantiation
|
|
70
|
+
- Standardize protocol imports from `models/protocols.py`
|
|
71
|
+
- Eliminate factory functions in favor of DI
|
|
72
|
+
|
|
73
|
+
### Dependency Injection Pattern
|
|
74
|
+
|
|
75
|
+
Managers use ACB dependency injection:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from acb.depends import depends, Inject
|
|
79
|
+
from crackerjack.models.protocols import Console, TestManagerProtocol
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@depends.inject
|
|
83
|
+
class HookManager:
|
|
84
|
+
def __init__(
|
|
85
|
+
self,
|
|
86
|
+
console: Inject[Console] = None,
|
|
87
|
+
test_manager: Inject[TestManagerProtocol] = None,
|
|
88
|
+
) -> None:
|
|
89
|
+
self.console = console
|
|
90
|
+
self.test_manager = test_manager
|
|
91
|
+
|
|
92
|
+
async def init(self) -> None:
|
|
93
|
+
# Async initialization
|
|
94
|
+
pass
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Usage Examples
|
|
98
|
+
|
|
99
|
+
### Hook Management
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from crackerjack.managers.hook_manager import HookManager
|
|
103
|
+
from acb.depends import depends
|
|
104
|
+
|
|
105
|
+
# Create manager with DI
|
|
106
|
+
manager = depends.get(HookManager)
|
|
107
|
+
await manager.init()
|
|
108
|
+
|
|
109
|
+
# Run fast hooks (~5s)
|
|
110
|
+
fast_results = await manager.run_fast_hooks()
|
|
111
|
+
|
|
112
|
+
# Run comprehensive hooks (~30s)
|
|
113
|
+
comp_results = await manager.run_comprehensive_hooks()
|
|
114
|
+
|
|
115
|
+
# Check results
|
|
116
|
+
if all(r.passed for r in fast_results):
|
|
117
|
+
print("All fast hooks passed!")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Async Hook Management
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from crackerjack.managers.async_hook_manager import AsyncHookManager
|
|
124
|
+
|
|
125
|
+
manager = AsyncHookManager(max_concurrent=11)
|
|
126
|
+
await manager.init()
|
|
127
|
+
|
|
128
|
+
# Parallel execution with progress tracking
|
|
129
|
+
results = await manager.run_hooks_parallel(
|
|
130
|
+
hooks=["ruff", "zuban", "bandit"],
|
|
131
|
+
progress_callback=lambda p: print(f"Progress: {p}%"),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Real-time results as they complete
|
|
135
|
+
async for result in manager.stream_hook_results():
|
|
136
|
+
print(f"Hook {result.hook_name}: {'✅' if result.passed else '❌'}")
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Publishing
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from crackerjack.managers.publish_manager import PublishManager
|
|
143
|
+
|
|
144
|
+
manager = PublishManager()
|
|
145
|
+
await manager.init()
|
|
146
|
+
|
|
147
|
+
# Bump version and publish
|
|
148
|
+
await manager.publish(
|
|
149
|
+
bump_type="patch", # 1.0.0 → 1.0.1
|
|
150
|
+
create_tag=True,
|
|
151
|
+
push_tag=True,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Check if authentication is configured
|
|
155
|
+
if manager.has_pypi_token():
|
|
156
|
+
print("Ready to publish!")
|
|
157
|
+
else:
|
|
158
|
+
print("Configure PyPI token first")
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Hook Execution Workflow
|
|
162
|
+
|
|
163
|
+
### Fast Hooks Stage (~5s)
|
|
164
|
+
|
|
165
|
+
**Purpose:** Rapid feedback for essential checks
|
|
166
|
+
|
|
167
|
+
**Hooks:**
|
|
168
|
+
|
|
169
|
+
- Ruff formatting and linting
|
|
170
|
+
- Trailing whitespace cleanup
|
|
171
|
+
- UV lock file updates
|
|
172
|
+
- Security credential detection (gitleaks)
|
|
173
|
+
- Spell checking (codespell)
|
|
174
|
+
|
|
175
|
+
**Retry Logic:**
|
|
176
|
+
|
|
177
|
+
- Format hooks retry once if they fail (formatting often fixes cascade issues)
|
|
178
|
+
- Other hooks don't retry
|
|
179
|
+
|
|
180
|
+
### Comprehensive Hooks Stage (~30s)
|
|
181
|
+
|
|
182
|
+
**Purpose:** Thorough static analysis
|
|
183
|
+
|
|
184
|
+
**Hooks:**
|
|
185
|
+
|
|
186
|
+
- Zuban type checking (20-200x faster than pyright)
|
|
187
|
+
- Bandit security analysis
|
|
188
|
+
- Dead code detection (skylos, 20x faster than vulture)
|
|
189
|
+
- Dependency analysis (creosote)
|
|
190
|
+
- Complexity limits (complexipy ≤15 per function)
|
|
191
|
+
- Modern Python patterns (refurb)
|
|
192
|
+
|
|
193
|
+
**Execution:**
|
|
194
|
+
|
|
195
|
+
- All hooks run, collecting ALL failures (don't stop on first)
|
|
196
|
+
- Results aggregated for batch AI fixing
|
|
197
|
+
- Real-time console output
|
|
198
|
+
|
|
199
|
+
## Publishing Workflow
|
|
200
|
+
|
|
201
|
+
### Version Bumping
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
# Semantic versioning
|
|
205
|
+
"patch": 1.0.0 → 1.0.1 # Bug fixes
|
|
206
|
+
"minor": 1.0.0 → 1.1.0 # New features
|
|
207
|
+
"major": 1.0.0 → 2.0.0 # Breaking changes
|
|
208
|
+
"auto": AI recommends based on changelog
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Publishing Steps
|
|
212
|
+
|
|
213
|
+
1. **Pre-validation**
|
|
214
|
+
|
|
215
|
+
- All quality hooks must pass
|
|
216
|
+
- Tests must pass
|
|
217
|
+
- No uncommitted changes
|
|
218
|
+
|
|
219
|
+
1. **Version Bump**
|
|
220
|
+
|
|
221
|
+
- Update `pyproject.toml`
|
|
222
|
+
- Update `__version__.py`
|
|
223
|
+
- Generate changelog entry
|
|
224
|
+
|
|
225
|
+
1. **Git Operations**
|
|
226
|
+
|
|
227
|
+
- Commit version bump
|
|
228
|
+
- Create git tag (v1.0.1)
|
|
229
|
+
- Push to remote
|
|
230
|
+
|
|
231
|
+
1. **Build & Publish**
|
|
232
|
+
|
|
233
|
+
- Build package with UV
|
|
234
|
+
- Publish to PyPI
|
|
235
|
+
- Verify upload
|
|
236
|
+
|
|
237
|
+
### Authentication
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Method 1: Keyring (most secure)
|
|
241
|
+
keyring set https://upload.pypi.org/legacy/ __token__
|
|
242
|
+
# Enter: pypi-your-token-here
|
|
243
|
+
|
|
244
|
+
# Method 2: Environment variable
|
|
245
|
+
export UV_PUBLISH_TOKEN=pypi-your-token-here
|
|
246
|
+
|
|
247
|
+
# Method 3: .env file (local development)
|
|
248
|
+
echo "UV_PUBLISH_TOKEN=pypi-your-token-here" > .env
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Configuration
|
|
252
|
+
|
|
253
|
+
Managers are configured via `settings/crackerjack.yaml`:
|
|
254
|
+
|
|
255
|
+
```yaml
|
|
256
|
+
# Hook Management
|
|
257
|
+
max_parallel_hooks: 11
|
|
258
|
+
hook_timeout: 60
|
|
259
|
+
fast_hook_retry: true
|
|
260
|
+
|
|
261
|
+
# Publishing
|
|
262
|
+
auto_git_tag: true
|
|
263
|
+
require_clean_working_tree: true
|
|
264
|
+
publish_on_tag: false
|
|
265
|
+
|
|
266
|
+
# Testing
|
|
267
|
+
test_workers: 0 # Auto-detect
|
|
268
|
+
test_timeout: 300
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Best Practices
|
|
272
|
+
|
|
273
|
+
1. **Use DI**: Always get managers via `depends.get(ManagerClass)`
|
|
274
|
+
1. **Initialize Async**: Call `await manager.init()` before use
|
|
275
|
+
1. **Handle Results**: Check all hook results, don't assume success
|
|
276
|
+
1. **Retry Formatting**: Let format hooks retry once (they fix cascade issues)
|
|
277
|
+
1. **Validate Before Publish**: Always run quality + tests before publishing
|
|
278
|
+
1. **Secure Tokens**: Use keyring, never commit tokens
|
|
279
|
+
1. **Tag Releases**: Always create git tags for versions
|
|
280
|
+
1. **Monitor Progress**: Use progress callbacks for long operations
|
|
281
|
+
1. **Batch Failures**: Collect all failures before fixing (don't stop on first)
|
|
282
|
+
1. **Use Real-time Output**: Async managers provide better UX
|
|
283
|
+
|
|
284
|
+
## Anti-Patterns to Avoid
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
# ❌ Manual service instantiation
|
|
288
|
+
self.git_service = GitService()
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
# ✅ Use dependency injection
|
|
292
|
+
@depends.inject
|
|
293
|
+
def __init__(self, git: Inject[GitServiceProtocol] = None):
|
|
294
|
+
self.git = git
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
# ❌ Factory functions
|
|
298
|
+
self.logger = get_logger()
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
# ✅ Inject dependencies
|
|
302
|
+
@depends.inject
|
|
303
|
+
def __init__(self, logger: Inject[Logger] = None):
|
|
304
|
+
self.logger = logger
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# ❌ Stopping on first failure
|
|
308
|
+
for hook in hooks:
|
|
309
|
+
result = hook.run()
|
|
310
|
+
if not result.passed:
|
|
311
|
+
break # Don't do this!
|
|
312
|
+
|
|
313
|
+
# ✅ Collect all failures
|
|
314
|
+
results = [hook.run() for hook in hooks]
|
|
315
|
+
failures = [r for r in results if not r.passed]
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Troubleshooting
|
|
319
|
+
|
|
320
|
+
### Hooks Timing Out
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
# Increase timeout for specific hooks
|
|
324
|
+
manager.set_hook_timeout("zuban", 120)
|
|
325
|
+
|
|
326
|
+
# Or globally
|
|
327
|
+
manager.hook_timeout = 120
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Publishing Failures
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
# Check authentication
|
|
334
|
+
keyring get https://upload.pypi.org/legacy/ __token__
|
|
335
|
+
|
|
336
|
+
# Verify package builds
|
|
337
|
+
uv build
|
|
338
|
+
|
|
339
|
+
# Check PyPI connectivity
|
|
340
|
+
curl -I https://upload.pypi.org/legacy/
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Parallel Execution Issues
|
|
344
|
+
|
|
345
|
+
```python
|
|
346
|
+
# Reduce concurrency
|
|
347
|
+
manager.max_concurrent = 5
|
|
348
|
+
|
|
349
|
+
# Or run sequentially for debugging
|
|
350
|
+
manager.max_concurrent = 1
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Related
|
|
354
|
+
|
|
355
|
+
- [Orchestration](<../orchestration/README.md>) — Lower-level orchestration used by managers
|
|
356
|
+
- [Services](<../services/README.md>) — Services used by managers
|
|
357
|
+
- [Adapters](<../adapters/README.md>) — Adapters managed by hook managers
|
|
358
|
+
- [CLI](<../cli/README.md>) — CLI handlers that use managers
|
|
359
|
+
- [Main README](<../../README.md>) — Overall workflow overview
|
|
360
|
+
|
|
361
|
+
## Future Enhancements
|
|
362
|
+
|
|
363
|
+
- [ ] Complete migration to ACB dependency injection (remove manual instantiation)
|
|
364
|
+
- [ ] Standardize async/sync patterns
|
|
365
|
+
- [ ] Add manager health checks and metrics
|
|
366
|
+
- [ ] Implement circuit breaker for external services
|
|
367
|
+
- [ ] Add manager benchmarking
|
|
368
|
+
- [ ] Create manager plugin system
|
|
369
|
+
- [ ] Improve error recovery and rollback
|
|
@@ -2,7 +2,7 @@ import asyncio
|
|
|
2
2
|
import typing as t
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from acb.console import Console
|
|
6
6
|
|
|
7
7
|
from crackerjack.config.hooks import HookConfigLoader
|
|
8
8
|
from crackerjack.executors.async_hook_executor import AsyncHookExecutor
|
|
@@ -62,70 +62,48 @@ class AsyncHookManager:
|
|
|
62
62
|
return asyncio.run(self.run_comprehensive_hooks_async())
|
|
63
63
|
|
|
64
64
|
async def install_hooks_async(self) -> bool:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
_, stderr = await asyncio.wait_for(process.communicate(), timeout=30)
|
|
77
|
-
|
|
78
|
-
if process.returncode == 0:
|
|
79
|
-
self.console.print("[green]✅[/ green] Pre-commit hooks installed")
|
|
80
|
-
return True
|
|
81
|
-
error_msg = stderr.decode() if stderr else "Unknown error"
|
|
82
|
-
self.console.print(
|
|
83
|
-
f"[red]❌[/ red] Failed to install hooks: {error_msg}",
|
|
84
|
-
)
|
|
85
|
-
return False
|
|
86
|
-
|
|
87
|
-
except TimeoutError:
|
|
88
|
-
self.console.print("[red]❌[/ red] Hook installation timed out")
|
|
89
|
-
return False
|
|
90
|
-
except Exception as e:
|
|
91
|
-
self.console.print(f"[red]❌[/ red] Error installing hooks: {e}")
|
|
92
|
-
return False
|
|
65
|
+
"""Install git hooks (async version).
|
|
66
|
+
|
|
67
|
+
Phase 8.5: This method is deprecated. Direct tool invocation doesn't require
|
|
68
|
+
pre-commit hook installation. Returns True with informational message.
|
|
69
|
+
"""
|
|
70
|
+
self.console.print(
|
|
71
|
+
"[yellow]ℹ️[/yellow] Hook installation not required with direct invocation"
|
|
72
|
+
)
|
|
73
|
+
return True
|
|
93
74
|
|
|
94
75
|
def install_hooks(self) -> bool:
|
|
95
76
|
return asyncio.run(self.install_hooks_async())
|
|
96
77
|
|
|
97
78
|
async def update_hooks_async(self) -> bool:
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
_, stderr = await asyncio.wait_for(process.communicate(), timeout=60)
|
|
110
|
-
|
|
111
|
-
if process.returncode == 0:
|
|
112
|
-
self.console.print("[green]✅[/ green] Pre-commit hooks updated")
|
|
113
|
-
return True
|
|
114
|
-
error_msg = stderr.decode() if stderr else "Unknown error"
|
|
115
|
-
self.console.print(f"[red]❌[/ red] Failed to update hooks: {error_msg}")
|
|
116
|
-
return False
|
|
117
|
-
|
|
118
|
-
except TimeoutError:
|
|
119
|
-
self.console.print("[red]❌[/ red] Hook update timed out")
|
|
120
|
-
return False
|
|
121
|
-
except Exception as e:
|
|
122
|
-
self.console.print(f"[red]❌[/ red] Error updating hooks: {e}")
|
|
123
|
-
return False
|
|
79
|
+
"""Update hooks to latest versions (async version).
|
|
80
|
+
|
|
81
|
+
Phase 8.5: This method is deprecated. Direct tool invocation uses UV for
|
|
82
|
+
dependency management. Returns True with informational message.
|
|
83
|
+
"""
|
|
84
|
+
self.console.print(
|
|
85
|
+
"[yellow]ℹ️[/yellow] Hook updates managed via UV dependency resolution"
|
|
86
|
+
)
|
|
87
|
+
return True
|
|
124
88
|
|
|
125
89
|
def update_hooks(self) -> bool:
|
|
126
90
|
return asyncio.run(self.update_hooks_async())
|
|
127
91
|
|
|
128
|
-
|
|
92
|
+
@staticmethod
|
|
93
|
+
def get_hook_summary(
|
|
94
|
+
results: list[HookResult], elapsed_time: float | None = None
|
|
95
|
+
) -> dict[str, t.Any]:
|
|
96
|
+
"""Calculate summary statistics for hook execution results.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
results: List of hook execution results
|
|
100
|
+
elapsed_time: Optional wall-clock elapsed time in seconds.
|
|
101
|
+
If provided, used as total_duration (critical for parallel execution).
|
|
102
|
+
If None, falls back to sum of individual durations (sequential mode).
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Dictionary with execution statistics
|
|
106
|
+
"""
|
|
129
107
|
if not results:
|
|
130
108
|
return {
|
|
131
109
|
"total": 0,
|
|
@@ -139,7 +117,13 @@ class AsyncHookManager:
|
|
|
139
117
|
passed = sum(1 for r in results if r.status == "passed")
|
|
140
118
|
failed = sum(1 for r in results if r.status == "failed")
|
|
141
119
|
errors = sum(1 for r in results if r.status in ("timeout", "error"))
|
|
142
|
-
|
|
120
|
+
|
|
121
|
+
# Use wall-clock time if provided (parallel execution), else sum durations (sequential)
|
|
122
|
+
total_duration = (
|
|
123
|
+
elapsed_time
|
|
124
|
+
if elapsed_time is not None
|
|
125
|
+
else sum(r.duration for r in results)
|
|
126
|
+
)
|
|
143
127
|
|
|
144
128
|
return {
|
|
145
129
|
"total": len(results),
|