tapps-agents 3.5.40__py3-none-any.whl → 3.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/scoring.py +1566 -1566
- tapps_agents/agents/reviewer/service_discovery.py +534 -534
- tapps_agents/agents/reviewer/tools/__init__.py +41 -41
- 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/health.py +665 -665
- 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 -219
- tapps_agents/cli/commands/tester.py +191 -191
- tapps_agents/cli/commands/top_level.py +3586 -3586
- 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/artifact_context_builder.py +293 -0
- 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/config.py +33 -0
- 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.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/orchestrator.py +271 -271
- 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/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/base.py +185 -185
- 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/build_orchestrator.py +2700 -2667
- 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/fix_orchestrator.py +723 -723
- 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_executor.py +2337 -2196
- 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/message_formatter.py +188 -188
- 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 -111
- 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.5.40.dist-info → tapps_agents-3.6.0.dist-info}/METADATA +672 -672
- tapps_agents-3.6.0.dist-info/RECORD +758 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/licenses/LICENSE +22 -22
- tapps_agents/health/checks/outcomes.backup_20260204_064058.py +0 -324
- tapps_agents/health/checks/outcomes.backup_20260204_064256.py +0 -324
- tapps_agents/health/checks/outcomes.backup_20260204_064600.py +0 -324
- tapps_agents-3.5.40.dist-info/RECORD +0 -760
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/WHEEL +0 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/entry_points.txt +0 -0
- {tapps_agents-3.5.40.dist-info → tapps_agents-3.6.0.dist-info}/top_level.txt +0 -0
|
@@ -1,586 +1,586 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Workflow Auto-Progression System.
|
|
3
|
-
|
|
4
|
-
Epic 10: Workflow Auto-Progression
|
|
5
|
-
Implements automatic step progression, gate evaluation, error handling, and progression visibility.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
import logging
|
|
11
|
-
from dataclasses import dataclass, field
|
|
12
|
-
from datetime import datetime
|
|
13
|
-
from enum import Enum
|
|
14
|
-
from typing import Any
|
|
15
|
-
|
|
16
|
-
from ..quality.quality_gates import QualityGate, QualityThresholds
|
|
17
|
-
from .models import StepExecution, WorkflowState, WorkflowStep
|
|
18
|
-
|
|
19
|
-
logger = logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class ProgressionAction(Enum):
|
|
23
|
-
"""Action to take when a step completes."""
|
|
24
|
-
|
|
25
|
-
CONTINUE = "continue" # Proceed to next step
|
|
26
|
-
SKIP = "skip" # Skip to next step
|
|
27
|
-
RETRY = "retry" # Retry current step
|
|
28
|
-
ABORT = "abort" # Abort workflow
|
|
29
|
-
PAUSE = "pause" # Pause workflow for manual intervention
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
@dataclass
|
|
33
|
-
class ProgressionDecision:
|
|
34
|
-
"""Decision about how to progress after a step completes."""
|
|
35
|
-
|
|
36
|
-
action: ProgressionAction
|
|
37
|
-
next_step_id: str | None = None
|
|
38
|
-
reason: str = ""
|
|
39
|
-
gate_passed: bool | None = None
|
|
40
|
-
retry_count: int = 0
|
|
41
|
-
metadata: dict[str, Any] = field(default_factory=dict)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@dataclass
|
|
45
|
-
class ProgressionHistory:
|
|
46
|
-
"""History of workflow progression decisions."""
|
|
47
|
-
|
|
48
|
-
step_id: str
|
|
49
|
-
timestamp: datetime
|
|
50
|
-
action: ProgressionAction
|
|
51
|
-
reason: str
|
|
52
|
-
gate_result: dict[str, Any] | None = None
|
|
53
|
-
metadata: dict[str, Any] = field(default_factory=dict)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class AutoProgressionManager:
|
|
57
|
-
"""
|
|
58
|
-
Manages automatic workflow step progression.
|
|
59
|
-
|
|
60
|
-
Features:
|
|
61
|
-
- Automatic step progression on completion
|
|
62
|
-
- Gate evaluation and routing
|
|
63
|
-
- Error handling with retry/skip/abort
|
|
64
|
-
- Progression history tracking
|
|
65
|
-
- Visibility and control
|
|
66
|
-
"""
|
|
67
|
-
|
|
68
|
-
def __init__(
|
|
69
|
-
self,
|
|
70
|
-
auto_progression_enabled: bool = True,
|
|
71
|
-
auto_retry_enabled: bool = True,
|
|
72
|
-
max_retries: int = 3,
|
|
73
|
-
retry_backoff_base: float = 2.0,
|
|
74
|
-
max_retry_backoff: float = 60.0,
|
|
75
|
-
):
|
|
76
|
-
"""
|
|
77
|
-
Initialize auto-progression manager.
|
|
78
|
-
|
|
79
|
-
Args:
|
|
80
|
-
auto_progression_enabled: Whether to enable automatic progression
|
|
81
|
-
auto_retry_enabled: Whether to enable automatic retries
|
|
82
|
-
max_retries: Maximum number of retry attempts
|
|
83
|
-
retry_backoff_base: Base for exponential backoff
|
|
84
|
-
max_retry_backoff: Maximum backoff delay in seconds
|
|
85
|
-
"""
|
|
86
|
-
self.auto_progression_enabled = auto_progression_enabled
|
|
87
|
-
self.auto_retry_enabled = auto_retry_enabled
|
|
88
|
-
self.max_retries = max_retries
|
|
89
|
-
self.retry_backoff_base = retry_backoff_base
|
|
90
|
-
self.max_retry_backoff = max_retry_backoff
|
|
91
|
-
self.progression_history: list[ProgressionHistory] = []
|
|
92
|
-
self.step_retry_counts: dict[str, int] = {}
|
|
93
|
-
|
|
94
|
-
def should_auto_progress(self) -> bool:
|
|
95
|
-
"""Check if auto-progression is enabled."""
|
|
96
|
-
return self.auto_progression_enabled
|
|
97
|
-
|
|
98
|
-
def record_progression(
|
|
99
|
-
self,
|
|
100
|
-
step_id: str,
|
|
101
|
-
action: ProgressionAction,
|
|
102
|
-
reason: str,
|
|
103
|
-
gate_result: dict[str, Any] | None = None,
|
|
104
|
-
metadata: dict[str, Any] | None = None,
|
|
105
|
-
) -> None:
|
|
106
|
-
"""
|
|
107
|
-
Record a progression decision in history.
|
|
108
|
-
|
|
109
|
-
Args:
|
|
110
|
-
step_id: Step ID that triggered the progression
|
|
111
|
-
action: Action taken
|
|
112
|
-
reason: Reason for the action
|
|
113
|
-
gate_result: Gate evaluation result if applicable
|
|
114
|
-
metadata: Additional metadata
|
|
115
|
-
"""
|
|
116
|
-
history = ProgressionHistory(
|
|
117
|
-
step_id=step_id,
|
|
118
|
-
timestamp=datetime.now(),
|
|
119
|
-
action=action,
|
|
120
|
-
reason=reason,
|
|
121
|
-
gate_result=gate_result,
|
|
122
|
-
metadata=metadata or {},
|
|
123
|
-
)
|
|
124
|
-
self.progression_history.append(history)
|
|
125
|
-
logger.info(
|
|
126
|
-
f"Progression recorded: step={step_id}, action={action.value}, reason={reason}"
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
def get_progression_history(
|
|
130
|
-
self, step_id: str | None = None
|
|
131
|
-
) -> list[ProgressionHistory]:
|
|
132
|
-
"""
|
|
133
|
-
Get progression history, optionally filtered by step ID.
|
|
134
|
-
|
|
135
|
-
Args:
|
|
136
|
-
step_id: Optional step ID to filter by
|
|
137
|
-
|
|
138
|
-
Returns:
|
|
139
|
-
List of progression history entries
|
|
140
|
-
"""
|
|
141
|
-
if step_id:
|
|
142
|
-
return [h for h in self.progression_history if h.step_id == step_id]
|
|
143
|
-
return self.progression_history.copy()
|
|
144
|
-
|
|
145
|
-
def evaluate_gate(
|
|
146
|
-
self,
|
|
147
|
-
step: WorkflowStep,
|
|
148
|
-
state: WorkflowState,
|
|
149
|
-
review_result: dict[str, Any] | None = None,
|
|
150
|
-
) -> ProgressionDecision:
|
|
151
|
-
"""
|
|
152
|
-
Evaluate a gate condition and determine progression.
|
|
153
|
-
|
|
154
|
-
Args:
|
|
155
|
-
step: Workflow step with gate configuration
|
|
156
|
-
state: Current workflow state
|
|
157
|
-
review_result: Review result from reviewer agent (if applicable)
|
|
158
|
-
|
|
159
|
-
Returns:
|
|
160
|
-
ProgressionDecision with gate evaluation result
|
|
161
|
-
"""
|
|
162
|
-
if not step.gate:
|
|
163
|
-
# No gate - proceed normally
|
|
164
|
-
return ProgressionDecision(
|
|
165
|
-
action=ProgressionAction.CONTINUE,
|
|
166
|
-
reason="No gate configured",
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
gate = step.gate
|
|
170
|
-
gate_result: dict[str, Any] = {}
|
|
171
|
-
|
|
172
|
-
# Check for pluggable gates first
|
|
173
|
-
from .gate_integration import GateIntegration
|
|
174
|
-
|
|
175
|
-
gate_integration = GateIntegration()
|
|
176
|
-
pluggable_results = gate_integration.evaluate_step_gates(
|
|
177
|
-
step, state, context={"review_result": review_result}
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
# If pluggable gates exist and failed, use those results
|
|
181
|
-
if pluggable_results.get("gate_results") and not pluggable_results.get("all_passed", True):
|
|
182
|
-
failures = pluggable_results.get("failures", [])
|
|
183
|
-
if failures:
|
|
184
|
-
# Block on critical/error failures
|
|
185
|
-
blocking_failures = [
|
|
186
|
-
f for f in failures
|
|
187
|
-
if f.get("severity") in ("error", "critical")
|
|
188
|
-
]
|
|
189
|
-
if blocking_failures:
|
|
190
|
-
on_fail = gate.get("on_fail") or gate.get("on-fail")
|
|
191
|
-
return ProgressionDecision(
|
|
192
|
-
action=ProgressionAction.CONTINUE,
|
|
193
|
-
next_step_id=on_fail if on_fail else None,
|
|
194
|
-
reason=f"Pluggable gate failed: {blocking_failures[0]['message']}",
|
|
195
|
-
gate_passed=False,
|
|
196
|
-
metadata={"gate_result": pluggable_results},
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
# Store pluggable gate results in state
|
|
200
|
-
state.variables["pluggable_gates_last"] = pluggable_results
|
|
201
|
-
|
|
202
|
-
# Evaluate quality gate if reviewer step
|
|
203
|
-
if step.agent == "reviewer" and review_result:
|
|
204
|
-
# Extract scoring thresholds from step configuration
|
|
205
|
-
scoring_config = step.metadata.get("scoring", {}) if step.metadata else {}
|
|
206
|
-
thresholds_config = scoring_config.get("thresholds", {}) # scoring_config is always a dict
|
|
207
|
-
|
|
208
|
-
# Create quality thresholds from step config or use defaults
|
|
209
|
-
thresholds = QualityThresholds.from_dict(thresholds_config)
|
|
210
|
-
|
|
211
|
-
# Extract scores from review result
|
|
212
|
-
scores = review_result.get("scores", {})
|
|
213
|
-
if not scores:
|
|
214
|
-
scoring = review_result.get("scoring", {})
|
|
215
|
-
if scoring:
|
|
216
|
-
scores = scoring.get("scores", {})
|
|
217
|
-
|
|
218
|
-
# Evaluate quality gate
|
|
219
|
-
quality_gate = QualityGate(thresholds=thresholds)
|
|
220
|
-
gate_eval_result = quality_gate.evaluate_from_review_result(
|
|
221
|
-
review_result, thresholds
|
|
222
|
-
)
|
|
223
|
-
|
|
224
|
-
passed = gate_eval_result.passed
|
|
225
|
-
gate_result = {
|
|
226
|
-
"passed": passed,
|
|
227
|
-
"step": step.id,
|
|
228
|
-
"scoring": scores,
|
|
229
|
-
"gate_result": gate_eval_result.to_dict(),
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
# Store in state for reference
|
|
233
|
-
state.variables["gate_last"] = gate_result
|
|
234
|
-
|
|
235
|
-
# Determine next step based on gate result
|
|
236
|
-
on_pass = gate.get("on_pass") or gate.get("on-pass")
|
|
237
|
-
on_fail = gate.get("on_fail") or gate.get("on-fail")
|
|
238
|
-
|
|
239
|
-
if passed:
|
|
240
|
-
if on_pass:
|
|
241
|
-
return ProgressionDecision(
|
|
242
|
-
action=ProgressionAction.CONTINUE,
|
|
243
|
-
next_step_id=on_pass,
|
|
244
|
-
reason=f"Gate passed, routing to {on_pass}",
|
|
245
|
-
gate_passed=True,
|
|
246
|
-
metadata={"gate_result": gate_result},
|
|
247
|
-
)
|
|
248
|
-
else:
|
|
249
|
-
return ProgressionDecision(
|
|
250
|
-
action=ProgressionAction.CONTINUE,
|
|
251
|
-
reason="Gate passed, continuing to next step",
|
|
252
|
-
gate_passed=True,
|
|
253
|
-
metadata={"gate_result": gate_result},
|
|
254
|
-
)
|
|
255
|
-
else:
|
|
256
|
-
if on_fail:
|
|
257
|
-
return ProgressionDecision(
|
|
258
|
-
action=ProgressionAction.CONTINUE,
|
|
259
|
-
next_step_id=on_fail,
|
|
260
|
-
reason=f"Gate failed, routing to {on_fail}",
|
|
261
|
-
gate_passed=False,
|
|
262
|
-
metadata={"gate_result": gate_result},
|
|
263
|
-
)
|
|
264
|
-
else:
|
|
265
|
-
# Gate failed but no on_fail route - retry or abort?
|
|
266
|
-
retry_on_fail = gate.get("retry_on_fail", False)
|
|
267
|
-
if retry_on_fail and self.auto_retry_enabled:
|
|
268
|
-
return ProgressionDecision(
|
|
269
|
-
action=ProgressionAction.RETRY,
|
|
270
|
-
reason="Gate failed, retrying step",
|
|
271
|
-
gate_passed=False,
|
|
272
|
-
metadata={"gate_result": gate_result},
|
|
273
|
-
)
|
|
274
|
-
else:
|
|
275
|
-
return ProgressionDecision(
|
|
276
|
-
action=ProgressionAction.ABORT,
|
|
277
|
-
reason="Gate failed and no recovery path configured",
|
|
278
|
-
gate_passed=False,
|
|
279
|
-
metadata={"gate_result": gate_result},
|
|
280
|
-
)
|
|
281
|
-
|
|
282
|
-
# For non-reviewer gates, evaluate condition if provided
|
|
283
|
-
condition = gate.get("condition")
|
|
284
|
-
if condition:
|
|
285
|
-
# Simple condition evaluation (can be extended)
|
|
286
|
-
# For now, check if condition references state variables
|
|
287
|
-
try:
|
|
288
|
-
# Evaluate condition as Python expression (safely)
|
|
289
|
-
# This is a simplified version - in production, use a proper expression evaluator
|
|
290
|
-
condition_result = self._evaluate_condition(condition, state)
|
|
291
|
-
passed = bool(condition_result)
|
|
292
|
-
|
|
293
|
-
gate_result = {
|
|
294
|
-
"passed": passed,
|
|
295
|
-
"step": step.id,
|
|
296
|
-
"condition": condition,
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
on_pass = gate.get("on_pass") or gate.get("on-pass")
|
|
300
|
-
on_fail = gate.get("on_fail") or gate.get("on-fail")
|
|
301
|
-
|
|
302
|
-
if passed:
|
|
303
|
-
next_step = on_pass if on_pass else None
|
|
304
|
-
return ProgressionDecision(
|
|
305
|
-
action=ProgressionAction.CONTINUE,
|
|
306
|
-
next_step_id=next_step,
|
|
307
|
-
reason=f"Gate condition passed: {condition}",
|
|
308
|
-
gate_passed=True,
|
|
309
|
-
metadata={"gate_result": gate_result},
|
|
310
|
-
)
|
|
311
|
-
else:
|
|
312
|
-
next_step = on_fail if on_fail else None
|
|
313
|
-
return ProgressionDecision(
|
|
314
|
-
action=ProgressionAction.CONTINUE,
|
|
315
|
-
next_step_id=next_step,
|
|
316
|
-
reason=f"Gate condition failed: {condition}",
|
|
317
|
-
gate_passed=False,
|
|
318
|
-
metadata={"gate_result": gate_result},
|
|
319
|
-
)
|
|
320
|
-
except Exception as e:
|
|
321
|
-
logger.warning(f"Error evaluating gate condition: {e}")
|
|
322
|
-
return ProgressionDecision(
|
|
323
|
-
action=ProgressionAction.CONTINUE,
|
|
324
|
-
reason=f"Gate evaluation error: {e}",
|
|
325
|
-
metadata={"error": str(e)},
|
|
326
|
-
)
|
|
327
|
-
|
|
328
|
-
# Default: continue
|
|
329
|
-
return ProgressionDecision(
|
|
330
|
-
action=ProgressionAction.CONTINUE,
|
|
331
|
-
reason="Gate evaluated, no specific routing",
|
|
332
|
-
metadata={"gate_result": gate_result},
|
|
333
|
-
)
|
|
334
|
-
|
|
335
|
-
def _evaluate_condition(self, condition: str, state: WorkflowState) -> Any:
|
|
336
|
-
"""
|
|
337
|
-
Evaluate a condition expression against workflow state.
|
|
338
|
-
|
|
339
|
-
This is a simplified evaluator. In production, use a proper expression evaluator
|
|
340
|
-
like `simpleeval` or `asteval` for safety.
|
|
341
|
-
|
|
342
|
-
Args:
|
|
343
|
-
condition: Condition expression string
|
|
344
|
-
state: Workflow state
|
|
345
|
-
|
|
346
|
-
Returns:
|
|
347
|
-
Evaluation result
|
|
348
|
-
"""
|
|
349
|
-
# Simple variable substitution
|
|
350
|
-
# Replace state.variables references
|
|
351
|
-
for key, value in state.variables.items():
|
|
352
|
-
condition = condition.replace(f"variables.{key}", str(value))
|
|
353
|
-
condition = condition.replace(f"state.{key}", str(value))
|
|
354
|
-
|
|
355
|
-
# Check for common patterns
|
|
356
|
-
if "gate_last.passed" in condition:
|
|
357
|
-
gate_last = state.variables.get("gate_last", {})
|
|
358
|
-
passed = gate_last.get("passed", False)
|
|
359
|
-
if "==" in condition and "true" in condition:
|
|
360
|
-
return passed
|
|
361
|
-
elif "==" in condition and "false" in condition:
|
|
362
|
-
return not passed
|
|
363
|
-
return passed
|
|
364
|
-
|
|
365
|
-
# For now, return True as default (can be enhanced)
|
|
366
|
-
return True
|
|
367
|
-
|
|
368
|
-
def handle_step_completion(
|
|
369
|
-
self,
|
|
370
|
-
step: WorkflowStep,
|
|
371
|
-
state: WorkflowState,
|
|
372
|
-
step_execution: StepExecution,
|
|
373
|
-
review_result: dict[str, Any] | None = None,
|
|
374
|
-
) -> ProgressionDecision:
|
|
375
|
-
"""
|
|
376
|
-
Handle step completion and determine next action.
|
|
377
|
-
|
|
378
|
-
Args:
|
|
379
|
-
step: Completed workflow step
|
|
380
|
-
state: Current workflow state
|
|
381
|
-
step_execution: Step execution record
|
|
382
|
-
review_result: Review result if this was a reviewer step
|
|
383
|
-
|
|
384
|
-
Returns:
|
|
385
|
-
ProgressionDecision with next action
|
|
386
|
-
"""
|
|
387
|
-
# Check if step failed
|
|
388
|
-
if step_execution.status == "failed":
|
|
389
|
-
return self._handle_step_failure(step, state, step_execution)
|
|
390
|
-
|
|
391
|
-
# Step completed successfully - evaluate gate if present
|
|
392
|
-
if step.gate:
|
|
393
|
-
decision = self.evaluate_gate(step, state, review_result)
|
|
394
|
-
self.record_progression(
|
|
395
|
-
step_id=step.id,
|
|
396
|
-
action=decision.action,
|
|
397
|
-
reason=decision.reason,
|
|
398
|
-
gate_result=decision.metadata.get("gate_result"),
|
|
399
|
-
metadata=decision.metadata,
|
|
400
|
-
)
|
|
401
|
-
return decision
|
|
402
|
-
|
|
403
|
-
# No gate - continue to next step
|
|
404
|
-
decision = ProgressionDecision(
|
|
405
|
-
action=ProgressionAction.CONTINUE,
|
|
406
|
-
reason="Step completed successfully",
|
|
407
|
-
)
|
|
408
|
-
self.record_progression(
|
|
409
|
-
step_id=step.id,
|
|
410
|
-
action=decision.action,
|
|
411
|
-
reason=decision.reason,
|
|
412
|
-
)
|
|
413
|
-
return decision
|
|
414
|
-
|
|
415
|
-
def _handle_step_failure(
|
|
416
|
-
self,
|
|
417
|
-
step: WorkflowStep,
|
|
418
|
-
state: WorkflowState,
|
|
419
|
-
step_execution: StepExecution,
|
|
420
|
-
) -> ProgressionDecision:
|
|
421
|
-
"""
|
|
422
|
-
Handle step failure and determine recovery action.
|
|
423
|
-
|
|
424
|
-
Args:
|
|
425
|
-
step: Failed workflow step
|
|
426
|
-
state: Current workflow state
|
|
427
|
-
step_execution: Step execution record with error
|
|
428
|
-
|
|
429
|
-
Returns:
|
|
430
|
-
ProgressionDecision with recovery action
|
|
431
|
-
"""
|
|
432
|
-
step_id = step.id
|
|
433
|
-
retry_count = self.step_retry_counts.get(step_id, 0)
|
|
434
|
-
|
|
435
|
-
# Check if we should retry
|
|
436
|
-
if self.auto_retry_enabled and retry_count < self.max_retries:
|
|
437
|
-
# Check step metadata for retry configuration
|
|
438
|
-
retry_config = step.metadata.get("retry", {})
|
|
439
|
-
if retry_config.get("enabled", True):
|
|
440
|
-
self.step_retry_counts[step_id] = retry_count + 1
|
|
441
|
-
backoff = min(
|
|
442
|
-
self.retry_backoff_base**retry_count,
|
|
443
|
-
self.max_retry_backoff,
|
|
444
|
-
)
|
|
445
|
-
|
|
446
|
-
decision = ProgressionDecision(
|
|
447
|
-
action=ProgressionAction.RETRY,
|
|
448
|
-
reason=f"Step failed, retrying (attempt {retry_count + 1}/{self.max_retries})",
|
|
449
|
-
retry_count=retry_count + 1,
|
|
450
|
-
metadata={
|
|
451
|
-
"error": step_execution.error,
|
|
452
|
-
"backoff_seconds": backoff,
|
|
453
|
-
},
|
|
454
|
-
)
|
|
455
|
-
self.record_progression(
|
|
456
|
-
step_id=step_id,
|
|
457
|
-
action=decision.action,
|
|
458
|
-
reason=decision.reason,
|
|
459
|
-
metadata=decision.metadata,
|
|
460
|
-
)
|
|
461
|
-
return decision
|
|
462
|
-
|
|
463
|
-
# Check if step should be skipped on failure
|
|
464
|
-
skip_on_fail = step.metadata.get("skip_on_fail", False)
|
|
465
|
-
if skip_on_fail:
|
|
466
|
-
decision = ProgressionDecision(
|
|
467
|
-
action=ProgressionAction.SKIP,
|
|
468
|
-
reason="Step failed but configured to skip on failure",
|
|
469
|
-
metadata={"error": step_execution.error},
|
|
470
|
-
)
|
|
471
|
-
self.record_progression(
|
|
472
|
-
step_id=step_id,
|
|
473
|
-
action=decision.action,
|
|
474
|
-
reason=decision.reason,
|
|
475
|
-
metadata=decision.metadata,
|
|
476
|
-
)
|
|
477
|
-
return decision
|
|
478
|
-
|
|
479
|
-
# Check if error is recoverable
|
|
480
|
-
error_recoverable = step.metadata.get("error_recoverable", False)
|
|
481
|
-
if error_recoverable:
|
|
482
|
-
# Try to continue despite error
|
|
483
|
-
decision = ProgressionDecision(
|
|
484
|
-
action=ProgressionAction.CONTINUE,
|
|
485
|
-
reason="Step failed but error is recoverable, continuing",
|
|
486
|
-
metadata={"error": step_execution.error},
|
|
487
|
-
)
|
|
488
|
-
self.record_progression(
|
|
489
|
-
step_id=step_id,
|
|
490
|
-
action=decision.action,
|
|
491
|
-
reason=decision.reason,
|
|
492
|
-
metadata=decision.metadata,
|
|
493
|
-
)
|
|
494
|
-
return decision
|
|
495
|
-
|
|
496
|
-
# Default: abort workflow
|
|
497
|
-
decision = ProgressionDecision(
|
|
498
|
-
action=ProgressionAction.ABORT,
|
|
499
|
-
reason=f"Step failed after {retry_count} retries: {step_execution.error}",
|
|
500
|
-
metadata={"error": step_execution.error, "retry_count": retry_count},
|
|
501
|
-
)
|
|
502
|
-
self.record_progression(
|
|
503
|
-
step_id=step_id,
|
|
504
|
-
action=decision.action,
|
|
505
|
-
reason=decision.reason,
|
|
506
|
-
metadata=decision.metadata,
|
|
507
|
-
)
|
|
508
|
-
return decision
|
|
509
|
-
|
|
510
|
-
def get_next_step_id(
|
|
511
|
-
self,
|
|
512
|
-
step: WorkflowStep,
|
|
513
|
-
decision: ProgressionDecision,
|
|
514
|
-
workflow_steps: list[WorkflowStep],
|
|
515
|
-
) -> str | None:
|
|
516
|
-
"""
|
|
517
|
-
Determine the next step ID based on progression decision.
|
|
518
|
-
|
|
519
|
-
Args:
|
|
520
|
-
step: Current step
|
|
521
|
-
decision: Progression decision
|
|
522
|
-
workflow_steps: All workflow steps
|
|
523
|
-
|
|
524
|
-
Returns:
|
|
525
|
-
Next step ID, or None if workflow should complete
|
|
526
|
-
"""
|
|
527
|
-
if decision.action == ProgressionAction.ABORT:
|
|
528
|
-
return None
|
|
529
|
-
|
|
530
|
-
if decision.action == ProgressionAction.RETRY:
|
|
531
|
-
return step.id # Retry same step
|
|
532
|
-
|
|
533
|
-
if decision.action == ProgressionAction.SKIP:
|
|
534
|
-
# Skip to next step
|
|
535
|
-
return step.next
|
|
536
|
-
|
|
537
|
-
if decision.next_step_id:
|
|
538
|
-
# Gate-based routing
|
|
539
|
-
return decision.next_step_id
|
|
540
|
-
|
|
541
|
-
# Default: use step.next
|
|
542
|
-
return step.next
|
|
543
|
-
|
|
544
|
-
def get_progression_status(
|
|
545
|
-
self, state: WorkflowState, workflow_steps: list[WorkflowStep]
|
|
546
|
-
) -> dict[str, Any]:
|
|
547
|
-
"""
|
|
548
|
-
Get current progression status and visibility information.
|
|
549
|
-
|
|
550
|
-
Args:
|
|
551
|
-
state: Current workflow state
|
|
552
|
-
workflow_steps: All workflow steps
|
|
553
|
-
|
|
554
|
-
Returns:
|
|
555
|
-
Dictionary with progression status information
|
|
556
|
-
"""
|
|
557
|
-
current_step = None
|
|
558
|
-
if state.current_step:
|
|
559
|
-
current_step = next(
|
|
560
|
-
(s for s in workflow_steps if s.id == state.current_step), None
|
|
561
|
-
)
|
|
562
|
-
|
|
563
|
-
next_step = None
|
|
564
|
-
if current_step and current_step.next:
|
|
565
|
-
next_step = next(
|
|
566
|
-
(s for s in workflow_steps if s.id == current_step.next), None
|
|
567
|
-
)
|
|
568
|
-
|
|
569
|
-
return {
|
|
570
|
-
"auto_progression_enabled": self.auto_progression_enabled,
|
|
571
|
-
"current_step": {
|
|
572
|
-
"id": state.current_step,
|
|
573
|
-
"agent": current_step.agent if current_step else None,
|
|
574
|
-
"action": current_step.action if current_step else None,
|
|
575
|
-
},
|
|
576
|
-
"next_step": {
|
|
577
|
-
"id": next_step.id if next_step else None,
|
|
578
|
-
"agent": next_step.agent if next_step else None,
|
|
579
|
-
"action": next_step.action if next_step else None,
|
|
580
|
-
},
|
|
581
|
-
"completed_steps": len(state.completed_steps),
|
|
582
|
-
"total_steps": len(workflow_steps),
|
|
583
|
-
"progression_history_count": len(self.progression_history),
|
|
584
|
-
"status": state.status,
|
|
585
|
-
}
|
|
586
|
-
|
|
1
|
+
"""
|
|
2
|
+
Workflow Auto-Progression System.
|
|
3
|
+
|
|
4
|
+
Epic 10: Workflow Auto-Progression
|
|
5
|
+
Implements automatic step progression, gate evaluation, error handling, and progression visibility.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from enum import Enum
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from ..quality.quality_gates import QualityGate, QualityThresholds
|
|
17
|
+
from .models import StepExecution, WorkflowState, WorkflowStep
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ProgressionAction(Enum):
|
|
23
|
+
"""Action to take when a step completes."""
|
|
24
|
+
|
|
25
|
+
CONTINUE = "continue" # Proceed to next step
|
|
26
|
+
SKIP = "skip" # Skip to next step
|
|
27
|
+
RETRY = "retry" # Retry current step
|
|
28
|
+
ABORT = "abort" # Abort workflow
|
|
29
|
+
PAUSE = "pause" # Pause workflow for manual intervention
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class ProgressionDecision:
|
|
34
|
+
"""Decision about how to progress after a step completes."""
|
|
35
|
+
|
|
36
|
+
action: ProgressionAction
|
|
37
|
+
next_step_id: str | None = None
|
|
38
|
+
reason: str = ""
|
|
39
|
+
gate_passed: bool | None = None
|
|
40
|
+
retry_count: int = 0
|
|
41
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class ProgressionHistory:
|
|
46
|
+
"""History of workflow progression decisions."""
|
|
47
|
+
|
|
48
|
+
step_id: str
|
|
49
|
+
timestamp: datetime
|
|
50
|
+
action: ProgressionAction
|
|
51
|
+
reason: str
|
|
52
|
+
gate_result: dict[str, Any] | None = None
|
|
53
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class AutoProgressionManager:
|
|
57
|
+
"""
|
|
58
|
+
Manages automatic workflow step progression.
|
|
59
|
+
|
|
60
|
+
Features:
|
|
61
|
+
- Automatic step progression on completion
|
|
62
|
+
- Gate evaluation and routing
|
|
63
|
+
- Error handling with retry/skip/abort
|
|
64
|
+
- Progression history tracking
|
|
65
|
+
- Visibility and control
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
auto_progression_enabled: bool = True,
|
|
71
|
+
auto_retry_enabled: bool = True,
|
|
72
|
+
max_retries: int = 3,
|
|
73
|
+
retry_backoff_base: float = 2.0,
|
|
74
|
+
max_retry_backoff: float = 60.0,
|
|
75
|
+
):
|
|
76
|
+
"""
|
|
77
|
+
Initialize auto-progression manager.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
auto_progression_enabled: Whether to enable automatic progression
|
|
81
|
+
auto_retry_enabled: Whether to enable automatic retries
|
|
82
|
+
max_retries: Maximum number of retry attempts
|
|
83
|
+
retry_backoff_base: Base for exponential backoff
|
|
84
|
+
max_retry_backoff: Maximum backoff delay in seconds
|
|
85
|
+
"""
|
|
86
|
+
self.auto_progression_enabled = auto_progression_enabled
|
|
87
|
+
self.auto_retry_enabled = auto_retry_enabled
|
|
88
|
+
self.max_retries = max_retries
|
|
89
|
+
self.retry_backoff_base = retry_backoff_base
|
|
90
|
+
self.max_retry_backoff = max_retry_backoff
|
|
91
|
+
self.progression_history: list[ProgressionHistory] = []
|
|
92
|
+
self.step_retry_counts: dict[str, int] = {}
|
|
93
|
+
|
|
94
|
+
def should_auto_progress(self) -> bool:
|
|
95
|
+
"""Check if auto-progression is enabled."""
|
|
96
|
+
return self.auto_progression_enabled
|
|
97
|
+
|
|
98
|
+
def record_progression(
|
|
99
|
+
self,
|
|
100
|
+
step_id: str,
|
|
101
|
+
action: ProgressionAction,
|
|
102
|
+
reason: str,
|
|
103
|
+
gate_result: dict[str, Any] | None = None,
|
|
104
|
+
metadata: dict[str, Any] | None = None,
|
|
105
|
+
) -> None:
|
|
106
|
+
"""
|
|
107
|
+
Record a progression decision in history.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
step_id: Step ID that triggered the progression
|
|
111
|
+
action: Action taken
|
|
112
|
+
reason: Reason for the action
|
|
113
|
+
gate_result: Gate evaluation result if applicable
|
|
114
|
+
metadata: Additional metadata
|
|
115
|
+
"""
|
|
116
|
+
history = ProgressionHistory(
|
|
117
|
+
step_id=step_id,
|
|
118
|
+
timestamp=datetime.now(),
|
|
119
|
+
action=action,
|
|
120
|
+
reason=reason,
|
|
121
|
+
gate_result=gate_result,
|
|
122
|
+
metadata=metadata or {},
|
|
123
|
+
)
|
|
124
|
+
self.progression_history.append(history)
|
|
125
|
+
logger.info(
|
|
126
|
+
f"Progression recorded: step={step_id}, action={action.value}, reason={reason}"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def get_progression_history(
|
|
130
|
+
self, step_id: str | None = None
|
|
131
|
+
) -> list[ProgressionHistory]:
|
|
132
|
+
"""
|
|
133
|
+
Get progression history, optionally filtered by step ID.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
step_id: Optional step ID to filter by
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
List of progression history entries
|
|
140
|
+
"""
|
|
141
|
+
if step_id:
|
|
142
|
+
return [h for h in self.progression_history if h.step_id == step_id]
|
|
143
|
+
return self.progression_history.copy()
|
|
144
|
+
|
|
145
|
+
def evaluate_gate(
|
|
146
|
+
self,
|
|
147
|
+
step: WorkflowStep,
|
|
148
|
+
state: WorkflowState,
|
|
149
|
+
review_result: dict[str, Any] | None = None,
|
|
150
|
+
) -> ProgressionDecision:
|
|
151
|
+
"""
|
|
152
|
+
Evaluate a gate condition and determine progression.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
step: Workflow step with gate configuration
|
|
156
|
+
state: Current workflow state
|
|
157
|
+
review_result: Review result from reviewer agent (if applicable)
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
ProgressionDecision with gate evaluation result
|
|
161
|
+
"""
|
|
162
|
+
if not step.gate:
|
|
163
|
+
# No gate - proceed normally
|
|
164
|
+
return ProgressionDecision(
|
|
165
|
+
action=ProgressionAction.CONTINUE,
|
|
166
|
+
reason="No gate configured",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
gate = step.gate
|
|
170
|
+
gate_result: dict[str, Any] = {}
|
|
171
|
+
|
|
172
|
+
# Check for pluggable gates first
|
|
173
|
+
from .gate_integration import GateIntegration
|
|
174
|
+
|
|
175
|
+
gate_integration = GateIntegration()
|
|
176
|
+
pluggable_results = gate_integration.evaluate_step_gates(
|
|
177
|
+
step, state, context={"review_result": review_result}
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# If pluggable gates exist and failed, use those results
|
|
181
|
+
if pluggable_results.get("gate_results") and not pluggable_results.get("all_passed", True):
|
|
182
|
+
failures = pluggable_results.get("failures", [])
|
|
183
|
+
if failures:
|
|
184
|
+
# Block on critical/error failures
|
|
185
|
+
blocking_failures = [
|
|
186
|
+
f for f in failures
|
|
187
|
+
if f.get("severity") in ("error", "critical")
|
|
188
|
+
]
|
|
189
|
+
if blocking_failures:
|
|
190
|
+
on_fail = gate.get("on_fail") or gate.get("on-fail")
|
|
191
|
+
return ProgressionDecision(
|
|
192
|
+
action=ProgressionAction.CONTINUE,
|
|
193
|
+
next_step_id=on_fail if on_fail else None,
|
|
194
|
+
reason=f"Pluggable gate failed: {blocking_failures[0]['message']}",
|
|
195
|
+
gate_passed=False,
|
|
196
|
+
metadata={"gate_result": pluggable_results},
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Store pluggable gate results in state
|
|
200
|
+
state.variables["pluggable_gates_last"] = pluggable_results
|
|
201
|
+
|
|
202
|
+
# Evaluate quality gate if reviewer step
|
|
203
|
+
if step.agent == "reviewer" and review_result:
|
|
204
|
+
# Extract scoring thresholds from step configuration
|
|
205
|
+
scoring_config = step.metadata.get("scoring", {}) if step.metadata else {}
|
|
206
|
+
thresholds_config = scoring_config.get("thresholds", {}) # scoring_config is always a dict
|
|
207
|
+
|
|
208
|
+
# Create quality thresholds from step config or use defaults
|
|
209
|
+
thresholds = QualityThresholds.from_dict(thresholds_config)
|
|
210
|
+
|
|
211
|
+
# Extract scores from review result
|
|
212
|
+
scores = review_result.get("scores", {})
|
|
213
|
+
if not scores:
|
|
214
|
+
scoring = review_result.get("scoring", {})
|
|
215
|
+
if scoring:
|
|
216
|
+
scores = scoring.get("scores", {})
|
|
217
|
+
|
|
218
|
+
# Evaluate quality gate
|
|
219
|
+
quality_gate = QualityGate(thresholds=thresholds)
|
|
220
|
+
gate_eval_result = quality_gate.evaluate_from_review_result(
|
|
221
|
+
review_result, thresholds
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
passed = gate_eval_result.passed
|
|
225
|
+
gate_result = {
|
|
226
|
+
"passed": passed,
|
|
227
|
+
"step": step.id,
|
|
228
|
+
"scoring": scores,
|
|
229
|
+
"gate_result": gate_eval_result.to_dict(),
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
# Store in state for reference
|
|
233
|
+
state.variables["gate_last"] = gate_result
|
|
234
|
+
|
|
235
|
+
# Determine next step based on gate result
|
|
236
|
+
on_pass = gate.get("on_pass") or gate.get("on-pass")
|
|
237
|
+
on_fail = gate.get("on_fail") or gate.get("on-fail")
|
|
238
|
+
|
|
239
|
+
if passed:
|
|
240
|
+
if on_pass:
|
|
241
|
+
return ProgressionDecision(
|
|
242
|
+
action=ProgressionAction.CONTINUE,
|
|
243
|
+
next_step_id=on_pass,
|
|
244
|
+
reason=f"Gate passed, routing to {on_pass}",
|
|
245
|
+
gate_passed=True,
|
|
246
|
+
metadata={"gate_result": gate_result},
|
|
247
|
+
)
|
|
248
|
+
else:
|
|
249
|
+
return ProgressionDecision(
|
|
250
|
+
action=ProgressionAction.CONTINUE,
|
|
251
|
+
reason="Gate passed, continuing to next step",
|
|
252
|
+
gate_passed=True,
|
|
253
|
+
metadata={"gate_result": gate_result},
|
|
254
|
+
)
|
|
255
|
+
else:
|
|
256
|
+
if on_fail:
|
|
257
|
+
return ProgressionDecision(
|
|
258
|
+
action=ProgressionAction.CONTINUE,
|
|
259
|
+
next_step_id=on_fail,
|
|
260
|
+
reason=f"Gate failed, routing to {on_fail}",
|
|
261
|
+
gate_passed=False,
|
|
262
|
+
metadata={"gate_result": gate_result},
|
|
263
|
+
)
|
|
264
|
+
else:
|
|
265
|
+
# Gate failed but no on_fail route - retry or abort?
|
|
266
|
+
retry_on_fail = gate.get("retry_on_fail", False)
|
|
267
|
+
if retry_on_fail and self.auto_retry_enabled:
|
|
268
|
+
return ProgressionDecision(
|
|
269
|
+
action=ProgressionAction.RETRY,
|
|
270
|
+
reason="Gate failed, retrying step",
|
|
271
|
+
gate_passed=False,
|
|
272
|
+
metadata={"gate_result": gate_result},
|
|
273
|
+
)
|
|
274
|
+
else:
|
|
275
|
+
return ProgressionDecision(
|
|
276
|
+
action=ProgressionAction.ABORT,
|
|
277
|
+
reason="Gate failed and no recovery path configured",
|
|
278
|
+
gate_passed=False,
|
|
279
|
+
metadata={"gate_result": gate_result},
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
# For non-reviewer gates, evaluate condition if provided
|
|
283
|
+
condition = gate.get("condition")
|
|
284
|
+
if condition:
|
|
285
|
+
# Simple condition evaluation (can be extended)
|
|
286
|
+
# For now, check if condition references state variables
|
|
287
|
+
try:
|
|
288
|
+
# Evaluate condition as Python expression (safely)
|
|
289
|
+
# This is a simplified version - in production, use a proper expression evaluator
|
|
290
|
+
condition_result = self._evaluate_condition(condition, state)
|
|
291
|
+
passed = bool(condition_result)
|
|
292
|
+
|
|
293
|
+
gate_result = {
|
|
294
|
+
"passed": passed,
|
|
295
|
+
"step": step.id,
|
|
296
|
+
"condition": condition,
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
on_pass = gate.get("on_pass") or gate.get("on-pass")
|
|
300
|
+
on_fail = gate.get("on_fail") or gate.get("on-fail")
|
|
301
|
+
|
|
302
|
+
if passed:
|
|
303
|
+
next_step = on_pass if on_pass else None
|
|
304
|
+
return ProgressionDecision(
|
|
305
|
+
action=ProgressionAction.CONTINUE,
|
|
306
|
+
next_step_id=next_step,
|
|
307
|
+
reason=f"Gate condition passed: {condition}",
|
|
308
|
+
gate_passed=True,
|
|
309
|
+
metadata={"gate_result": gate_result},
|
|
310
|
+
)
|
|
311
|
+
else:
|
|
312
|
+
next_step = on_fail if on_fail else None
|
|
313
|
+
return ProgressionDecision(
|
|
314
|
+
action=ProgressionAction.CONTINUE,
|
|
315
|
+
next_step_id=next_step,
|
|
316
|
+
reason=f"Gate condition failed: {condition}",
|
|
317
|
+
gate_passed=False,
|
|
318
|
+
metadata={"gate_result": gate_result},
|
|
319
|
+
)
|
|
320
|
+
except Exception as e:
|
|
321
|
+
logger.warning(f"Error evaluating gate condition: {e}")
|
|
322
|
+
return ProgressionDecision(
|
|
323
|
+
action=ProgressionAction.CONTINUE,
|
|
324
|
+
reason=f"Gate evaluation error: {e}",
|
|
325
|
+
metadata={"error": str(e)},
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Default: continue
|
|
329
|
+
return ProgressionDecision(
|
|
330
|
+
action=ProgressionAction.CONTINUE,
|
|
331
|
+
reason="Gate evaluated, no specific routing",
|
|
332
|
+
metadata={"gate_result": gate_result},
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
def _evaluate_condition(self, condition: str, state: WorkflowState) -> Any:
|
|
336
|
+
"""
|
|
337
|
+
Evaluate a condition expression against workflow state.
|
|
338
|
+
|
|
339
|
+
This is a simplified evaluator. In production, use a proper expression evaluator
|
|
340
|
+
like `simpleeval` or `asteval` for safety.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
condition: Condition expression string
|
|
344
|
+
state: Workflow state
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
Evaluation result
|
|
348
|
+
"""
|
|
349
|
+
# Simple variable substitution
|
|
350
|
+
# Replace state.variables references
|
|
351
|
+
for key, value in state.variables.items():
|
|
352
|
+
condition = condition.replace(f"variables.{key}", str(value))
|
|
353
|
+
condition = condition.replace(f"state.{key}", str(value))
|
|
354
|
+
|
|
355
|
+
# Check for common patterns
|
|
356
|
+
if "gate_last.passed" in condition:
|
|
357
|
+
gate_last = state.variables.get("gate_last", {})
|
|
358
|
+
passed = gate_last.get("passed", False)
|
|
359
|
+
if "==" in condition and "true" in condition:
|
|
360
|
+
return passed
|
|
361
|
+
elif "==" in condition and "false" in condition:
|
|
362
|
+
return not passed
|
|
363
|
+
return passed
|
|
364
|
+
|
|
365
|
+
# For now, return True as default (can be enhanced)
|
|
366
|
+
return True
|
|
367
|
+
|
|
368
|
+
def handle_step_completion(
|
|
369
|
+
self,
|
|
370
|
+
step: WorkflowStep,
|
|
371
|
+
state: WorkflowState,
|
|
372
|
+
step_execution: StepExecution,
|
|
373
|
+
review_result: dict[str, Any] | None = None,
|
|
374
|
+
) -> ProgressionDecision:
|
|
375
|
+
"""
|
|
376
|
+
Handle step completion and determine next action.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
step: Completed workflow step
|
|
380
|
+
state: Current workflow state
|
|
381
|
+
step_execution: Step execution record
|
|
382
|
+
review_result: Review result if this was a reviewer step
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
ProgressionDecision with next action
|
|
386
|
+
"""
|
|
387
|
+
# Check if step failed
|
|
388
|
+
if step_execution.status == "failed":
|
|
389
|
+
return self._handle_step_failure(step, state, step_execution)
|
|
390
|
+
|
|
391
|
+
# Step completed successfully - evaluate gate if present
|
|
392
|
+
if step.gate:
|
|
393
|
+
decision = self.evaluate_gate(step, state, review_result)
|
|
394
|
+
self.record_progression(
|
|
395
|
+
step_id=step.id,
|
|
396
|
+
action=decision.action,
|
|
397
|
+
reason=decision.reason,
|
|
398
|
+
gate_result=decision.metadata.get("gate_result"),
|
|
399
|
+
metadata=decision.metadata,
|
|
400
|
+
)
|
|
401
|
+
return decision
|
|
402
|
+
|
|
403
|
+
# No gate - continue to next step
|
|
404
|
+
decision = ProgressionDecision(
|
|
405
|
+
action=ProgressionAction.CONTINUE,
|
|
406
|
+
reason="Step completed successfully",
|
|
407
|
+
)
|
|
408
|
+
self.record_progression(
|
|
409
|
+
step_id=step.id,
|
|
410
|
+
action=decision.action,
|
|
411
|
+
reason=decision.reason,
|
|
412
|
+
)
|
|
413
|
+
return decision
|
|
414
|
+
|
|
415
|
+
def _handle_step_failure(
|
|
416
|
+
self,
|
|
417
|
+
step: WorkflowStep,
|
|
418
|
+
state: WorkflowState,
|
|
419
|
+
step_execution: StepExecution,
|
|
420
|
+
) -> ProgressionDecision:
|
|
421
|
+
"""
|
|
422
|
+
Handle step failure and determine recovery action.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
step: Failed workflow step
|
|
426
|
+
state: Current workflow state
|
|
427
|
+
step_execution: Step execution record with error
|
|
428
|
+
|
|
429
|
+
Returns:
|
|
430
|
+
ProgressionDecision with recovery action
|
|
431
|
+
"""
|
|
432
|
+
step_id = step.id
|
|
433
|
+
retry_count = self.step_retry_counts.get(step_id, 0)
|
|
434
|
+
|
|
435
|
+
# Check if we should retry
|
|
436
|
+
if self.auto_retry_enabled and retry_count < self.max_retries:
|
|
437
|
+
# Check step metadata for retry configuration
|
|
438
|
+
retry_config = step.metadata.get("retry", {})
|
|
439
|
+
if retry_config.get("enabled", True):
|
|
440
|
+
self.step_retry_counts[step_id] = retry_count + 1
|
|
441
|
+
backoff = min(
|
|
442
|
+
self.retry_backoff_base**retry_count,
|
|
443
|
+
self.max_retry_backoff,
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
decision = ProgressionDecision(
|
|
447
|
+
action=ProgressionAction.RETRY,
|
|
448
|
+
reason=f"Step failed, retrying (attempt {retry_count + 1}/{self.max_retries})",
|
|
449
|
+
retry_count=retry_count + 1,
|
|
450
|
+
metadata={
|
|
451
|
+
"error": step_execution.error,
|
|
452
|
+
"backoff_seconds": backoff,
|
|
453
|
+
},
|
|
454
|
+
)
|
|
455
|
+
self.record_progression(
|
|
456
|
+
step_id=step_id,
|
|
457
|
+
action=decision.action,
|
|
458
|
+
reason=decision.reason,
|
|
459
|
+
metadata=decision.metadata,
|
|
460
|
+
)
|
|
461
|
+
return decision
|
|
462
|
+
|
|
463
|
+
# Check if step should be skipped on failure
|
|
464
|
+
skip_on_fail = step.metadata.get("skip_on_fail", False)
|
|
465
|
+
if skip_on_fail:
|
|
466
|
+
decision = ProgressionDecision(
|
|
467
|
+
action=ProgressionAction.SKIP,
|
|
468
|
+
reason="Step failed but configured to skip on failure",
|
|
469
|
+
metadata={"error": step_execution.error},
|
|
470
|
+
)
|
|
471
|
+
self.record_progression(
|
|
472
|
+
step_id=step_id,
|
|
473
|
+
action=decision.action,
|
|
474
|
+
reason=decision.reason,
|
|
475
|
+
metadata=decision.metadata,
|
|
476
|
+
)
|
|
477
|
+
return decision
|
|
478
|
+
|
|
479
|
+
# Check if error is recoverable
|
|
480
|
+
error_recoverable = step.metadata.get("error_recoverable", False)
|
|
481
|
+
if error_recoverable:
|
|
482
|
+
# Try to continue despite error
|
|
483
|
+
decision = ProgressionDecision(
|
|
484
|
+
action=ProgressionAction.CONTINUE,
|
|
485
|
+
reason="Step failed but error is recoverable, continuing",
|
|
486
|
+
metadata={"error": step_execution.error},
|
|
487
|
+
)
|
|
488
|
+
self.record_progression(
|
|
489
|
+
step_id=step_id,
|
|
490
|
+
action=decision.action,
|
|
491
|
+
reason=decision.reason,
|
|
492
|
+
metadata=decision.metadata,
|
|
493
|
+
)
|
|
494
|
+
return decision
|
|
495
|
+
|
|
496
|
+
# Default: abort workflow
|
|
497
|
+
decision = ProgressionDecision(
|
|
498
|
+
action=ProgressionAction.ABORT,
|
|
499
|
+
reason=f"Step failed after {retry_count} retries: {step_execution.error}",
|
|
500
|
+
metadata={"error": step_execution.error, "retry_count": retry_count},
|
|
501
|
+
)
|
|
502
|
+
self.record_progression(
|
|
503
|
+
step_id=step_id,
|
|
504
|
+
action=decision.action,
|
|
505
|
+
reason=decision.reason,
|
|
506
|
+
metadata=decision.metadata,
|
|
507
|
+
)
|
|
508
|
+
return decision
|
|
509
|
+
|
|
510
|
+
def get_next_step_id(
|
|
511
|
+
self,
|
|
512
|
+
step: WorkflowStep,
|
|
513
|
+
decision: ProgressionDecision,
|
|
514
|
+
workflow_steps: list[WorkflowStep],
|
|
515
|
+
) -> str | None:
|
|
516
|
+
"""
|
|
517
|
+
Determine the next step ID based on progression decision.
|
|
518
|
+
|
|
519
|
+
Args:
|
|
520
|
+
step: Current step
|
|
521
|
+
decision: Progression decision
|
|
522
|
+
workflow_steps: All workflow steps
|
|
523
|
+
|
|
524
|
+
Returns:
|
|
525
|
+
Next step ID, or None if workflow should complete
|
|
526
|
+
"""
|
|
527
|
+
if decision.action == ProgressionAction.ABORT:
|
|
528
|
+
return None
|
|
529
|
+
|
|
530
|
+
if decision.action == ProgressionAction.RETRY:
|
|
531
|
+
return step.id # Retry same step
|
|
532
|
+
|
|
533
|
+
if decision.action == ProgressionAction.SKIP:
|
|
534
|
+
# Skip to next step
|
|
535
|
+
return step.next
|
|
536
|
+
|
|
537
|
+
if decision.next_step_id:
|
|
538
|
+
# Gate-based routing
|
|
539
|
+
return decision.next_step_id
|
|
540
|
+
|
|
541
|
+
# Default: use step.next
|
|
542
|
+
return step.next
|
|
543
|
+
|
|
544
|
+
def get_progression_status(
|
|
545
|
+
self, state: WorkflowState, workflow_steps: list[WorkflowStep]
|
|
546
|
+
) -> dict[str, Any]:
|
|
547
|
+
"""
|
|
548
|
+
Get current progression status and visibility information.
|
|
549
|
+
|
|
550
|
+
Args:
|
|
551
|
+
state: Current workflow state
|
|
552
|
+
workflow_steps: All workflow steps
|
|
553
|
+
|
|
554
|
+
Returns:
|
|
555
|
+
Dictionary with progression status information
|
|
556
|
+
"""
|
|
557
|
+
current_step = None
|
|
558
|
+
if state.current_step:
|
|
559
|
+
current_step = next(
|
|
560
|
+
(s for s in workflow_steps if s.id == state.current_step), None
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
next_step = None
|
|
564
|
+
if current_step and current_step.next:
|
|
565
|
+
next_step = next(
|
|
566
|
+
(s for s in workflow_steps if s.id == current_step.next), None
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
return {
|
|
570
|
+
"auto_progression_enabled": self.auto_progression_enabled,
|
|
571
|
+
"current_step": {
|
|
572
|
+
"id": state.current_step,
|
|
573
|
+
"agent": current_step.agent if current_step else None,
|
|
574
|
+
"action": current_step.action if current_step else None,
|
|
575
|
+
},
|
|
576
|
+
"next_step": {
|
|
577
|
+
"id": next_step.id if next_step else None,
|
|
578
|
+
"agent": next_step.agent if next_step else None,
|
|
579
|
+
"action": next_step.action if next_step else None,
|
|
580
|
+
},
|
|
581
|
+
"completed_steps": len(state.completed_steps),
|
|
582
|
+
"total_steps": len(workflow_steps),
|
|
583
|
+
"progression_history_count": len(self.progression_history),
|
|
584
|
+
"status": state.status,
|
|
585
|
+
}
|
|
586
|
+
|