tapps-agents 3.6.0__py3-none-any.whl → 3.6.1__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.
- tapps_agents/__init__.py +2 -2
- tapps_agents/agents/__init__.py +22 -22
- tapps_agents/agents/analyst/__init__.py +5 -5
- tapps_agents/agents/architect/__init__.py +5 -5
- tapps_agents/agents/architect/agent.py +1033 -1033
- tapps_agents/agents/architect/pattern_detector.py +75 -75
- tapps_agents/agents/cleanup/__init__.py +7 -7
- tapps_agents/agents/cleanup/agent.py +445 -445
- tapps_agents/agents/debugger/__init__.py +7 -7
- tapps_agents/agents/debugger/agent.py +310 -310
- tapps_agents/agents/debugger/error_analyzer.py +437 -437
- tapps_agents/agents/designer/__init__.py +5 -5
- tapps_agents/agents/designer/agent.py +786 -786
- tapps_agents/agents/designer/visual_designer.py +638 -638
- tapps_agents/agents/documenter/__init__.py +7 -7
- tapps_agents/agents/documenter/agent.py +531 -531
- tapps_agents/agents/documenter/doc_generator.py +472 -472
- tapps_agents/agents/documenter/doc_validator.py +393 -393
- tapps_agents/agents/documenter/framework_doc_updater.py +493 -493
- tapps_agents/agents/enhancer/__init__.py +7 -7
- tapps_agents/agents/evaluator/__init__.py +7 -7
- tapps_agents/agents/evaluator/agent.py +443 -443
- tapps_agents/agents/evaluator/priority_evaluator.py +641 -641
- tapps_agents/agents/evaluator/quality_analyzer.py +147 -147
- tapps_agents/agents/evaluator/report_generator.py +344 -344
- tapps_agents/agents/evaluator/usage_analyzer.py +192 -192
- tapps_agents/agents/evaluator/workflow_analyzer.py +189 -189
- tapps_agents/agents/implementer/__init__.py +7 -7
- tapps_agents/agents/implementer/agent.py +798 -798
- tapps_agents/agents/implementer/auto_fix.py +1119 -1119
- tapps_agents/agents/implementer/code_generator.py +73 -73
- tapps_agents/agents/improver/__init__.py +1 -1
- tapps_agents/agents/improver/agent.py +753 -753
- tapps_agents/agents/ops/__init__.py +1 -1
- tapps_agents/agents/ops/agent.py +619 -619
- tapps_agents/agents/ops/dependency_analyzer.py +600 -600
- tapps_agents/agents/orchestrator/__init__.py +5 -5
- tapps_agents/agents/orchestrator/agent.py +522 -522
- tapps_agents/agents/planner/__init__.py +7 -7
- tapps_agents/agents/planner/agent.py +1127 -1127
- tapps_agents/agents/reviewer/__init__.py +24 -24
- tapps_agents/agents/reviewer/agent.py +3513 -3513
- tapps_agents/agents/reviewer/aggregator.py +213 -213
- tapps_agents/agents/reviewer/batch_review.py +448 -448
- tapps_agents/agents/reviewer/cache.py +443 -443
- tapps_agents/agents/reviewer/context7_enhancer.py +630 -630
- tapps_agents/agents/reviewer/context_detector.py +203 -203
- tapps_agents/agents/reviewer/docker_compose_validator.py +158 -158
- tapps_agents/agents/reviewer/dockerfile_validator.py +176 -176
- tapps_agents/agents/reviewer/error_handling.py +126 -126
- tapps_agents/agents/reviewer/feedback_generator.py +490 -490
- tapps_agents/agents/reviewer/influxdb_validator.py +316 -316
- tapps_agents/agents/reviewer/issue_tracking.py +169 -169
- tapps_agents/agents/reviewer/library_detector.py +295 -295
- tapps_agents/agents/reviewer/library_patterns.py +268 -268
- tapps_agents/agents/reviewer/maintainability_scorer.py +593 -593
- tapps_agents/agents/reviewer/metric_strategies.py +276 -276
- tapps_agents/agents/reviewer/mqtt_validator.py +160 -160
- tapps_agents/agents/reviewer/output_enhancer.py +105 -105
- tapps_agents/agents/reviewer/pattern_detector.py +241 -241
- tapps_agents/agents/reviewer/performance_scorer.py +357 -357
- tapps_agents/agents/reviewer/phased_review.py +516 -516
- tapps_agents/agents/reviewer/progressive_review.py +435 -435
- tapps_agents/agents/reviewer/react_scorer.py +331 -331
- tapps_agents/agents/reviewer/score_constants.py +228 -228
- tapps_agents/agents/reviewer/score_validator.py +507 -507
- tapps_agents/agents/reviewer/scorer_registry.py +373 -373
- tapps_agents/agents/reviewer/service_discovery.py +534 -534
- tapps_agents/agents/reviewer/tools/parallel_executor.py +581 -581
- tapps_agents/agents/reviewer/tools/ruff_grouping.py +250 -250
- tapps_agents/agents/reviewer/tools/scoped_mypy.py +284 -284
- tapps_agents/agents/reviewer/typescript_scorer.py +1142 -1142
- tapps_agents/agents/reviewer/validation.py +208 -208
- tapps_agents/agents/reviewer/websocket_validator.py +132 -132
- tapps_agents/agents/tester/__init__.py +7 -7
- tapps_agents/agents/tester/accessibility_auditor.py +309 -309
- tapps_agents/agents/tester/agent.py +1080 -1080
- tapps_agents/agents/tester/batch_generator.py +54 -54
- tapps_agents/agents/tester/context_learner.py +51 -51
- tapps_agents/agents/tester/coverage_analyzer.py +386 -386
- tapps_agents/agents/tester/coverage_test_generator.py +290 -290
- tapps_agents/agents/tester/debug_enhancer.py +238 -238
- tapps_agents/agents/tester/device_emulator.py +241 -241
- tapps_agents/agents/tester/integration_generator.py +62 -62
- tapps_agents/agents/tester/network_recorder.py +300 -300
- tapps_agents/agents/tester/performance_monitor.py +320 -320
- tapps_agents/agents/tester/test_fixer.py +316 -316
- tapps_agents/agents/tester/test_generator.py +632 -632
- tapps_agents/agents/tester/trace_manager.py +234 -234
- tapps_agents/agents/tester/visual_regression.py +291 -291
- tapps_agents/analysis/pattern_detector.py +36 -36
- tapps_agents/beads/hydration.py +213 -213
- tapps_agents/beads/parse.py +32 -32
- tapps_agents/beads/specs.py +206 -206
- tapps_agents/cli/__init__.py +9 -9
- tapps_agents/cli/__main__.py +8 -8
- tapps_agents/cli/base.py +478 -478
- tapps_agents/cli/command_classifier.py +72 -72
- tapps_agents/cli/commands/__init__.py +2 -2
- tapps_agents/cli/commands/analyst.py +173 -173
- tapps_agents/cli/commands/architect.py +109 -109
- tapps_agents/cli/commands/cleanup_agent.py +92 -92
- tapps_agents/cli/commands/common.py +126 -126
- tapps_agents/cli/commands/debugger.py +90 -90
- tapps_agents/cli/commands/designer.py +112 -112
- tapps_agents/cli/commands/documenter.py +136 -136
- tapps_agents/cli/commands/enhancer.py +110 -110
- tapps_agents/cli/commands/evaluator.py +255 -255
- tapps_agents/cli/commands/implementer.py +301 -301
- tapps_agents/cli/commands/improver.py +91 -91
- tapps_agents/cli/commands/knowledge.py +111 -111
- tapps_agents/cli/commands/learning.py +172 -172
- tapps_agents/cli/commands/observability.py +283 -283
- tapps_agents/cli/commands/ops.py +135 -135
- tapps_agents/cli/commands/orchestrator.py +116 -116
- tapps_agents/cli/commands/planner.py +237 -237
- tapps_agents/cli/commands/reviewer.py +1872 -1872
- tapps_agents/cli/commands/status.py +285 -285
- tapps_agents/cli/commands/task.py +227 -227
- tapps_agents/cli/commands/tester.py +191 -191
- tapps_agents/cli/feedback.py +936 -936
- tapps_agents/cli/formatters.py +608 -608
- tapps_agents/cli/help/__init__.py +7 -7
- tapps_agents/cli/help/static_help.py +425 -425
- tapps_agents/cli/network_detection.py +110 -110
- tapps_agents/cli/output_compactor.py +274 -274
- tapps_agents/cli/parsers/__init__.py +2 -2
- tapps_agents/cli/parsers/analyst.py +186 -186
- tapps_agents/cli/parsers/architect.py +167 -167
- tapps_agents/cli/parsers/cleanup_agent.py +228 -228
- tapps_agents/cli/parsers/debugger.py +116 -116
- tapps_agents/cli/parsers/designer.py +182 -182
- tapps_agents/cli/parsers/documenter.py +134 -134
- tapps_agents/cli/parsers/enhancer.py +113 -113
- tapps_agents/cli/parsers/evaluator.py +213 -213
- tapps_agents/cli/parsers/implementer.py +168 -168
- tapps_agents/cli/parsers/improver.py +132 -132
- tapps_agents/cli/parsers/ops.py +159 -159
- tapps_agents/cli/parsers/orchestrator.py +98 -98
- tapps_agents/cli/parsers/planner.py +145 -145
- tapps_agents/cli/parsers/reviewer.py +462 -462
- tapps_agents/cli/parsers/tester.py +124 -124
- tapps_agents/cli/progress_heartbeat.py +254 -254
- tapps_agents/cli/streaming_progress.py +336 -336
- tapps_agents/cli/utils/__init__.py +6 -6
- tapps_agents/cli/utils/agent_lifecycle.py +48 -48
- tapps_agents/cli/utils/error_formatter.py +82 -82
- tapps_agents/cli/utils/error_recovery.py +188 -188
- tapps_agents/cli/utils/output_handler.py +59 -59
- tapps_agents/cli/utils/prompt_enhancer.py +319 -319
- tapps_agents/cli/validators/__init__.py +9 -9
- tapps_agents/cli/validators/command_validator.py +81 -81
- tapps_agents/context7/__init__.py +112 -112
- tapps_agents/context7/agent_integration.py +869 -869
- tapps_agents/context7/analytics.py +382 -382
- tapps_agents/context7/analytics_dashboard.py +299 -299
- tapps_agents/context7/async_cache.py +681 -681
- tapps_agents/context7/backup_client.py +958 -958
- tapps_agents/context7/cache_locking.py +194 -194
- tapps_agents/context7/cache_metadata.py +214 -214
- tapps_agents/context7/cache_prewarm.py +488 -488
- tapps_agents/context7/cache_structure.py +168 -168
- tapps_agents/context7/cache_warming.py +604 -604
- tapps_agents/context7/circuit_breaker.py +376 -376
- tapps_agents/context7/cleanup.py +461 -461
- tapps_agents/context7/commands.py +858 -858
- tapps_agents/context7/credential_validation.py +276 -276
- tapps_agents/context7/cross_reference_resolver.py +168 -168
- tapps_agents/context7/cross_references.py +424 -424
- tapps_agents/context7/doc_manager.py +225 -225
- tapps_agents/context7/fuzzy_matcher.py +369 -369
- tapps_agents/context7/kb_cache.py +404 -404
- tapps_agents/context7/language_detector.py +219 -219
- tapps_agents/context7/library_detector.py +725 -725
- tapps_agents/context7/lookup.py +738 -738
- tapps_agents/context7/metadata.py +258 -258
- tapps_agents/context7/refresh_queue.py +300 -300
- tapps_agents/context7/security.py +373 -373
- tapps_agents/context7/staleness_policies.py +278 -278
- tapps_agents/context7/tiles_integration.py +47 -47
- tapps_agents/continuous_bug_fix/__init__.py +20 -20
- tapps_agents/continuous_bug_fix/bug_finder.py +306 -306
- tapps_agents/continuous_bug_fix/bug_fix_coordinator.py +177 -177
- tapps_agents/continuous_bug_fix/commit_manager.py +178 -178
- tapps_agents/continuous_bug_fix/continuous_bug_fixer.py +322 -322
- tapps_agents/continuous_bug_fix/proactive_bug_finder.py +285 -285
- tapps_agents/core/__init__.py +298 -298
- tapps_agents/core/adaptive_cache_config.py +432 -432
- tapps_agents/core/agent_base.py +647 -647
- tapps_agents/core/agent_cache.py +466 -466
- tapps_agents/core/agent_learning.py +1865 -1865
- tapps_agents/core/analytics_dashboard.py +563 -563
- tapps_agents/core/analytics_enhancements.py +597 -597
- tapps_agents/core/anonymization.py +274 -274
- tapps_agents/core/ast_parser.py +228 -228
- tapps_agents/core/async_file_ops.py +402 -402
- tapps_agents/core/best_practice_consultant.py +299 -299
- tapps_agents/core/brownfield_analyzer.py +299 -299
- tapps_agents/core/brownfield_review.py +541 -541
- tapps_agents/core/browser_controller.py +513 -513
- tapps_agents/core/capability_registry.py +418 -418
- tapps_agents/core/change_impact_analyzer.py +190 -190
- tapps_agents/core/checkpoint_manager.py +377 -377
- tapps_agents/core/code_generator.py +329 -329
- tapps_agents/core/code_validator.py +276 -276
- tapps_agents/core/command_registry.py +327 -327
- tapps_agents/core/context_gathering/__init__.py +2 -2
- tapps_agents/core/context_gathering/repository_explorer.py +28 -28
- tapps_agents/core/context_intelligence/__init__.py +2 -2
- tapps_agents/core/context_intelligence/relevance_scorer.py +24 -24
- tapps_agents/core/context_intelligence/token_budget_manager.py +27 -27
- tapps_agents/core/context_manager.py +240 -240
- tapps_agents/core/cursor_feedback_monitor.py +146 -146
- tapps_agents/core/cursor_verification.py +290 -290
- tapps_agents/core/customization_loader.py +280 -280
- tapps_agents/core/customization_schema.py +260 -260
- tapps_agents/core/customization_template.py +238 -238
- tapps_agents/core/debug_logger.py +124 -124
- tapps_agents/core/design_validator.py +298 -298
- tapps_agents/core/diagram_generator.py +226 -226
- tapps_agents/core/docker_utils.py +232 -232
- tapps_agents/core/document_generator.py +617 -617
- tapps_agents/core/domain_detector.py +30 -30
- tapps_agents/core/error_envelope.py +454 -454
- tapps_agents/core/error_handler.py +270 -270
- tapps_agents/core/estimation_tracker.py +189 -189
- tapps_agents/core/eval_prompt_engine.py +116 -116
- tapps_agents/core/evaluation_base.py +119 -119
- tapps_agents/core/evaluation_models.py +320 -320
- tapps_agents/core/evaluation_orchestrator.py +225 -225
- tapps_agents/core/evaluators/__init__.py +7 -7
- tapps_agents/core/evaluators/architectural_evaluator.py +205 -205
- tapps_agents/core/evaluators/behavioral_evaluator.py +160 -160
- tapps_agents/core/evaluators/performance_profile_evaluator.py +160 -160
- tapps_agents/core/evaluators/security_posture_evaluator.py +148 -148
- tapps_agents/core/evaluators/spec_compliance_evaluator.py +181 -181
- tapps_agents/core/exceptions.py +107 -107
- tapps_agents/core/expert_config_generator.py +293 -293
- tapps_agents/core/export_schema.py +202 -202
- tapps_agents/core/external_feedback_models.py +102 -102
- tapps_agents/core/external_feedback_storage.py +213 -213
- tapps_agents/core/fallback_strategy.py +314 -314
- tapps_agents/core/feedback_analyzer.py +162 -162
- tapps_agents/core/feedback_collector.py +178 -178
- tapps_agents/core/git_operations.py +445 -445
- tapps_agents/core/hardware_profiler.py +151 -151
- tapps_agents/core/instructions.py +324 -324
- tapps_agents/core/io_guardrails.py +69 -69
- tapps_agents/core/issue_manifest.py +249 -249
- tapps_agents/core/issue_schema.py +139 -139
- tapps_agents/core/json_utils.py +128 -128
- tapps_agents/core/knowledge_graph.py +446 -446
- tapps_agents/core/language_detector.py +296 -296
- tapps_agents/core/learning_confidence.py +242 -242
- tapps_agents/core/learning_dashboard.py +246 -246
- tapps_agents/core/learning_decision.py +384 -384
- tapps_agents/core/learning_explainability.py +578 -578
- tapps_agents/core/learning_export.py +287 -287
- tapps_agents/core/learning_integration.py +228 -228
- tapps_agents/core/llm_behavior.py +232 -232
- tapps_agents/core/long_duration_support.py +786 -786
- tapps_agents/core/mcp_setup.py +106 -106
- tapps_agents/core/memory_integration.py +396 -396
- tapps_agents/core/meta_learning.py +666 -666
- tapps_agents/core/module_path_sanitizer.py +199 -199
- tapps_agents/core/multi_agent_orchestrator.py +382 -382
- tapps_agents/core/network_errors.py +125 -125
- tapps_agents/core/nfr_validator.py +336 -336
- tapps_agents/core/offline_mode.py +158 -158
- tapps_agents/core/output_contracts.py +300 -300
- tapps_agents/core/output_formatter.py +300 -300
- tapps_agents/core/path_normalizer.py +174 -174
- tapps_agents/core/path_validator.py +322 -322
- tapps_agents/core/pattern_library.py +250 -250
- tapps_agents/core/performance_benchmark.py +301 -301
- tapps_agents/core/performance_monitor.py +184 -184
- tapps_agents/core/playwright_mcp_controller.py +771 -771
- tapps_agents/core/policy_loader.py +135 -135
- tapps_agents/core/progress.py +166 -166
- tapps_agents/core/project_profile.py +354 -354
- tapps_agents/core/project_type_detector.py +454 -454
- tapps_agents/core/prompt_base.py +223 -223
- tapps_agents/core/prompt_learning/__init__.py +2 -2
- tapps_agents/core/prompt_learning/learning_loop.py +24 -24
- tapps_agents/core/prompt_learning/project_prompt_store.py +25 -25
- tapps_agents/core/prompt_learning/skills_prompt_analyzer.py +35 -35
- tapps_agents/core/prompt_optimization/__init__.py +6 -6
- tapps_agents/core/prompt_optimization/ab_tester.py +114 -114
- tapps_agents/core/prompt_optimization/correlation_analyzer.py +160 -160
- tapps_agents/core/prompt_optimization/progressive_refiner.py +129 -129
- tapps_agents/core/prompt_optimization/prompt_library.py +37 -37
- tapps_agents/core/requirements_evaluator.py +431 -431
- tapps_agents/core/resource_aware_executor.py +449 -449
- tapps_agents/core/resource_monitor.py +343 -343
- tapps_agents/core/resume_handler.py +298 -298
- tapps_agents/core/retry_handler.py +197 -197
- tapps_agents/core/review_checklists.py +479 -479
- tapps_agents/core/role_loader.py +201 -201
- tapps_agents/core/role_template_loader.py +201 -201
- tapps_agents/core/runtime_mode.py +60 -60
- tapps_agents/core/security_scanner.py +342 -342
- tapps_agents/core/skill_agent_registry.py +194 -194
- tapps_agents/core/skill_integration.py +208 -208
- tapps_agents/core/skill_loader.py +492 -492
- tapps_agents/core/skill_template.py +341 -341
- tapps_agents/core/skill_validator.py +478 -478
- tapps_agents/core/stack_analyzer.py +35 -35
- tapps_agents/core/startup.py +174 -174
- tapps_agents/core/storage_manager.py +397 -397
- tapps_agents/core/storage_models.py +166 -166
- tapps_agents/core/story_evaluator.py +410 -410
- tapps_agents/core/subprocess_utils.py +170 -170
- tapps_agents/core/task_duration.py +296 -296
- tapps_agents/core/task_memory.py +582 -582
- tapps_agents/core/task_state.py +226 -226
- tapps_agents/core/tech_stack_priorities.py +208 -208
- tapps_agents/core/temp_directory.py +194 -194
- tapps_agents/core/template_merger.py +600 -600
- tapps_agents/core/template_selector.py +280 -280
- tapps_agents/core/test_generator.py +286 -286
- tapps_agents/core/tiered_context.py +253 -253
- tapps_agents/core/token_monitor.py +345 -345
- tapps_agents/core/traceability.py +254 -254
- tapps_agents/core/trajectory_tracker.py +50 -50
- tapps_agents/core/unicode_safe.py +143 -143
- tapps_agents/core/unified_cache_config.py +170 -170
- tapps_agents/core/unified_state.py +324 -324
- tapps_agents/core/validate_cursor_setup.py +237 -237
- tapps_agents/core/validation_registry.py +136 -136
- tapps_agents/core/validators/__init__.py +4 -4
- tapps_agents/core/validators/python_validator.py +87 -87
- tapps_agents/core/verification_agent.py +90 -90
- tapps_agents/core/visual_feedback.py +644 -644
- tapps_agents/core/workflow_validator.py +197 -197
- tapps_agents/core/worktree.py +367 -367
- tapps_agents/docker/__init__.py +10 -10
- tapps_agents/docker/analyzer.py +186 -186
- tapps_agents/docker/debugger.py +229 -229
- tapps_agents/docker/error_patterns.py +216 -216
- tapps_agents/epic/__init__.py +22 -22
- tapps_agents/epic/beads_sync.py +115 -115
- tapps_agents/epic/markdown_sync.py +105 -105
- tapps_agents/epic/models.py +96 -96
- tapps_agents/experts/__init__.py +163 -163
- tapps_agents/experts/agent_integration.py +243 -243
- tapps_agents/experts/auto_generator.py +331 -331
- tapps_agents/experts/base_expert.py +536 -536
- tapps_agents/experts/builtin_registry.py +261 -261
- tapps_agents/experts/business_metrics.py +565 -565
- tapps_agents/experts/cache.py +266 -266
- tapps_agents/experts/confidence_breakdown.py +306 -306
- tapps_agents/experts/confidence_calculator.py +336 -336
- tapps_agents/experts/confidence_metrics.py +236 -236
- tapps_agents/experts/domain_config.py +311 -311
- tapps_agents/experts/domain_detector.py +550 -550
- tapps_agents/experts/domain_utils.py +84 -84
- tapps_agents/experts/expert_config.py +113 -113
- tapps_agents/experts/expert_engine.py +465 -465
- tapps_agents/experts/expert_registry.py +744 -744
- tapps_agents/experts/expert_synthesizer.py +70 -70
- tapps_agents/experts/governance.py +197 -197
- tapps_agents/experts/history_logger.py +312 -312
- tapps_agents/experts/knowledge/README.md +180 -180
- tapps_agents/experts/knowledge/accessibility/accessible-forms.md +331 -331
- tapps_agents/experts/knowledge/accessibility/aria-patterns.md +344 -344
- tapps_agents/experts/knowledge/accessibility/color-contrast.md +285 -285
- tapps_agents/experts/knowledge/accessibility/keyboard-navigation.md +332 -332
- tapps_agents/experts/knowledge/accessibility/screen-readers.md +282 -282
- tapps_agents/experts/knowledge/accessibility/semantic-html.md +355 -355
- tapps_agents/experts/knowledge/accessibility/testing-accessibility.md +369 -369
- tapps_agents/experts/knowledge/accessibility/wcag-2.1.md +296 -296
- tapps_agents/experts/knowledge/accessibility/wcag-2.2.md +211 -211
- tapps_agents/experts/knowledge/agent-learning/best-practices.md +715 -715
- tapps_agents/experts/knowledge/agent-learning/pattern-extraction.md +282 -282
- tapps_agents/experts/knowledge/agent-learning/prompt-optimization.md +320 -320
- tapps_agents/experts/knowledge/ai-frameworks/model-optimization.md +90 -90
- tapps_agents/experts/knowledge/ai-frameworks/openvino-patterns.md +260 -260
- tapps_agents/experts/knowledge/api-design-integration/api-gateway-patterns.md +309 -309
- tapps_agents/experts/knowledge/api-design-integration/api-security-patterns.md +521 -521
- tapps_agents/experts/knowledge/api-design-integration/api-versioning.md +421 -421
- tapps_agents/experts/knowledge/api-design-integration/async-protocol-patterns.md +61 -61
- tapps_agents/experts/knowledge/api-design-integration/contract-testing.md +221 -221
- tapps_agents/experts/knowledge/api-design-integration/external-api-integration.md +489 -489
- tapps_agents/experts/knowledge/api-design-integration/fastapi-patterns.md +360 -360
- tapps_agents/experts/knowledge/api-design-integration/fastapi-testing.md +262 -262
- tapps_agents/experts/knowledge/api-design-integration/graphql-patterns.md +582 -582
- tapps_agents/experts/knowledge/api-design-integration/grpc-best-practices.md +499 -499
- tapps_agents/experts/knowledge/api-design-integration/mqtt-patterns.md +455 -455
- tapps_agents/experts/knowledge/api-design-integration/rate-limiting.md +507 -507
- tapps_agents/experts/knowledge/api-design-integration/restful-api-design.md +618 -618
- tapps_agents/experts/knowledge/api-design-integration/websocket-patterns.md +480 -480
- tapps_agents/experts/knowledge/cloud-infrastructure/cloud-native-patterns.md +175 -175
- tapps_agents/experts/knowledge/cloud-infrastructure/container-health-checks.md +261 -261
- tapps_agents/experts/knowledge/cloud-infrastructure/containerization.md +222 -222
- tapps_agents/experts/knowledge/cloud-infrastructure/cost-optimization.md +122 -122
- tapps_agents/experts/knowledge/cloud-infrastructure/disaster-recovery.md +153 -153
- tapps_agents/experts/knowledge/cloud-infrastructure/dockerfile-patterns.md +285 -285
- tapps_agents/experts/knowledge/cloud-infrastructure/infrastructure-as-code.md +187 -187
- tapps_agents/experts/knowledge/cloud-infrastructure/kubernetes-patterns.md +253 -253
- tapps_agents/experts/knowledge/cloud-infrastructure/multi-cloud-strategies.md +155 -155
- tapps_agents/experts/knowledge/cloud-infrastructure/serverless-architecture.md +200 -200
- tapps_agents/experts/knowledge/code-quality-analysis/README.md +16 -16
- tapps_agents/experts/knowledge/code-quality-analysis/code-metrics.md +137 -137
- tapps_agents/experts/knowledge/code-quality-analysis/complexity-analysis.md +181 -181
- tapps_agents/experts/knowledge/code-quality-analysis/technical-debt-patterns.md +191 -191
- tapps_agents/experts/knowledge/data-privacy-compliance/anonymization.md +313 -313
- tapps_agents/experts/knowledge/data-privacy-compliance/ccpa.md +255 -255
- tapps_agents/experts/knowledge/data-privacy-compliance/consent-management.md +282 -282
- tapps_agents/experts/knowledge/data-privacy-compliance/data-minimization.md +275 -275
- tapps_agents/experts/knowledge/data-privacy-compliance/data-retention.md +297 -297
- tapps_agents/experts/knowledge/data-privacy-compliance/data-subject-rights.md +383 -383
- tapps_agents/experts/knowledge/data-privacy-compliance/encryption-privacy.md +285 -285
- tapps_agents/experts/knowledge/data-privacy-compliance/gdpr.md +344 -344
- tapps_agents/experts/knowledge/data-privacy-compliance/hipaa.md +385 -385
- tapps_agents/experts/knowledge/data-privacy-compliance/privacy-by-design.md +280 -280
- tapps_agents/experts/knowledge/database-data-management/acid-vs-cap.md +164 -164
- tapps_agents/experts/knowledge/database-data-management/backup-and-recovery.md +182 -182
- tapps_agents/experts/knowledge/database-data-management/data-modeling.md +172 -172
- tapps_agents/experts/knowledge/database-data-management/database-design.md +187 -187
- tapps_agents/experts/knowledge/database-data-management/flux-query-optimization.md +342 -342
- tapps_agents/experts/knowledge/database-data-management/influxdb-connection-patterns.md +432 -432
- tapps_agents/experts/knowledge/database-data-management/influxdb-patterns.md +442 -442
- tapps_agents/experts/knowledge/database-data-management/migration-strategies.md +216 -216
- tapps_agents/experts/knowledge/database-data-management/nosql-patterns.md +259 -259
- tapps_agents/experts/knowledge/database-data-management/scalability-patterns.md +184 -184
- tapps_agents/experts/knowledge/database-data-management/sql-optimization.md +175 -175
- tapps_agents/experts/knowledge/database-data-management/time-series-modeling.md +444 -444
- tapps_agents/experts/knowledge/development-workflow/README.md +16 -16
- tapps_agents/experts/knowledge/development-workflow/automation-best-practices.md +216 -216
- tapps_agents/experts/knowledge/development-workflow/build-strategies.md +198 -198
- tapps_agents/experts/knowledge/development-workflow/deployment-patterns.md +205 -205
- tapps_agents/experts/knowledge/development-workflow/git-workflows.md +205 -205
- tapps_agents/experts/knowledge/documentation-knowledge-management/README.md +16 -16
- tapps_agents/experts/knowledge/documentation-knowledge-management/api-documentation-patterns.md +231 -231
- tapps_agents/experts/knowledge/documentation-knowledge-management/documentation-standards.md +191 -191
- tapps_agents/experts/knowledge/documentation-knowledge-management/knowledge-management.md +171 -171
- tapps_agents/experts/knowledge/documentation-knowledge-management/technical-writing-guide.md +192 -192
- tapps_agents/experts/knowledge/observability-monitoring/alerting-patterns.md +461 -461
- tapps_agents/experts/knowledge/observability-monitoring/apm-tools.md +459 -459
- tapps_agents/experts/knowledge/observability-monitoring/distributed-tracing.md +367 -367
- tapps_agents/experts/knowledge/observability-monitoring/logging-strategies.md +478 -478
- tapps_agents/experts/knowledge/observability-monitoring/metrics-and-monitoring.md +510 -510
- tapps_agents/experts/knowledge/observability-monitoring/observability-best-practices.md +492 -492
- tapps_agents/experts/knowledge/observability-monitoring/open-telemetry.md +573 -573
- tapps_agents/experts/knowledge/observability-monitoring/slo-sli-sla.md +419 -419
- tapps_agents/experts/knowledge/performance/anti-patterns.md +284 -284
- tapps_agents/experts/knowledge/performance/api-performance.md +256 -256
- tapps_agents/experts/knowledge/performance/caching.md +327 -327
- tapps_agents/experts/knowledge/performance/database-performance.md +252 -252
- tapps_agents/experts/knowledge/performance/optimization-patterns.md +327 -327
- tapps_agents/experts/knowledge/performance/profiling.md +297 -297
- tapps_agents/experts/knowledge/performance/resource-management.md +293 -293
- tapps_agents/experts/knowledge/performance/scalability.md +306 -306
- tapps_agents/experts/knowledge/security/owasp-top10.md +209 -209
- tapps_agents/experts/knowledge/security/secure-coding-practices.md +207 -207
- tapps_agents/experts/knowledge/security/threat-modeling.md +220 -220
- tapps_agents/experts/knowledge/security/vulnerability-patterns.md +342 -342
- tapps_agents/experts/knowledge/software-architecture/docker-compose-patterns.md +314 -314
- tapps_agents/experts/knowledge/software-architecture/microservices-patterns.md +379 -379
- tapps_agents/experts/knowledge/software-architecture/service-communication.md +316 -316
- tapps_agents/experts/knowledge/testing/best-practices.md +310 -310
- tapps_agents/experts/knowledge/testing/coverage-analysis.md +293 -293
- tapps_agents/experts/knowledge/testing/mocking.md +256 -256
- tapps_agents/experts/knowledge/testing/test-automation.md +276 -276
- tapps_agents/experts/knowledge/testing/test-data.md +271 -271
- tapps_agents/experts/knowledge/testing/test-design-patterns.md +280 -280
- tapps_agents/experts/knowledge/testing/test-maintenance.md +236 -236
- tapps_agents/experts/knowledge/testing/test-strategies.md +311 -311
- tapps_agents/experts/knowledge/user-experience/information-architecture.md +325 -325
- tapps_agents/experts/knowledge/user-experience/interaction-design.md +363 -363
- tapps_agents/experts/knowledge/user-experience/prototyping.md +293 -293
- tapps_agents/experts/knowledge/user-experience/usability-heuristics.md +337 -337
- tapps_agents/experts/knowledge/user-experience/usability-testing.md +311 -311
- tapps_agents/experts/knowledge/user-experience/user-journeys.md +296 -296
- tapps_agents/experts/knowledge/user-experience/user-research.md +373 -373
- tapps_agents/experts/knowledge/user-experience/ux-principles.md +340 -340
- tapps_agents/experts/knowledge_freshness.py +321 -321
- tapps_agents/experts/knowledge_ingestion.py +438 -438
- tapps_agents/experts/knowledge_need_detector.py +93 -93
- tapps_agents/experts/knowledge_validator.py +382 -382
- tapps_agents/experts/observability.py +440 -440
- tapps_agents/experts/passive_notifier.py +238 -238
- tapps_agents/experts/proactive_orchestrator.py +32 -32
- tapps_agents/experts/rag_chunker.py +205 -205
- tapps_agents/experts/rag_embedder.py +152 -152
- tapps_agents/experts/rag_evaluation.py +299 -299
- tapps_agents/experts/rag_index.py +303 -303
- tapps_agents/experts/rag_metrics.py +293 -293
- tapps_agents/experts/rag_safety.py +263 -263
- tapps_agents/experts/report_generator.py +296 -296
- tapps_agents/experts/setup_wizard.py +441 -441
- tapps_agents/experts/simple_rag.py +431 -431
- tapps_agents/experts/vector_rag.py +354 -354
- tapps_agents/experts/weight_distributor.py +304 -304
- tapps_agents/health/__init__.py +24 -24
- tapps_agents/health/base.py +75 -75
- tapps_agents/health/checks/__init__.py +22 -22
- tapps_agents/health/checks/automation.py +127 -127
- tapps_agents/health/checks/context7_cache.py +210 -210
- tapps_agents/health/checks/environment.py +116 -116
- tapps_agents/health/checks/execution.py +170 -170
- tapps_agents/health/checks/knowledge_base.py +187 -187
- tapps_agents/health/checks/outcomes.backup_20260204_064058.py +324 -0
- tapps_agents/health/checks/outcomes.backup_20260204_064256.py +324 -0
- tapps_agents/health/checks/outcomes.backup_20260204_064600.py +324 -0
- tapps_agents/health/checks/outcomes.py +324 -324
- tapps_agents/health/collector.py +280 -280
- tapps_agents/health/dashboard.py +137 -137
- tapps_agents/health/metrics.py +151 -151
- tapps_agents/health/registry.py +166 -166
- tapps_agents/hooks/__init__.py +33 -33
- tapps_agents/hooks/config.py +140 -140
- tapps_agents/hooks/events.py +135 -135
- tapps_agents/hooks/executor.py +128 -128
- tapps_agents/hooks/manager.py +143 -143
- tapps_agents/integration/__init__.py +8 -8
- tapps_agents/integration/service_integrator.py +121 -121
- tapps_agents/integrations/__init__.py +10 -10
- tapps_agents/integrations/clawdbot.py +525 -525
- tapps_agents/integrations/memory_bridge.py +356 -356
- tapps_agents/mcp/__init__.py +18 -18
- tapps_agents/mcp/gateway.py +112 -112
- tapps_agents/mcp/servers/__init__.py +13 -13
- tapps_agents/mcp/servers/analysis.py +204 -204
- tapps_agents/mcp/servers/context7.py +198 -198
- tapps_agents/mcp/servers/filesystem.py +218 -218
- tapps_agents/mcp/servers/git.py +201 -201
- tapps_agents/mcp/tool_registry.py +115 -115
- tapps_agents/quality/__init__.py +54 -54
- tapps_agents/quality/coverage_analyzer.py +379 -379
- tapps_agents/quality/enforcement.py +82 -82
- tapps_agents/quality/gates/__init__.py +37 -37
- tapps_agents/quality/gates/approval_gate.py +255 -255
- tapps_agents/quality/gates/base.py +84 -84
- tapps_agents/quality/gates/exceptions.py +43 -43
- tapps_agents/quality/gates/policy_gate.py +195 -195
- tapps_agents/quality/gates/registry.py +239 -239
- tapps_agents/quality/gates/security_gate.py +156 -156
- tapps_agents/quality/quality_gates.py +369 -369
- tapps_agents/quality/secret_scanner.py +335 -335
- tapps_agents/resources/__init__.py +5 -0
- tapps_agents/resources/claude/__init__.py +1 -0
- tapps_agents/resources/claude/commands/README.md +156 -0
- tapps_agents/resources/claude/commands/__init__.py +1 -0
- tapps_agents/resources/claude/commands/build-fix.md +22 -0
- tapps_agents/resources/claude/commands/build.md +77 -0
- tapps_agents/resources/claude/commands/debug.md +53 -0
- tapps_agents/resources/claude/commands/design.md +68 -0
- tapps_agents/resources/claude/commands/docs.md +53 -0
- tapps_agents/resources/claude/commands/e2e.md +22 -0
- tapps_agents/resources/claude/commands/fix.md +54 -0
- tapps_agents/resources/claude/commands/implement.md +53 -0
- tapps_agents/resources/claude/commands/improve.md +53 -0
- tapps_agents/resources/claude/commands/library-docs.md +64 -0
- tapps_agents/resources/claude/commands/lint.md +52 -0
- tapps_agents/resources/claude/commands/plan.md +65 -0
- tapps_agents/resources/claude/commands/refactor-clean.md +21 -0
- tapps_agents/resources/claude/commands/refactor.md +55 -0
- tapps_agents/resources/claude/commands/review.md +67 -0
- tapps_agents/resources/claude/commands/score.md +60 -0
- tapps_agents/resources/claude/commands/security-review.md +22 -0
- tapps_agents/resources/claude/commands/security-scan.md +54 -0
- tapps_agents/resources/claude/commands/tdd.md +24 -0
- tapps_agents/resources/claude/commands/test-coverage.md +21 -0
- tapps_agents/resources/claude/commands/test.md +54 -0
- tapps_agents/resources/claude/commands/update-codemaps.md +20 -0
- tapps_agents/resources/claude/commands/update-docs.md +21 -0
- tapps_agents/resources/claude/skills/__init__.py +1 -0
- tapps_agents/resources/claude/skills/analyst/SKILL.md +272 -0
- tapps_agents/resources/claude/skills/analyst/__init__.py +1 -0
- tapps_agents/resources/claude/skills/architect/SKILL.md +282 -0
- tapps_agents/resources/claude/skills/architect/__init__.py +1 -0
- tapps_agents/resources/claude/skills/backend-patterns/SKILL.md +30 -0
- tapps_agents/resources/claude/skills/backend-patterns/__init__.py +1 -0
- tapps_agents/resources/claude/skills/coding-standards/SKILL.md +29 -0
- tapps_agents/resources/claude/skills/coding-standards/__init__.py +1 -0
- tapps_agents/resources/claude/skills/debugger/SKILL.md +203 -0
- tapps_agents/resources/claude/skills/debugger/__init__.py +1 -0
- tapps_agents/resources/claude/skills/designer/SKILL.md +243 -0
- tapps_agents/resources/claude/skills/designer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/documenter/SKILL.md +252 -0
- tapps_agents/resources/claude/skills/documenter/__init__.py +1 -0
- tapps_agents/resources/claude/skills/enhancer/SKILL.md +307 -0
- tapps_agents/resources/claude/skills/enhancer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/evaluator/SKILL.md +204 -0
- tapps_agents/resources/claude/skills/evaluator/__init__.py +1 -0
- tapps_agents/resources/claude/skills/frontend-patterns/SKILL.md +29 -0
- tapps_agents/resources/claude/skills/frontend-patterns/__init__.py +1 -0
- tapps_agents/resources/claude/skills/implementer/SKILL.md +188 -0
- tapps_agents/resources/claude/skills/implementer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/improver/SKILL.md +218 -0
- tapps_agents/resources/claude/skills/improver/__init__.py +1 -0
- tapps_agents/resources/claude/skills/ops/SKILL.md +281 -0
- tapps_agents/resources/claude/skills/ops/__init__.py +1 -0
- tapps_agents/resources/claude/skills/orchestrator/SKILL.md +390 -0
- tapps_agents/resources/claude/skills/orchestrator/__init__.py +1 -0
- tapps_agents/resources/claude/skills/planner/SKILL.md +254 -0
- tapps_agents/resources/claude/skills/planner/__init__.py +1 -0
- tapps_agents/resources/claude/skills/reviewer/SKILL.md +434 -0
- tapps_agents/resources/claude/skills/reviewer/__init__.py +1 -0
- tapps_agents/resources/claude/skills/security-review/SKILL.md +31 -0
- tapps_agents/resources/claude/skills/security-review/__init__.py +1 -0
- tapps_agents/resources/claude/skills/simple-mode/SKILL.md +695 -0
- tapps_agents/resources/claude/skills/simple-mode/__init__.py +1 -0
- tapps_agents/resources/claude/skills/tester/SKILL.md +219 -0
- tapps_agents/resources/claude/skills/tester/__init__.py +1 -0
- tapps_agents/resources/cursor/.cursorignore +35 -0
- tapps_agents/resources/cursor/__init__.py +1 -0
- tapps_agents/resources/cursor/commands/__init__.py +1 -0
- tapps_agents/resources/cursor/commands/build-fix.md +11 -0
- tapps_agents/resources/cursor/commands/build.md +11 -0
- tapps_agents/resources/cursor/commands/e2e.md +11 -0
- tapps_agents/resources/cursor/commands/fix.md +11 -0
- tapps_agents/resources/cursor/commands/refactor-clean.md +11 -0
- tapps_agents/resources/cursor/commands/review.md +11 -0
- tapps_agents/resources/cursor/commands/security-review.md +11 -0
- tapps_agents/resources/cursor/commands/tdd.md +11 -0
- tapps_agents/resources/cursor/commands/test-coverage.md +11 -0
- tapps_agents/resources/cursor/commands/test.md +11 -0
- tapps_agents/resources/cursor/commands/update-codemaps.md +10 -0
- tapps_agents/resources/cursor/commands/update-docs.md +11 -0
- tapps_agents/resources/cursor/rules/__init__.py +1 -0
- tapps_agents/resources/cursor/rules/agent-capabilities.mdc +687 -0
- tapps_agents/resources/cursor/rules/coding-style.mdc +31 -0
- tapps_agents/resources/cursor/rules/command-reference.mdc +2081 -0
- tapps_agents/resources/cursor/rules/cursor-mode-usage.mdc +125 -0
- tapps_agents/resources/cursor/rules/git-workflow.mdc +29 -0
- tapps_agents/resources/cursor/rules/performance.mdc +29 -0
- tapps_agents/resources/cursor/rules/project-context.mdc +163 -0
- tapps_agents/resources/cursor/rules/project-profiling.mdc +197 -0
- tapps_agents/resources/cursor/rules/quick-reference.mdc +630 -0
- tapps_agents/resources/cursor/rules/security.mdc +32 -0
- tapps_agents/resources/cursor/rules/simple-mode.mdc +500 -0
- tapps_agents/resources/cursor/rules/testing.mdc +31 -0
- tapps_agents/resources/cursor/rules/when-to-use.mdc +156 -0
- tapps_agents/resources/cursor/rules/workflow-presets.mdc +179 -0
- tapps_agents/resources/customizations/__init__.py +1 -0
- tapps_agents/resources/customizations/example-custom.yaml +83 -0
- tapps_agents/resources/hooks/__init__.py +1 -0
- tapps_agents/resources/hooks/templates/README.md +5 -0
- tapps_agents/resources/hooks/templates/__init__.py +1 -0
- tapps_agents/resources/hooks/templates/add-project-context.yaml +8 -0
- tapps_agents/resources/hooks/templates/auto-format-js.yaml +10 -0
- tapps_agents/resources/hooks/templates/auto-format-python.yaml +10 -0
- tapps_agents/resources/hooks/templates/git-commit-check.yaml +7 -0
- tapps_agents/resources/hooks/templates/notify-on-complete.yaml +8 -0
- tapps_agents/resources/hooks/templates/quality-gate.yaml +8 -0
- tapps_agents/resources/hooks/templates/security-scan-on-edit.yaml +10 -0
- tapps_agents/resources/hooks/templates/session-end-log.yaml +7 -0
- tapps_agents/resources/hooks/templates/show-beads-ready.yaml +8 -0
- tapps_agents/resources/hooks/templates/test-on-edit.yaml +10 -0
- tapps_agents/resources/hooks/templates/update-docs-on-complete.yaml +8 -0
- tapps_agents/resources/hooks/templates/user-prompt-log.yaml +7 -0
- tapps_agents/resources/scripts/__init__.py +1 -0
- tapps_agents/resources/scripts/set_bd_path.ps1 +51 -0
- tapps_agents/resources/workflows/__init__.py +1 -0
- tapps_agents/resources/workflows/presets/__init__.py +1 -0
- tapps_agents/resources/workflows/presets/brownfield-analysis.yaml +235 -0
- tapps_agents/resources/workflows/presets/fix.yaml +78 -0
- tapps_agents/resources/workflows/presets/full-sdlc.yaml +122 -0
- tapps_agents/resources/workflows/presets/quality.yaml +82 -0
- tapps_agents/resources/workflows/presets/rapid-dev.yaml +84 -0
- tapps_agents/session/__init__.py +19 -19
- tapps_agents/session/manager.py +256 -256
- tapps_agents/simple_mode/__init__.py +66 -66
- tapps_agents/simple_mode/agent_contracts.py +357 -357
- tapps_agents/simple_mode/beads_hooks.py +151 -151
- tapps_agents/simple_mode/code_snippet_handler.py +382 -382
- tapps_agents/simple_mode/documentation_manager.py +395 -395
- tapps_agents/simple_mode/documentation_reader.py +187 -187
- tapps_agents/simple_mode/file_inference.py +292 -292
- tapps_agents/simple_mode/framework_change_detector.py +268 -268
- tapps_agents/simple_mode/intent_parser.py +510 -510
- tapps_agents/simple_mode/learning_progression.py +358 -358
- tapps_agents/simple_mode/nl_handler.py +700 -700
- tapps_agents/simple_mode/onboarding.py +253 -253
- tapps_agents/simple_mode/orchestrators/__init__.py +38 -38
- tapps_agents/simple_mode/orchestrators/breakdown_orchestrator.py +49 -49
- tapps_agents/simple_mode/orchestrators/brownfield_orchestrator.py +135 -135
- tapps_agents/simple_mode/orchestrators/deliverable_checklist.py +349 -349
- tapps_agents/simple_mode/orchestrators/enhance_orchestrator.py +53 -53
- tapps_agents/simple_mode/orchestrators/epic_orchestrator.py +122 -122
- tapps_agents/simple_mode/orchestrators/explore_orchestrator.py +184 -184
- tapps_agents/simple_mode/orchestrators/plan_analysis_orchestrator.py +206 -206
- tapps_agents/simple_mode/orchestrators/pr_orchestrator.py +237 -237
- tapps_agents/simple_mode/orchestrators/refactor_orchestrator.py +222 -222
- tapps_agents/simple_mode/orchestrators/requirements_tracer.py +262 -262
- tapps_agents/simple_mode/orchestrators/resume_orchestrator.py +210 -210
- tapps_agents/simple_mode/orchestrators/review_orchestrator.py +161 -161
- tapps_agents/simple_mode/orchestrators/test_orchestrator.py +82 -82
- tapps_agents/simple_mode/output_aggregator.py +340 -340
- tapps_agents/simple_mode/result_formatters.py +598 -598
- tapps_agents/simple_mode/step_dependencies.py +382 -382
- tapps_agents/simple_mode/step_results.py +276 -276
- tapps_agents/simple_mode/streaming.py +388 -388
- tapps_agents/simple_mode/variations.py +129 -129
- tapps_agents/simple_mode/visual_feedback.py +238 -238
- tapps_agents/simple_mode/zero_config.py +274 -274
- tapps_agents/suggestions/__init__.py +8 -8
- tapps_agents/suggestions/inline_suggester.py +52 -52
- tapps_agents/templates/__init__.py +8 -8
- tapps_agents/templates/microservice_generator.py +274 -274
- tapps_agents/utils/env_validator.py +291 -291
- tapps_agents/workflow/__init__.py +171 -171
- tapps_agents/workflow/acceptance_verifier.py +132 -132
- tapps_agents/workflow/agent_handlers/__init__.py +41 -41
- tapps_agents/workflow/agent_handlers/analyst_handler.py +75 -75
- tapps_agents/workflow/agent_handlers/architect_handler.py +107 -107
- tapps_agents/workflow/agent_handlers/base.py +84 -84
- tapps_agents/workflow/agent_handlers/debugger_handler.py +100 -100
- tapps_agents/workflow/agent_handlers/designer_handler.py +110 -110
- tapps_agents/workflow/agent_handlers/documenter_handler.py +94 -94
- tapps_agents/workflow/agent_handlers/implementer_handler.py +235 -235
- tapps_agents/workflow/agent_handlers/ops_handler.py +62 -62
- tapps_agents/workflow/agent_handlers/orchestrator_handler.py +43 -43
- tapps_agents/workflow/agent_handlers/planner_handler.py +98 -98
- tapps_agents/workflow/agent_handlers/registry.py +119 -119
- tapps_agents/workflow/agent_handlers/reviewer_handler.py +119 -119
- tapps_agents/workflow/agent_handlers/tester_handler.py +69 -69
- tapps_agents/workflow/analytics_accessor.py +337 -337
- tapps_agents/workflow/analytics_alerts.py +416 -416
- tapps_agents/workflow/analytics_dashboard_cursor.py +281 -281
- tapps_agents/workflow/analytics_dual_write.py +103 -103
- tapps_agents/workflow/analytics_integration.py +119 -119
- tapps_agents/workflow/analytics_query_parser.py +278 -278
- tapps_agents/workflow/analytics_visualizer.py +259 -259
- tapps_agents/workflow/artifact_helper.py +204 -204
- tapps_agents/workflow/audit_logger.py +263 -263
- tapps_agents/workflow/auto_execution_config.py +340 -340
- tapps_agents/workflow/auto_progression.py +586 -586
- tapps_agents/workflow/branch_cleanup.py +349 -349
- tapps_agents/workflow/checkpoint.py +256 -256
- tapps_agents/workflow/checkpoint_manager.py +178 -178
- tapps_agents/workflow/code_artifact.py +179 -179
- tapps_agents/workflow/common_enums.py +96 -96
- tapps_agents/workflow/confirmation_handler.py +130 -130
- tapps_agents/workflow/context_analyzer.py +222 -222
- tapps_agents/workflow/context_artifact.py +230 -230
- tapps_agents/workflow/cursor_chat.py +94 -94
- tapps_agents/workflow/cursor_skill_helper.py +516 -516
- tapps_agents/workflow/dependency_resolver.py +244 -244
- tapps_agents/workflow/design_artifact.py +156 -156
- tapps_agents/workflow/detector.py +751 -751
- tapps_agents/workflow/direct_execution_fallback.py +301 -301
- tapps_agents/workflow/docs_artifact.py +168 -168
- tapps_agents/workflow/enforcer.py +389 -389
- tapps_agents/workflow/enhancement_artifact.py +142 -142
- tapps_agents/workflow/error_recovery.py +806 -806
- tapps_agents/workflow/event_bus.py +183 -183
- tapps_agents/workflow/event_log.py +612 -612
- tapps_agents/workflow/events.py +63 -63
- tapps_agents/workflow/exceptions.py +43 -43
- tapps_agents/workflow/execution_graph.py +498 -498
- tapps_agents/workflow/execution_plan.py +126 -126
- tapps_agents/workflow/file_utils.py +186 -186
- tapps_agents/workflow/gate_evaluator.py +182 -182
- tapps_agents/workflow/gate_integration.py +200 -200
- tapps_agents/workflow/graph_visualizer.py +130 -130
- tapps_agents/workflow/health_checker.py +206 -206
- tapps_agents/workflow/logging_helper.py +243 -243
- tapps_agents/workflow/manifest.py +582 -582
- tapps_agents/workflow/marker_writer.py +250 -250
- tapps_agents/workflow/messaging.py +325 -325
- tapps_agents/workflow/metadata_models.py +91 -91
- tapps_agents/workflow/metrics_integration.py +226 -226
- tapps_agents/workflow/migration_utils.py +116 -116
- tapps_agents/workflow/models.py +148 -148
- tapps_agents/workflow/nlp_config.py +198 -198
- tapps_agents/workflow/nlp_error_handler.py +207 -207
- tapps_agents/workflow/nlp_executor.py +163 -163
- tapps_agents/workflow/nlp_parser.py +528 -528
- tapps_agents/workflow/observability_dashboard.py +451 -451
- tapps_agents/workflow/observer.py +170 -170
- tapps_agents/workflow/ops_artifact.py +257 -257
- tapps_agents/workflow/output_passing.py +214 -214
- tapps_agents/workflow/parallel_executor.py +463 -463
- tapps_agents/workflow/planning_artifact.py +179 -179
- tapps_agents/workflow/preset_loader.py +285 -285
- tapps_agents/workflow/preset_recommender.py +270 -270
- tapps_agents/workflow/progress_logger.py +145 -145
- tapps_agents/workflow/progress_manager.py +303 -303
- tapps_agents/workflow/progress_monitor.py +186 -186
- tapps_agents/workflow/progress_updates.py +423 -423
- tapps_agents/workflow/quality_artifact.py +158 -158
- tapps_agents/workflow/quality_loopback.py +101 -101
- tapps_agents/workflow/recommender.py +387 -387
- tapps_agents/workflow/remediation_loop.py +166 -166
- tapps_agents/workflow/result_aggregator.py +300 -300
- tapps_agents/workflow/review_artifact.py +185 -185
- tapps_agents/workflow/schema_validator.py +522 -522
- tapps_agents/workflow/session_handoff.py +178 -178
- tapps_agents/workflow/skill_invoker.py +648 -648
- tapps_agents/workflow/state_manager.py +756 -756
- tapps_agents/workflow/state_persistence_config.py +331 -331
- tapps_agents/workflow/status_monitor.py +449 -449
- tapps_agents/workflow/step_checkpoint.py +314 -314
- tapps_agents/workflow/step_details.py +201 -201
- tapps_agents/workflow/story_models.py +147 -147
- tapps_agents/workflow/streaming.py +416 -416
- tapps_agents/workflow/suggestion_engine.py +552 -552
- tapps_agents/workflow/testing_artifact.py +186 -186
- tapps_agents/workflow/timeline.py +158 -158
- tapps_agents/workflow/token_integration.py +209 -209
- tapps_agents/workflow/validation.py +217 -217
- tapps_agents/workflow/visual_feedback.py +391 -391
- tapps_agents/workflow/workflow_chain.py +95 -95
- tapps_agents/workflow/workflow_summary.py +219 -219
- tapps_agents/workflow/worktree_manager.py +724 -724
- {tapps_agents-3.6.0.dist-info → tapps_agents-3.6.1.dist-info}/METADATA +672 -672
- tapps_agents-3.6.1.dist-info/RECORD +883 -0
- {tapps_agents-3.6.0.dist-info → tapps_agents-3.6.1.dist-info}/licenses/LICENSE +22 -22
- tapps_agents-3.6.0.dist-info/RECORD +0 -758
- {tapps_agents-3.6.0.dist-info → tapps_agents-3.6.1.dist-info}/WHEEL +0 -0
- {tapps_agents-3.6.0.dist-info → tapps_agents-3.6.1.dist-info}/entry_points.txt +0 -0
- {tapps_agents-3.6.0.dist-info → tapps_agents-3.6.1.dist-info}/top_level.txt +0 -0
|
@@ -1,463 +1,463 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Parallel workflow step execution with bounded concurrency, timeouts, and cancellation.
|
|
3
|
-
|
|
4
|
-
Epic 1 / Story 1.3: Workflow Orchestration Agent Core
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import asyncio
|
|
10
|
-
import logging
|
|
11
|
-
from collections.abc import Callable
|
|
12
|
-
from dataclasses import dataclass
|
|
13
|
-
from datetime import datetime
|
|
14
|
-
from typing import Any, TypeVar
|
|
15
|
-
|
|
16
|
-
from .models import StepExecution, WorkflowState, WorkflowStep
|
|
17
|
-
|
|
18
|
-
logger = logging.getLogger(__name__)
|
|
19
|
-
|
|
20
|
-
T = TypeVar("T")
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@dataclass(frozen=True)
|
|
24
|
-
class RetryConfig:
|
|
25
|
-
"""Configuration for step retry behavior."""
|
|
26
|
-
|
|
27
|
-
max_attempts: int = 1
|
|
28
|
-
initial_backoff_seconds: float = 1.0
|
|
29
|
-
max_backoff_seconds: float = 60.0
|
|
30
|
-
backoff_multiplier: float = 2.0
|
|
31
|
-
retryable_errors: tuple[type[Exception], ...] = (Exception,)
|
|
32
|
-
non_retryable_errors: tuple[type[Exception], ...] = ()
|
|
33
|
-
|
|
34
|
-
def should_retry(self, attempt: int, error: Exception) -> bool:
|
|
35
|
-
"""
|
|
36
|
-
Determine if a step should be retried.
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
attempt: Current attempt number (1-indexed)
|
|
40
|
-
error: Exception that occurred
|
|
41
|
-
|
|
42
|
-
Returns:
|
|
43
|
-
True if step should be retried
|
|
44
|
-
"""
|
|
45
|
-
if attempt >= self.max_attempts:
|
|
46
|
-
return False
|
|
47
|
-
|
|
48
|
-
# Check if error is explicitly non-retryable
|
|
49
|
-
if self.non_retryable_errors:
|
|
50
|
-
if isinstance(error, self.non_retryable_errors):
|
|
51
|
-
return False
|
|
52
|
-
|
|
53
|
-
# Check if error is retryable
|
|
54
|
-
if self.retryable_errors:
|
|
55
|
-
return isinstance(error, self.retryable_errors)
|
|
56
|
-
|
|
57
|
-
# Default: retry all errors if max_attempts > 1
|
|
58
|
-
return self.max_attempts > 1
|
|
59
|
-
|
|
60
|
-
def get_backoff_seconds(self, attempt: int) -> float:
|
|
61
|
-
"""
|
|
62
|
-
Calculate backoff delay for a retry attempt.
|
|
63
|
-
|
|
64
|
-
Args:
|
|
65
|
-
attempt: Current attempt number (1-indexed)
|
|
66
|
-
|
|
67
|
-
Returns:
|
|
68
|
-
Backoff delay in seconds
|
|
69
|
-
"""
|
|
70
|
-
backoff = self.initial_backoff_seconds * (
|
|
71
|
-
self.backoff_multiplier ** (attempt - 1)
|
|
72
|
-
)
|
|
73
|
-
return min(backoff, self.max_backoff_seconds)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
@dataclass(frozen=True)
|
|
77
|
-
class StepExecutionResult:
|
|
78
|
-
"""Result of executing a single workflow step."""
|
|
79
|
-
|
|
80
|
-
step: WorkflowStep
|
|
81
|
-
step_execution: StepExecution
|
|
82
|
-
artifacts: dict[str, Any] | None = None
|
|
83
|
-
error: Exception | None = None
|
|
84
|
-
attempts: int = 1
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
# @note[2025-03-15]: Parallel execution uses dependency-based parallelism per ADR-004.
|
|
88
|
-
# Steps are automatically parallelized based on dependencies - no manual parallel_tasks
|
|
89
|
-
# configuration needed. See docs/architecture/decisions/ADR-004-yaml-first-workflows.md
|
|
90
|
-
|
|
91
|
-
class ParallelStepExecutor:
|
|
92
|
-
"""
|
|
93
|
-
Executes independent workflow steps in parallel with bounded concurrency.
|
|
94
|
-
|
|
95
|
-
Features:
|
|
96
|
-
- Bounded parallelism (max concurrent steps)
|
|
97
|
-
- Per-step timeouts
|
|
98
|
-
- Cancellation propagation
|
|
99
|
-
- Deterministic state updates (ordered by step.id)
|
|
100
|
-
"""
|
|
101
|
-
|
|
102
|
-
def __init__(
|
|
103
|
-
self,
|
|
104
|
-
max_parallel: int = 8,
|
|
105
|
-
default_timeout_seconds: float | None = 3600.0, # 1 hour default
|
|
106
|
-
default_retry_config: RetryConfig | None = None,
|
|
107
|
-
):
|
|
108
|
-
"""
|
|
109
|
-
Initialize parallel step executor.
|
|
110
|
-
|
|
111
|
-
Args:
|
|
112
|
-
max_parallel: Maximum number of steps to execute concurrently
|
|
113
|
-
default_timeout_seconds: Default timeout per step (None = no timeout)
|
|
114
|
-
default_retry_config: Default retry configuration (no retries if None)
|
|
115
|
-
"""
|
|
116
|
-
self.max_parallel = max_parallel
|
|
117
|
-
self.default_timeout_seconds = default_timeout_seconds
|
|
118
|
-
self.default_retry_config = default_retry_config or RetryConfig(max_attempts=1)
|
|
119
|
-
|
|
120
|
-
def find_ready_steps(
|
|
121
|
-
self,
|
|
122
|
-
workflow_steps: list[WorkflowStep],
|
|
123
|
-
completed_step_ids: set[str],
|
|
124
|
-
running_step_ids: set[str],
|
|
125
|
-
available_artifacts: set[str] | None = None,
|
|
126
|
-
) -> list[WorkflowStep]:
|
|
127
|
-
"""
|
|
128
|
-
Find steps that are ready to execute (dependencies met).
|
|
129
|
-
|
|
130
|
-
This method respects the workflow's sequential 'next:' chain to prevent
|
|
131
|
-
premature step execution. A step is only ready if:
|
|
132
|
-
1. It's the 'next:' step from a completed step, OR it's the first step
|
|
133
|
-
2. All its artifact dependencies are met
|
|
134
|
-
|
|
135
|
-
This ensures workflows follow the intended sequence (enhance→planning→
|
|
136
|
-
implementation→review→testing→complete) instead of jumping to the final
|
|
137
|
-
step prematurely.
|
|
138
|
-
|
|
139
|
-
Fix for: Workflow Auto-Continuation Issue (BUG - Workflow stops after 1 step)
|
|
140
|
-
Root cause: Previous logic only checked artifact dependencies, allowing
|
|
141
|
-
'complete' step to execute immediately after 'enhance' because it had
|
|
142
|
-
no 'requires' dependencies.
|
|
143
|
-
|
|
144
|
-
Args:
|
|
145
|
-
workflow_steps: All workflow steps
|
|
146
|
-
completed_step_ids: Set of completed step IDs
|
|
147
|
-
running_step_ids: Set of currently running step IDs
|
|
148
|
-
available_artifacts: Set of available artifact names (from state.artifacts)
|
|
149
|
-
|
|
150
|
-
Returns:
|
|
151
|
-
List of steps ready to execute
|
|
152
|
-
"""
|
|
153
|
-
ready: list[WorkflowStep] = []
|
|
154
|
-
artifacts = available_artifacts or set()
|
|
155
|
-
|
|
156
|
-
# Build set of next steps from completed steps' next: fields
|
|
157
|
-
next_step_ids: set[str] = set()
|
|
158
|
-
for step in workflow_steps:
|
|
159
|
-
if step.id in completed_step_ids and step.next:
|
|
160
|
-
next_step_ids.add(step.next)
|
|
161
|
-
|
|
162
|
-
# If no completed steps yet, first step is ready
|
|
163
|
-
if not completed_step_ids:
|
|
164
|
-
first_step = workflow_steps[0] if workflow_steps else None
|
|
165
|
-
if first_step and first_step.id not in running_step_ids:
|
|
166
|
-
# Still check artifact dependencies for first step
|
|
167
|
-
if first_step.requires:
|
|
168
|
-
all_met = all(req in artifacts for req in first_step.requires)
|
|
169
|
-
if all_met:
|
|
170
|
-
return [first_step]
|
|
171
|
-
else:
|
|
172
|
-
return [first_step]
|
|
173
|
-
return []
|
|
174
|
-
|
|
175
|
-
# Find steps that are in the next_step_ids set (sequential progression)
|
|
176
|
-
for step in workflow_steps:
|
|
177
|
-
if step.id in completed_step_ids or step.id in running_step_ids:
|
|
178
|
-
continue
|
|
179
|
-
|
|
180
|
-
# Must be in the next_step_ids set (respects sequential workflow chain)
|
|
181
|
-
if step.id not in next_step_ids:
|
|
182
|
-
continue
|
|
183
|
-
|
|
184
|
-
# Check if all required artifacts exist and are available
|
|
185
|
-
if step.requires:
|
|
186
|
-
all_met = all(req in artifacts for req in step.requires)
|
|
187
|
-
if not all_met:
|
|
188
|
-
continue
|
|
189
|
-
|
|
190
|
-
ready.append(step)
|
|
191
|
-
|
|
192
|
-
return ready
|
|
193
|
-
|
|
194
|
-
def _get_retry_config(self, step: WorkflowStep) -> RetryConfig:
|
|
195
|
-
"""
|
|
196
|
-
Get retry configuration for a step.
|
|
197
|
-
|
|
198
|
-
Args:
|
|
199
|
-
step: Workflow step
|
|
200
|
-
|
|
201
|
-
Returns:
|
|
202
|
-
Retry configuration (from step metadata or default)
|
|
203
|
-
"""
|
|
204
|
-
# Check step metadata for retry configuration
|
|
205
|
-
if step.metadata:
|
|
206
|
-
retry_meta = step.metadata.get("retry", {})
|
|
207
|
-
if retry_meta:
|
|
208
|
-
return RetryConfig(
|
|
209
|
-
max_attempts=retry_meta.get("max_attempts", 1),
|
|
210
|
-
initial_backoff_seconds=retry_meta.get("initial_backoff_seconds", 1.0),
|
|
211
|
-
max_backoff_seconds=retry_meta.get("max_backoff_seconds", 60.0),
|
|
212
|
-
backoff_multiplier=retry_meta.get("backoff_multiplier", 2.0),
|
|
213
|
-
)
|
|
214
|
-
return self.default_retry_config
|
|
215
|
-
|
|
216
|
-
async def execute_parallel(
|
|
217
|
-
self,
|
|
218
|
-
steps: list[WorkflowStep],
|
|
219
|
-
execute_fn: Callable[[WorkflowStep], Any],
|
|
220
|
-
*,
|
|
221
|
-
state: WorkflowState,
|
|
222
|
-
timeout_seconds: float | None = None,
|
|
223
|
-
) -> list[StepExecutionResult]:
|
|
224
|
-
"""
|
|
225
|
-
Execute multiple steps in parallel with bounded concurrency and retries.
|
|
226
|
-
|
|
227
|
-
Args:
|
|
228
|
-
steps: Steps to execute in parallel
|
|
229
|
-
execute_fn: Async function that executes a step and returns artifacts
|
|
230
|
-
state: Workflow state (for recording step executions)
|
|
231
|
-
timeout_seconds: Per-step timeout (uses default if None)
|
|
232
|
-
|
|
233
|
-
Returns:
|
|
234
|
-
List of execution results, ordered by step.id for determinism
|
|
235
|
-
"""
|
|
236
|
-
if not steps:
|
|
237
|
-
return []
|
|
238
|
-
|
|
239
|
-
timeout = timeout_seconds or self.default_timeout_seconds
|
|
240
|
-
semaphore = asyncio.Semaphore(self.max_parallel)
|
|
241
|
-
|
|
242
|
-
async def execute_with_retries(step: WorkflowStep) -> StepExecutionResult:
|
|
243
|
-
"""Execute a single step with retries, semaphore, and timeout."""
|
|
244
|
-
retry_config = self._get_retry_config(step)
|
|
245
|
-
step_execution = StepExecution(
|
|
246
|
-
step_id=step.id,
|
|
247
|
-
agent=step.agent or "",
|
|
248
|
-
action=step.action or "",
|
|
249
|
-
started_at=datetime.now(),
|
|
250
|
-
status="running",
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
# Record start in state (thread-safe: we're in async context)
|
|
254
|
-
state.step_executions.append(step_execution)
|
|
255
|
-
|
|
256
|
-
async with semaphore:
|
|
257
|
-
last_error: Exception | None = None
|
|
258
|
-
attempts = 0
|
|
259
|
-
|
|
260
|
-
while attempts < retry_config.max_attempts:
|
|
261
|
-
attempts += 1
|
|
262
|
-
try:
|
|
263
|
-
# Use asyncio.timeout (Python 3.11+) for better cancellation support
|
|
264
|
-
if timeout:
|
|
265
|
-
async with asyncio.timeout(timeout):
|
|
266
|
-
artifacts = await execute_fn(step)
|
|
267
|
-
else:
|
|
268
|
-
artifacts = await execute_fn(step)
|
|
269
|
-
|
|
270
|
-
step_execution.completed_at = datetime.now()
|
|
271
|
-
step_execution.duration_seconds = (
|
|
272
|
-
step_execution.completed_at - step_execution.started_at
|
|
273
|
-
).total_seconds()
|
|
274
|
-
step_execution.status = "completed"
|
|
275
|
-
|
|
276
|
-
return StepExecutionResult(
|
|
277
|
-
step=step,
|
|
278
|
-
step_execution=step_execution,
|
|
279
|
-
artifacts=artifacts,
|
|
280
|
-
attempts=attempts,
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
except TimeoutError as e:
|
|
284
|
-
last_error = e
|
|
285
|
-
# Use error envelope for structured error
|
|
286
|
-
from ..core.error_envelope import ErrorEnvelopeBuilder
|
|
287
|
-
envelope = ErrorEnvelopeBuilder.from_exception(
|
|
288
|
-
e,
|
|
289
|
-
workflow_id=state.workflow_id,
|
|
290
|
-
step_id=step.id,
|
|
291
|
-
agent=step.agent or "",
|
|
292
|
-
)
|
|
293
|
-
step_execution.error = envelope.to_user_message()
|
|
294
|
-
|
|
295
|
-
logger.warning(
|
|
296
|
-
f"Step {step.id} timed out after {timeout}s (attempt {attempts}/{retry_config.max_attempts})",
|
|
297
|
-
extra={
|
|
298
|
-
"workflow_id": state.workflow_id,
|
|
299
|
-
"step_id": step.id,
|
|
300
|
-
"agent": step.agent or "",
|
|
301
|
-
"action": step.action or "",
|
|
302
|
-
"timeout": timeout,
|
|
303
|
-
"attempt": attempts,
|
|
304
|
-
},
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
if not retry_config.should_retry(attempts, e):
|
|
308
|
-
break
|
|
309
|
-
|
|
310
|
-
# Wait before retry
|
|
311
|
-
backoff = retry_config.get_backoff_seconds(attempts)
|
|
312
|
-
await asyncio.sleep(backoff)
|
|
313
|
-
continue
|
|
314
|
-
|
|
315
|
-
except asyncio.CancelledError:
|
|
316
|
-
# Task was cancelled - don't retry, propagate cancellation
|
|
317
|
-
step_execution.status = "cancelled"
|
|
318
|
-
step_execution.error = "Step execution was cancelled"
|
|
319
|
-
step_execution.completed_at = datetime.now()
|
|
320
|
-
raise # Re-raise to propagate cancellation
|
|
321
|
-
|
|
322
|
-
except Exception as e:
|
|
323
|
-
last_error = e
|
|
324
|
-
# Use error envelope for structured error
|
|
325
|
-
from ..core.error_envelope import ErrorEnvelopeBuilder
|
|
326
|
-
envelope = ErrorEnvelopeBuilder.from_exception(
|
|
327
|
-
e,
|
|
328
|
-
workflow_id=state.workflow_id,
|
|
329
|
-
step_id=step.id,
|
|
330
|
-
agent=step.agent or "",
|
|
331
|
-
)
|
|
332
|
-
step_execution.error = envelope.to_user_message()
|
|
333
|
-
|
|
334
|
-
logger.error(
|
|
335
|
-
f"Step {step.id} failed: {e} (attempt {attempts}/{retry_config.max_attempts})",
|
|
336
|
-
exc_info=attempts == retry_config.max_attempts, # Only log full trace on final attempt
|
|
337
|
-
extra={
|
|
338
|
-
"workflow_id": state.workflow_id,
|
|
339
|
-
"step_id": step.id,
|
|
340
|
-
"agent": step.agent or "",
|
|
341
|
-
"action": step.action or "",
|
|
342
|
-
"attempt": attempts,
|
|
343
|
-
},
|
|
344
|
-
)
|
|
345
|
-
|
|
346
|
-
if not retry_config.should_retry(attempts, e):
|
|
347
|
-
break
|
|
348
|
-
|
|
349
|
-
# Wait before retry
|
|
350
|
-
backoff = retry_config.get_backoff_seconds(attempts)
|
|
351
|
-
logger.info(
|
|
352
|
-
f"Retrying step {step.id} after {backoff:.2f}s (attempt {attempts + 1}/{retry_config.max_attempts})"
|
|
353
|
-
)
|
|
354
|
-
await asyncio.sleep(backoff)
|
|
355
|
-
continue
|
|
356
|
-
|
|
357
|
-
# All retries exhausted
|
|
358
|
-
step_execution.completed_at = datetime.now()
|
|
359
|
-
step_execution.duration_seconds = (
|
|
360
|
-
step_execution.completed_at - step_execution.started_at
|
|
361
|
-
).total_seconds()
|
|
362
|
-
step_execution.status = "failed"
|
|
363
|
-
|
|
364
|
-
return StepExecutionResult(
|
|
365
|
-
step=step,
|
|
366
|
-
step_execution=step_execution,
|
|
367
|
-
error=last_error,
|
|
368
|
-
attempts=attempts,
|
|
369
|
-
)
|
|
370
|
-
|
|
371
|
-
# Execute all steps concurrently with bounded concurrency
|
|
372
|
-
# Use TaskGroup (Python 3.11+) for structured concurrency and automatic cancellation
|
|
373
|
-
results: list[StepExecutionResult] = []
|
|
374
|
-
tasks_map: dict[asyncio.Task[StepExecutionResult], WorkflowStep] = {}
|
|
375
|
-
|
|
376
|
-
try:
|
|
377
|
-
async with asyncio.TaskGroup() as tg:
|
|
378
|
-
# Create tasks for all steps
|
|
379
|
-
for step in steps:
|
|
380
|
-
task = tg.create_task(execute_with_retries(step))
|
|
381
|
-
tasks_map[task] = step
|
|
382
|
-
|
|
383
|
-
# All tasks completed successfully - collect results
|
|
384
|
-
for task in tasks_map:
|
|
385
|
-
result = await task
|
|
386
|
-
results.append(result)
|
|
387
|
-
|
|
388
|
-
except* Exception as eg:
|
|
389
|
-
# Handle ExceptionGroup (Python 3.11+)
|
|
390
|
-
# TaskGroup automatically cancels all tasks if any task raises an exception
|
|
391
|
-
logger.error(
|
|
392
|
-
f"Parallel execution encountered {len(eg.exceptions)} exceptions",
|
|
393
|
-
extra={
|
|
394
|
-
"workflow_id": state.workflow_id,
|
|
395
|
-
"exception_count": len(eg.exceptions),
|
|
396
|
-
},
|
|
397
|
-
)
|
|
398
|
-
|
|
399
|
-
# Collect results from completed tasks and handle cancelled/failed ones
|
|
400
|
-
for task, step in tasks_map.items():
|
|
401
|
-
if task.done():
|
|
402
|
-
try:
|
|
403
|
-
result = await task
|
|
404
|
-
results.append(result)
|
|
405
|
-
except Exception as e:
|
|
406
|
-
# Task completed but raised exception
|
|
407
|
-
logger.error(
|
|
408
|
-
f"Step {step.id} failed: {e}",
|
|
409
|
-
exc_info=e,
|
|
410
|
-
extra={
|
|
411
|
-
"workflow_id": state.workflow_id,
|
|
412
|
-
"step_id": step.id,
|
|
413
|
-
"agent": step.agent or "",
|
|
414
|
-
"action": step.action or "",
|
|
415
|
-
},
|
|
416
|
-
)
|
|
417
|
-
results.append(
|
|
418
|
-
StepExecutionResult(
|
|
419
|
-
step=step,
|
|
420
|
-
step_execution=StepExecution(
|
|
421
|
-
step_id=step.id,
|
|
422
|
-
agent=step.agent or "",
|
|
423
|
-
action=step.action or "",
|
|
424
|
-
started_at=datetime.now(),
|
|
425
|
-
completed_at=datetime.now(),
|
|
426
|
-
status="failed",
|
|
427
|
-
error=str(e),
|
|
428
|
-
),
|
|
429
|
-
error=e,
|
|
430
|
-
)
|
|
431
|
-
)
|
|
432
|
-
else:
|
|
433
|
-
# Task was cancelled by TaskGroup
|
|
434
|
-
logger.warning(
|
|
435
|
-
f"Step {step.id} was cancelled due to another step's failure",
|
|
436
|
-
extra={
|
|
437
|
-
"workflow_id": state.workflow_id,
|
|
438
|
-
"step_id": step.id,
|
|
439
|
-
},
|
|
440
|
-
)
|
|
441
|
-
results.append(
|
|
442
|
-
StepExecutionResult(
|
|
443
|
-
step=step,
|
|
444
|
-
step_execution=StepExecution(
|
|
445
|
-
step_id=step.id,
|
|
446
|
-
agent=step.agent or "",
|
|
447
|
-
action=step.action or "",
|
|
448
|
-
started_at=datetime.now(),
|
|
449
|
-
completed_at=datetime.now(),
|
|
450
|
-
status="cancelled",
|
|
451
|
-
error="Step execution was cancelled",
|
|
452
|
-
),
|
|
453
|
-
)
|
|
454
|
-
)
|
|
455
|
-
|
|
456
|
-
# Re-raise the first exception for upstream handling
|
|
457
|
-
if eg.exceptions:
|
|
458
|
-
raise eg.exceptions[0] from None
|
|
459
|
-
|
|
460
|
-
# Sort by step.id for deterministic ordering
|
|
461
|
-
results.sort(key=lambda r: r.step.id)
|
|
462
|
-
|
|
463
|
-
return results
|
|
1
|
+
"""
|
|
2
|
+
Parallel workflow step execution with bounded concurrency, timeouts, and cancellation.
|
|
3
|
+
|
|
4
|
+
Epic 1 / Story 1.3: Workflow Orchestration Agent Core
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import logging
|
|
11
|
+
from collections.abc import Callable
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from typing import Any, TypeVar
|
|
15
|
+
|
|
16
|
+
from .models import StepExecution, WorkflowState, WorkflowStep
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
T = TypeVar("T")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class RetryConfig:
|
|
25
|
+
"""Configuration for step retry behavior."""
|
|
26
|
+
|
|
27
|
+
max_attempts: int = 1
|
|
28
|
+
initial_backoff_seconds: float = 1.0
|
|
29
|
+
max_backoff_seconds: float = 60.0
|
|
30
|
+
backoff_multiplier: float = 2.0
|
|
31
|
+
retryable_errors: tuple[type[Exception], ...] = (Exception,)
|
|
32
|
+
non_retryable_errors: tuple[type[Exception], ...] = ()
|
|
33
|
+
|
|
34
|
+
def should_retry(self, attempt: int, error: Exception) -> bool:
|
|
35
|
+
"""
|
|
36
|
+
Determine if a step should be retried.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
attempt: Current attempt number (1-indexed)
|
|
40
|
+
error: Exception that occurred
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
True if step should be retried
|
|
44
|
+
"""
|
|
45
|
+
if attempt >= self.max_attempts:
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
# Check if error is explicitly non-retryable
|
|
49
|
+
if self.non_retryable_errors:
|
|
50
|
+
if isinstance(error, self.non_retryable_errors):
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
# Check if error is retryable
|
|
54
|
+
if self.retryable_errors:
|
|
55
|
+
return isinstance(error, self.retryable_errors)
|
|
56
|
+
|
|
57
|
+
# Default: retry all errors if max_attempts > 1
|
|
58
|
+
return self.max_attempts > 1
|
|
59
|
+
|
|
60
|
+
def get_backoff_seconds(self, attempt: int) -> float:
|
|
61
|
+
"""
|
|
62
|
+
Calculate backoff delay for a retry attempt.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
attempt: Current attempt number (1-indexed)
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Backoff delay in seconds
|
|
69
|
+
"""
|
|
70
|
+
backoff = self.initial_backoff_seconds * (
|
|
71
|
+
self.backoff_multiplier ** (attempt - 1)
|
|
72
|
+
)
|
|
73
|
+
return min(backoff, self.max_backoff_seconds)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass(frozen=True)
|
|
77
|
+
class StepExecutionResult:
|
|
78
|
+
"""Result of executing a single workflow step."""
|
|
79
|
+
|
|
80
|
+
step: WorkflowStep
|
|
81
|
+
step_execution: StepExecution
|
|
82
|
+
artifacts: dict[str, Any] | None = None
|
|
83
|
+
error: Exception | None = None
|
|
84
|
+
attempts: int = 1
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# @note[2025-03-15]: Parallel execution uses dependency-based parallelism per ADR-004.
|
|
88
|
+
# Steps are automatically parallelized based on dependencies - no manual parallel_tasks
|
|
89
|
+
# configuration needed. See docs/architecture/decisions/ADR-004-yaml-first-workflows.md
|
|
90
|
+
|
|
91
|
+
class ParallelStepExecutor:
|
|
92
|
+
"""
|
|
93
|
+
Executes independent workflow steps in parallel with bounded concurrency.
|
|
94
|
+
|
|
95
|
+
Features:
|
|
96
|
+
- Bounded parallelism (max concurrent steps)
|
|
97
|
+
- Per-step timeouts
|
|
98
|
+
- Cancellation propagation
|
|
99
|
+
- Deterministic state updates (ordered by step.id)
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def __init__(
|
|
103
|
+
self,
|
|
104
|
+
max_parallel: int = 8,
|
|
105
|
+
default_timeout_seconds: float | None = 3600.0, # 1 hour default
|
|
106
|
+
default_retry_config: RetryConfig | None = None,
|
|
107
|
+
):
|
|
108
|
+
"""
|
|
109
|
+
Initialize parallel step executor.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
max_parallel: Maximum number of steps to execute concurrently
|
|
113
|
+
default_timeout_seconds: Default timeout per step (None = no timeout)
|
|
114
|
+
default_retry_config: Default retry configuration (no retries if None)
|
|
115
|
+
"""
|
|
116
|
+
self.max_parallel = max_parallel
|
|
117
|
+
self.default_timeout_seconds = default_timeout_seconds
|
|
118
|
+
self.default_retry_config = default_retry_config or RetryConfig(max_attempts=1)
|
|
119
|
+
|
|
120
|
+
def find_ready_steps(
|
|
121
|
+
self,
|
|
122
|
+
workflow_steps: list[WorkflowStep],
|
|
123
|
+
completed_step_ids: set[str],
|
|
124
|
+
running_step_ids: set[str],
|
|
125
|
+
available_artifacts: set[str] | None = None,
|
|
126
|
+
) -> list[WorkflowStep]:
|
|
127
|
+
"""
|
|
128
|
+
Find steps that are ready to execute (dependencies met).
|
|
129
|
+
|
|
130
|
+
This method respects the workflow's sequential 'next:' chain to prevent
|
|
131
|
+
premature step execution. A step is only ready if:
|
|
132
|
+
1. It's the 'next:' step from a completed step, OR it's the first step
|
|
133
|
+
2. All its artifact dependencies are met
|
|
134
|
+
|
|
135
|
+
This ensures workflows follow the intended sequence (enhance→planning→
|
|
136
|
+
implementation→review→testing→complete) instead of jumping to the final
|
|
137
|
+
step prematurely.
|
|
138
|
+
|
|
139
|
+
Fix for: Workflow Auto-Continuation Issue (BUG - Workflow stops after 1 step)
|
|
140
|
+
Root cause: Previous logic only checked artifact dependencies, allowing
|
|
141
|
+
'complete' step to execute immediately after 'enhance' because it had
|
|
142
|
+
no 'requires' dependencies.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
workflow_steps: All workflow steps
|
|
146
|
+
completed_step_ids: Set of completed step IDs
|
|
147
|
+
running_step_ids: Set of currently running step IDs
|
|
148
|
+
available_artifacts: Set of available artifact names (from state.artifacts)
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
List of steps ready to execute
|
|
152
|
+
"""
|
|
153
|
+
ready: list[WorkflowStep] = []
|
|
154
|
+
artifacts = available_artifacts or set()
|
|
155
|
+
|
|
156
|
+
# Build set of next steps from completed steps' next: fields
|
|
157
|
+
next_step_ids: set[str] = set()
|
|
158
|
+
for step in workflow_steps:
|
|
159
|
+
if step.id in completed_step_ids and step.next:
|
|
160
|
+
next_step_ids.add(step.next)
|
|
161
|
+
|
|
162
|
+
# If no completed steps yet, first step is ready
|
|
163
|
+
if not completed_step_ids:
|
|
164
|
+
first_step = workflow_steps[0] if workflow_steps else None
|
|
165
|
+
if first_step and first_step.id not in running_step_ids:
|
|
166
|
+
# Still check artifact dependencies for first step
|
|
167
|
+
if first_step.requires:
|
|
168
|
+
all_met = all(req in artifacts for req in first_step.requires)
|
|
169
|
+
if all_met:
|
|
170
|
+
return [first_step]
|
|
171
|
+
else:
|
|
172
|
+
return [first_step]
|
|
173
|
+
return []
|
|
174
|
+
|
|
175
|
+
# Find steps that are in the next_step_ids set (sequential progression)
|
|
176
|
+
for step in workflow_steps:
|
|
177
|
+
if step.id in completed_step_ids or step.id in running_step_ids:
|
|
178
|
+
continue
|
|
179
|
+
|
|
180
|
+
# Must be in the next_step_ids set (respects sequential workflow chain)
|
|
181
|
+
if step.id not in next_step_ids:
|
|
182
|
+
continue
|
|
183
|
+
|
|
184
|
+
# Check if all required artifacts exist and are available
|
|
185
|
+
if step.requires:
|
|
186
|
+
all_met = all(req in artifacts for req in step.requires)
|
|
187
|
+
if not all_met:
|
|
188
|
+
continue
|
|
189
|
+
|
|
190
|
+
ready.append(step)
|
|
191
|
+
|
|
192
|
+
return ready
|
|
193
|
+
|
|
194
|
+
def _get_retry_config(self, step: WorkflowStep) -> RetryConfig:
|
|
195
|
+
"""
|
|
196
|
+
Get retry configuration for a step.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
step: Workflow step
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
Retry configuration (from step metadata or default)
|
|
203
|
+
"""
|
|
204
|
+
# Check step metadata for retry configuration
|
|
205
|
+
if step.metadata:
|
|
206
|
+
retry_meta = step.metadata.get("retry", {})
|
|
207
|
+
if retry_meta:
|
|
208
|
+
return RetryConfig(
|
|
209
|
+
max_attempts=retry_meta.get("max_attempts", 1),
|
|
210
|
+
initial_backoff_seconds=retry_meta.get("initial_backoff_seconds", 1.0),
|
|
211
|
+
max_backoff_seconds=retry_meta.get("max_backoff_seconds", 60.0),
|
|
212
|
+
backoff_multiplier=retry_meta.get("backoff_multiplier", 2.0),
|
|
213
|
+
)
|
|
214
|
+
return self.default_retry_config
|
|
215
|
+
|
|
216
|
+
async def execute_parallel(
|
|
217
|
+
self,
|
|
218
|
+
steps: list[WorkflowStep],
|
|
219
|
+
execute_fn: Callable[[WorkflowStep], Any],
|
|
220
|
+
*,
|
|
221
|
+
state: WorkflowState,
|
|
222
|
+
timeout_seconds: float | None = None,
|
|
223
|
+
) -> list[StepExecutionResult]:
|
|
224
|
+
"""
|
|
225
|
+
Execute multiple steps in parallel with bounded concurrency and retries.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
steps: Steps to execute in parallel
|
|
229
|
+
execute_fn: Async function that executes a step and returns artifacts
|
|
230
|
+
state: Workflow state (for recording step executions)
|
|
231
|
+
timeout_seconds: Per-step timeout (uses default if None)
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
List of execution results, ordered by step.id for determinism
|
|
235
|
+
"""
|
|
236
|
+
if not steps:
|
|
237
|
+
return []
|
|
238
|
+
|
|
239
|
+
timeout = timeout_seconds or self.default_timeout_seconds
|
|
240
|
+
semaphore = asyncio.Semaphore(self.max_parallel)
|
|
241
|
+
|
|
242
|
+
async def execute_with_retries(step: WorkflowStep) -> StepExecutionResult:
|
|
243
|
+
"""Execute a single step with retries, semaphore, and timeout."""
|
|
244
|
+
retry_config = self._get_retry_config(step)
|
|
245
|
+
step_execution = StepExecution(
|
|
246
|
+
step_id=step.id,
|
|
247
|
+
agent=step.agent or "",
|
|
248
|
+
action=step.action or "",
|
|
249
|
+
started_at=datetime.now(),
|
|
250
|
+
status="running",
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Record start in state (thread-safe: we're in async context)
|
|
254
|
+
state.step_executions.append(step_execution)
|
|
255
|
+
|
|
256
|
+
async with semaphore:
|
|
257
|
+
last_error: Exception | None = None
|
|
258
|
+
attempts = 0
|
|
259
|
+
|
|
260
|
+
while attempts < retry_config.max_attempts:
|
|
261
|
+
attempts += 1
|
|
262
|
+
try:
|
|
263
|
+
# Use asyncio.timeout (Python 3.11+) for better cancellation support
|
|
264
|
+
if timeout:
|
|
265
|
+
async with asyncio.timeout(timeout):
|
|
266
|
+
artifacts = await execute_fn(step)
|
|
267
|
+
else:
|
|
268
|
+
artifacts = await execute_fn(step)
|
|
269
|
+
|
|
270
|
+
step_execution.completed_at = datetime.now()
|
|
271
|
+
step_execution.duration_seconds = (
|
|
272
|
+
step_execution.completed_at - step_execution.started_at
|
|
273
|
+
).total_seconds()
|
|
274
|
+
step_execution.status = "completed"
|
|
275
|
+
|
|
276
|
+
return StepExecutionResult(
|
|
277
|
+
step=step,
|
|
278
|
+
step_execution=step_execution,
|
|
279
|
+
artifacts=artifacts,
|
|
280
|
+
attempts=attempts,
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
except TimeoutError as e:
|
|
284
|
+
last_error = e
|
|
285
|
+
# Use error envelope for structured error
|
|
286
|
+
from ..core.error_envelope import ErrorEnvelopeBuilder
|
|
287
|
+
envelope = ErrorEnvelopeBuilder.from_exception(
|
|
288
|
+
e,
|
|
289
|
+
workflow_id=state.workflow_id,
|
|
290
|
+
step_id=step.id,
|
|
291
|
+
agent=step.agent or "",
|
|
292
|
+
)
|
|
293
|
+
step_execution.error = envelope.to_user_message()
|
|
294
|
+
|
|
295
|
+
logger.warning(
|
|
296
|
+
f"Step {step.id} timed out after {timeout}s (attempt {attempts}/{retry_config.max_attempts})",
|
|
297
|
+
extra={
|
|
298
|
+
"workflow_id": state.workflow_id,
|
|
299
|
+
"step_id": step.id,
|
|
300
|
+
"agent": step.agent or "",
|
|
301
|
+
"action": step.action or "",
|
|
302
|
+
"timeout": timeout,
|
|
303
|
+
"attempt": attempts,
|
|
304
|
+
},
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
if not retry_config.should_retry(attempts, e):
|
|
308
|
+
break
|
|
309
|
+
|
|
310
|
+
# Wait before retry
|
|
311
|
+
backoff = retry_config.get_backoff_seconds(attempts)
|
|
312
|
+
await asyncio.sleep(backoff)
|
|
313
|
+
continue
|
|
314
|
+
|
|
315
|
+
except asyncio.CancelledError:
|
|
316
|
+
# Task was cancelled - don't retry, propagate cancellation
|
|
317
|
+
step_execution.status = "cancelled"
|
|
318
|
+
step_execution.error = "Step execution was cancelled"
|
|
319
|
+
step_execution.completed_at = datetime.now()
|
|
320
|
+
raise # Re-raise to propagate cancellation
|
|
321
|
+
|
|
322
|
+
except Exception as e:
|
|
323
|
+
last_error = e
|
|
324
|
+
# Use error envelope for structured error
|
|
325
|
+
from ..core.error_envelope import ErrorEnvelopeBuilder
|
|
326
|
+
envelope = ErrorEnvelopeBuilder.from_exception(
|
|
327
|
+
e,
|
|
328
|
+
workflow_id=state.workflow_id,
|
|
329
|
+
step_id=step.id,
|
|
330
|
+
agent=step.agent or "",
|
|
331
|
+
)
|
|
332
|
+
step_execution.error = envelope.to_user_message()
|
|
333
|
+
|
|
334
|
+
logger.error(
|
|
335
|
+
f"Step {step.id} failed: {e} (attempt {attempts}/{retry_config.max_attempts})",
|
|
336
|
+
exc_info=attempts == retry_config.max_attempts, # Only log full trace on final attempt
|
|
337
|
+
extra={
|
|
338
|
+
"workflow_id": state.workflow_id,
|
|
339
|
+
"step_id": step.id,
|
|
340
|
+
"agent": step.agent or "",
|
|
341
|
+
"action": step.action or "",
|
|
342
|
+
"attempt": attempts,
|
|
343
|
+
},
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
if not retry_config.should_retry(attempts, e):
|
|
347
|
+
break
|
|
348
|
+
|
|
349
|
+
# Wait before retry
|
|
350
|
+
backoff = retry_config.get_backoff_seconds(attempts)
|
|
351
|
+
logger.info(
|
|
352
|
+
f"Retrying step {step.id} after {backoff:.2f}s (attempt {attempts + 1}/{retry_config.max_attempts})"
|
|
353
|
+
)
|
|
354
|
+
await asyncio.sleep(backoff)
|
|
355
|
+
continue
|
|
356
|
+
|
|
357
|
+
# All retries exhausted
|
|
358
|
+
step_execution.completed_at = datetime.now()
|
|
359
|
+
step_execution.duration_seconds = (
|
|
360
|
+
step_execution.completed_at - step_execution.started_at
|
|
361
|
+
).total_seconds()
|
|
362
|
+
step_execution.status = "failed"
|
|
363
|
+
|
|
364
|
+
return StepExecutionResult(
|
|
365
|
+
step=step,
|
|
366
|
+
step_execution=step_execution,
|
|
367
|
+
error=last_error,
|
|
368
|
+
attempts=attempts,
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
# Execute all steps concurrently with bounded concurrency
|
|
372
|
+
# Use TaskGroup (Python 3.11+) for structured concurrency and automatic cancellation
|
|
373
|
+
results: list[StepExecutionResult] = []
|
|
374
|
+
tasks_map: dict[asyncio.Task[StepExecutionResult], WorkflowStep] = {}
|
|
375
|
+
|
|
376
|
+
try:
|
|
377
|
+
async with asyncio.TaskGroup() as tg:
|
|
378
|
+
# Create tasks for all steps
|
|
379
|
+
for step in steps:
|
|
380
|
+
task = tg.create_task(execute_with_retries(step))
|
|
381
|
+
tasks_map[task] = step
|
|
382
|
+
|
|
383
|
+
# All tasks completed successfully - collect results
|
|
384
|
+
for task in tasks_map:
|
|
385
|
+
result = await task
|
|
386
|
+
results.append(result)
|
|
387
|
+
|
|
388
|
+
except* Exception as eg:
|
|
389
|
+
# Handle ExceptionGroup (Python 3.11+)
|
|
390
|
+
# TaskGroup automatically cancels all tasks if any task raises an exception
|
|
391
|
+
logger.error(
|
|
392
|
+
f"Parallel execution encountered {len(eg.exceptions)} exceptions",
|
|
393
|
+
extra={
|
|
394
|
+
"workflow_id": state.workflow_id,
|
|
395
|
+
"exception_count": len(eg.exceptions),
|
|
396
|
+
},
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
# Collect results from completed tasks and handle cancelled/failed ones
|
|
400
|
+
for task, step in tasks_map.items():
|
|
401
|
+
if task.done():
|
|
402
|
+
try:
|
|
403
|
+
result = await task
|
|
404
|
+
results.append(result)
|
|
405
|
+
except Exception as e:
|
|
406
|
+
# Task completed but raised exception
|
|
407
|
+
logger.error(
|
|
408
|
+
f"Step {step.id} failed: {e}",
|
|
409
|
+
exc_info=e,
|
|
410
|
+
extra={
|
|
411
|
+
"workflow_id": state.workflow_id,
|
|
412
|
+
"step_id": step.id,
|
|
413
|
+
"agent": step.agent or "",
|
|
414
|
+
"action": step.action or "",
|
|
415
|
+
},
|
|
416
|
+
)
|
|
417
|
+
results.append(
|
|
418
|
+
StepExecutionResult(
|
|
419
|
+
step=step,
|
|
420
|
+
step_execution=StepExecution(
|
|
421
|
+
step_id=step.id,
|
|
422
|
+
agent=step.agent or "",
|
|
423
|
+
action=step.action or "",
|
|
424
|
+
started_at=datetime.now(),
|
|
425
|
+
completed_at=datetime.now(),
|
|
426
|
+
status="failed",
|
|
427
|
+
error=str(e),
|
|
428
|
+
),
|
|
429
|
+
error=e,
|
|
430
|
+
)
|
|
431
|
+
)
|
|
432
|
+
else:
|
|
433
|
+
# Task was cancelled by TaskGroup
|
|
434
|
+
logger.warning(
|
|
435
|
+
f"Step {step.id} was cancelled due to another step's failure",
|
|
436
|
+
extra={
|
|
437
|
+
"workflow_id": state.workflow_id,
|
|
438
|
+
"step_id": step.id,
|
|
439
|
+
},
|
|
440
|
+
)
|
|
441
|
+
results.append(
|
|
442
|
+
StepExecutionResult(
|
|
443
|
+
step=step,
|
|
444
|
+
step_execution=StepExecution(
|
|
445
|
+
step_id=step.id,
|
|
446
|
+
agent=step.agent or "",
|
|
447
|
+
action=step.action or "",
|
|
448
|
+
started_at=datetime.now(),
|
|
449
|
+
completed_at=datetime.now(),
|
|
450
|
+
status="cancelled",
|
|
451
|
+
error="Step execution was cancelled",
|
|
452
|
+
),
|
|
453
|
+
)
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
# Re-raise the first exception for upstream handling
|
|
457
|
+
if eg.exceptions:
|
|
458
|
+
raise eg.exceptions[0] from None
|
|
459
|
+
|
|
460
|
+
# Sort by step.id for deterministic ordering
|
|
461
|
+
results.sort(key=lambda r: r.step.id)
|
|
462
|
+
|
|
463
|
+
return results
|