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,126 +1,126 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Workflow Execution Plan Generator
|
|
3
|
-
|
|
4
|
-
Generates normalized execution plan JSON from workflow definitions.
|
|
5
|
-
Epic 6 / Story 6.7: Workflow Execution Plan Generation
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import json
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
from typing import Any
|
|
11
|
-
|
|
12
|
-
from .models import Workflow, WorkflowStep
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def generate_execution_plan(workflow: Workflow) -> dict[str, Any]:
|
|
16
|
-
"""
|
|
17
|
-
Generate normalized execution plan JSON from workflow definition.
|
|
18
|
-
|
|
19
|
-
Args:
|
|
20
|
-
workflow: Parsed workflow definition
|
|
21
|
-
|
|
22
|
-
Returns:
|
|
23
|
-
Normalized execution plan as dictionary
|
|
24
|
-
"""
|
|
25
|
-
# Build step graph with dependencies
|
|
26
|
-
step_graph: dict[str, dict[str, Any]] = {}
|
|
27
|
-
for step in workflow.steps:
|
|
28
|
-
step_graph[step.id] = {
|
|
29
|
-
"id": step.id,
|
|
30
|
-
"agent": step.agent,
|
|
31
|
-
"action": step.action,
|
|
32
|
-
"context_tier": step.context_tier,
|
|
33
|
-
"creates": step.creates,
|
|
34
|
-
"requires": step.requires,
|
|
35
|
-
"consults": step.consults,
|
|
36
|
-
"condition": step.condition,
|
|
37
|
-
"next": step.next,
|
|
38
|
-
"optional_steps": step.optional_steps,
|
|
39
|
-
"repeats": step.repeats,
|
|
40
|
-
"has_gate": step.gate is not None,
|
|
41
|
-
"gate": step.gate if step.gate else None,
|
|
42
|
-
"scoring": step.scoring if step.scoring else None,
|
|
43
|
-
"metadata": step.metadata,
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
# Build dependency graph (reverse: which steps depend on this step's artifacts)
|
|
47
|
-
dependency_graph: dict[str, list[str]] = {}
|
|
48
|
-
artifact_to_steps: dict[str, list[str]] = {}
|
|
49
|
-
|
|
50
|
-
for step in workflow.steps:
|
|
51
|
-
dependency_graph[step.id] = []
|
|
52
|
-
for artifact in step.creates:
|
|
53
|
-
if artifact not in artifact_to_steps:
|
|
54
|
-
artifact_to_steps[artifact] = []
|
|
55
|
-
artifact_to_steps[artifact].append(step.id)
|
|
56
|
-
|
|
57
|
-
# Build reverse dependencies (which steps wait for this step)
|
|
58
|
-
for step in workflow.steps:
|
|
59
|
-
for required_artifact in step.requires:
|
|
60
|
-
if required_artifact in artifact_to_steps:
|
|
61
|
-
for creating_step_id in artifact_to_steps[required_artifact]:
|
|
62
|
-
if step.id not in dependency_graph[creating_step_id]:
|
|
63
|
-
dependency_graph[creating_step_id].append(step.id)
|
|
64
|
-
|
|
65
|
-
# Find entry points (steps with no requirements)
|
|
66
|
-
entry_points = [
|
|
67
|
-
step.id for step in workflow.steps if not step.requires
|
|
68
|
-
]
|
|
69
|
-
|
|
70
|
-
# Find exit points (steps with no next and no dependents)
|
|
71
|
-
exit_points = []
|
|
72
|
-
for step in workflow.steps:
|
|
73
|
-
if not step.next:
|
|
74
|
-
# Check if any other step depends on this one
|
|
75
|
-
has_dependents = any(
|
|
76
|
-
step.id in deps for deps in dependency_graph.values()
|
|
77
|
-
)
|
|
78
|
-
if not has_dependents:
|
|
79
|
-
exit_points.append(step.id)
|
|
80
|
-
|
|
81
|
-
plan = {
|
|
82
|
-
"workflow_id": workflow.id,
|
|
83
|
-
"workflow_name": workflow.name,
|
|
84
|
-
"workflow_version": workflow.version,
|
|
85
|
-
"workflow_type": workflow.type.value,
|
|
86
|
-
"schema_version": "2.0",
|
|
87
|
-
"settings": {
|
|
88
|
-
"quality_gates": workflow.settings.quality_gates,
|
|
89
|
-
"code_scoring": workflow.settings.code_scoring,
|
|
90
|
-
"context_tier_default": workflow.settings.context_tier_default,
|
|
91
|
-
"auto_detect": workflow.settings.auto_detect,
|
|
92
|
-
},
|
|
93
|
-
"step_graph": step_graph,
|
|
94
|
-
"dependency_graph": dependency_graph,
|
|
95
|
-
"entry_points": entry_points,
|
|
96
|
-
"exit_points": exit_points,
|
|
97
|
-
"total_steps": len(workflow.steps),
|
|
98
|
-
"steps_with_gates": len([s for s in workflow.steps if s.gate]),
|
|
99
|
-
"steps_with_retry": len([s for s in workflow.steps if s.repeats]),
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return plan
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def save_execution_plan(
|
|
106
|
-
execution_plan: dict[str, Any], state_dir: Path, workflow_id: str
|
|
107
|
-
) -> Path:
|
|
108
|
-
"""
|
|
109
|
-
Save execution plan JSON to workflow state directory.
|
|
110
|
-
|
|
111
|
-
Args:
|
|
112
|
-
execution_plan: Execution plan dictionary
|
|
113
|
-
state_dir: Directory where workflow state is stored
|
|
114
|
-
workflow_id: Workflow ID
|
|
115
|
-
|
|
116
|
-
Returns:
|
|
117
|
-
Path to saved execution plan file
|
|
118
|
-
"""
|
|
119
|
-
state_dir.mkdir(parents=True, exist_ok=True)
|
|
120
|
-
plan_path = state_dir / f"{workflow_id}-execution-plan.json"
|
|
121
|
-
|
|
122
|
-
with open(plan_path, "w", encoding="utf-8") as f:
|
|
123
|
-
json.dump(execution_plan, f, indent=2)
|
|
124
|
-
|
|
125
|
-
return plan_path
|
|
126
|
-
|
|
1
|
+
"""
|
|
2
|
+
Workflow Execution Plan Generator
|
|
3
|
+
|
|
4
|
+
Generates normalized execution plan JSON from workflow definitions.
|
|
5
|
+
Epic 6 / Story 6.7: Workflow Execution Plan Generation
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from .models import Workflow, WorkflowStep
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def generate_execution_plan(workflow: Workflow) -> dict[str, Any]:
|
|
16
|
+
"""
|
|
17
|
+
Generate normalized execution plan JSON from workflow definition.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
workflow: Parsed workflow definition
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Normalized execution plan as dictionary
|
|
24
|
+
"""
|
|
25
|
+
# Build step graph with dependencies
|
|
26
|
+
step_graph: dict[str, dict[str, Any]] = {}
|
|
27
|
+
for step in workflow.steps:
|
|
28
|
+
step_graph[step.id] = {
|
|
29
|
+
"id": step.id,
|
|
30
|
+
"agent": step.agent,
|
|
31
|
+
"action": step.action,
|
|
32
|
+
"context_tier": step.context_tier,
|
|
33
|
+
"creates": step.creates,
|
|
34
|
+
"requires": step.requires,
|
|
35
|
+
"consults": step.consults,
|
|
36
|
+
"condition": step.condition,
|
|
37
|
+
"next": step.next,
|
|
38
|
+
"optional_steps": step.optional_steps,
|
|
39
|
+
"repeats": step.repeats,
|
|
40
|
+
"has_gate": step.gate is not None,
|
|
41
|
+
"gate": step.gate if step.gate else None,
|
|
42
|
+
"scoring": step.scoring if step.scoring else None,
|
|
43
|
+
"metadata": step.metadata,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Build dependency graph (reverse: which steps depend on this step's artifacts)
|
|
47
|
+
dependency_graph: dict[str, list[str]] = {}
|
|
48
|
+
artifact_to_steps: dict[str, list[str]] = {}
|
|
49
|
+
|
|
50
|
+
for step in workflow.steps:
|
|
51
|
+
dependency_graph[step.id] = []
|
|
52
|
+
for artifact in step.creates:
|
|
53
|
+
if artifact not in artifact_to_steps:
|
|
54
|
+
artifact_to_steps[artifact] = []
|
|
55
|
+
artifact_to_steps[artifact].append(step.id)
|
|
56
|
+
|
|
57
|
+
# Build reverse dependencies (which steps wait for this step)
|
|
58
|
+
for step in workflow.steps:
|
|
59
|
+
for required_artifact in step.requires:
|
|
60
|
+
if required_artifact in artifact_to_steps:
|
|
61
|
+
for creating_step_id in artifact_to_steps[required_artifact]:
|
|
62
|
+
if step.id not in dependency_graph[creating_step_id]:
|
|
63
|
+
dependency_graph[creating_step_id].append(step.id)
|
|
64
|
+
|
|
65
|
+
# Find entry points (steps with no requirements)
|
|
66
|
+
entry_points = [
|
|
67
|
+
step.id for step in workflow.steps if not step.requires
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
# Find exit points (steps with no next and no dependents)
|
|
71
|
+
exit_points = []
|
|
72
|
+
for step in workflow.steps:
|
|
73
|
+
if not step.next:
|
|
74
|
+
# Check if any other step depends on this one
|
|
75
|
+
has_dependents = any(
|
|
76
|
+
step.id in deps for deps in dependency_graph.values()
|
|
77
|
+
)
|
|
78
|
+
if not has_dependents:
|
|
79
|
+
exit_points.append(step.id)
|
|
80
|
+
|
|
81
|
+
plan = {
|
|
82
|
+
"workflow_id": workflow.id,
|
|
83
|
+
"workflow_name": workflow.name,
|
|
84
|
+
"workflow_version": workflow.version,
|
|
85
|
+
"workflow_type": workflow.type.value,
|
|
86
|
+
"schema_version": "2.0",
|
|
87
|
+
"settings": {
|
|
88
|
+
"quality_gates": workflow.settings.quality_gates,
|
|
89
|
+
"code_scoring": workflow.settings.code_scoring,
|
|
90
|
+
"context_tier_default": workflow.settings.context_tier_default,
|
|
91
|
+
"auto_detect": workflow.settings.auto_detect,
|
|
92
|
+
},
|
|
93
|
+
"step_graph": step_graph,
|
|
94
|
+
"dependency_graph": dependency_graph,
|
|
95
|
+
"entry_points": entry_points,
|
|
96
|
+
"exit_points": exit_points,
|
|
97
|
+
"total_steps": len(workflow.steps),
|
|
98
|
+
"steps_with_gates": len([s for s in workflow.steps if s.gate]),
|
|
99
|
+
"steps_with_retry": len([s for s in workflow.steps if s.repeats]),
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return plan
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def save_execution_plan(
|
|
106
|
+
execution_plan: dict[str, Any], state_dir: Path, workflow_id: str
|
|
107
|
+
) -> Path:
|
|
108
|
+
"""
|
|
109
|
+
Save execution plan JSON to workflow state directory.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
execution_plan: Execution plan dictionary
|
|
113
|
+
state_dir: Directory where workflow state is stored
|
|
114
|
+
workflow_id: Workflow ID
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Path to saved execution plan file
|
|
118
|
+
"""
|
|
119
|
+
state_dir.mkdir(parents=True, exist_ok=True)
|
|
120
|
+
plan_path = state_dir / f"{workflow_id}-execution-plan.json"
|
|
121
|
+
|
|
122
|
+
with open(plan_path, "w", encoding="utf-8") as f:
|
|
123
|
+
json.dump(execution_plan, f, indent=2)
|
|
124
|
+
|
|
125
|
+
return plan_path
|
|
126
|
+
|
|
@@ -1,186 +1,186 @@
|
|
|
1
|
-
"""
|
|
2
|
-
File utilities for atomic writing and safe reading of JSON files.
|
|
3
|
-
|
|
4
|
-
Provides utilities to prevent race conditions when reading/writing state files.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import json
|
|
8
|
-
import gzip
|
|
9
|
-
import time
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
from typing import Any
|
|
12
|
-
import logging
|
|
13
|
-
|
|
14
|
-
logger = logging.getLogger(__name__)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def atomic_write_json(
|
|
18
|
-
path: Path,
|
|
19
|
-
data: dict[str, Any],
|
|
20
|
-
compress: bool = False,
|
|
21
|
-
indent: int = 2,
|
|
22
|
-
**kwargs,
|
|
23
|
-
) -> None:
|
|
24
|
-
"""
|
|
25
|
-
Atomically write JSON data to a file using temp-then-rename pattern.
|
|
26
|
-
|
|
27
|
-
This ensures the file is only visible when fully written, preventing
|
|
28
|
-
race conditions where readers see incomplete files.
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
path: Target file path
|
|
32
|
-
data: Data to write as JSON
|
|
33
|
-
compress: Whether to compress with gzip
|
|
34
|
-
indent: JSON indentation level
|
|
35
|
-
**kwargs: Additional arguments for json.dump()
|
|
36
|
-
"""
|
|
37
|
-
path = Path(path)
|
|
38
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
39
|
-
|
|
40
|
-
# Write to temp file first
|
|
41
|
-
temp_path = path.with_suffix(path.suffix + ".tmp")
|
|
42
|
-
|
|
43
|
-
try:
|
|
44
|
-
if compress:
|
|
45
|
-
with gzip.open(temp_path, "wt", encoding="utf-8") as f:
|
|
46
|
-
json.dump(data, f, indent=indent, **kwargs)
|
|
47
|
-
else:
|
|
48
|
-
with open(temp_path, "w", encoding="utf-8") as f:
|
|
49
|
-
json.dump(data, f, indent=indent, **kwargs)
|
|
50
|
-
|
|
51
|
-
# Atomic rename (works on most filesystems)
|
|
52
|
-
temp_path.replace(path)
|
|
53
|
-
except Exception:
|
|
54
|
-
# Clean up temp file on error
|
|
55
|
-
if temp_path.exists():
|
|
56
|
-
try:
|
|
57
|
-
temp_path.unlink()
|
|
58
|
-
except Exception:
|
|
59
|
-
pass
|
|
60
|
-
raise
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def is_valid_json_file(path: Path, min_size: int = 100) -> bool:
|
|
64
|
-
"""
|
|
65
|
-
Check if a file contains valid JSON and meets minimum size.
|
|
66
|
-
|
|
67
|
-
Args:
|
|
68
|
-
path: File path to check
|
|
69
|
-
min_size: Minimum file size in bytes (default: 100)
|
|
70
|
-
|
|
71
|
-
Returns:
|
|
72
|
-
True if file is valid JSON and meets size requirement
|
|
73
|
-
"""
|
|
74
|
-
if not path.exists():
|
|
75
|
-
return False
|
|
76
|
-
|
|
77
|
-
# Check file size
|
|
78
|
-
try:
|
|
79
|
-
if path.stat().st_size < min_size:
|
|
80
|
-
return False
|
|
81
|
-
except OSError:
|
|
82
|
-
return False
|
|
83
|
-
|
|
84
|
-
# Try to parse JSON
|
|
85
|
-
try:
|
|
86
|
-
if path.suffix == ".gz" or path.name.endswith(".gz"):
|
|
87
|
-
with gzip.open(path, "rt", encoding="utf-8") as f:
|
|
88
|
-
json.load(f)
|
|
89
|
-
else:
|
|
90
|
-
with open(path, encoding="utf-8") as f:
|
|
91
|
-
json.load(f)
|
|
92
|
-
return True
|
|
93
|
-
except (json.JSONDecodeError, OSError, ValueError):
|
|
94
|
-
return False
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def is_file_stable(path: Path, min_age_seconds: float = 2.0) -> bool:
|
|
98
|
-
"""
|
|
99
|
-
Check if a file has been stable (not modified recently).
|
|
100
|
-
|
|
101
|
-
This helps avoid reading files that are still being written.
|
|
102
|
-
|
|
103
|
-
Args:
|
|
104
|
-
path: File path to check
|
|
105
|
-
min_age_seconds: Minimum age in seconds (default: 2.0)
|
|
106
|
-
|
|
107
|
-
Returns:
|
|
108
|
-
True if file exists and hasn't been modified recently
|
|
109
|
-
"""
|
|
110
|
-
if not path.exists():
|
|
111
|
-
return False
|
|
112
|
-
|
|
113
|
-
try:
|
|
114
|
-
mtime = path.stat().st_mtime
|
|
115
|
-
age = time.time() - mtime
|
|
116
|
-
return age >= min_age_seconds
|
|
117
|
-
except OSError:
|
|
118
|
-
return False
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
def safe_load_json(
|
|
122
|
-
path: Path,
|
|
123
|
-
retries: int = 3,
|
|
124
|
-
backoff: float = 0.5,
|
|
125
|
-
min_age_seconds: float = 2.0,
|
|
126
|
-
min_size: int = 100,
|
|
127
|
-
) -> dict[str, Any] | None:
|
|
128
|
-
"""
|
|
129
|
-
Safely load JSON from a file with retry logic and validation.
|
|
130
|
-
|
|
131
|
-
Args:
|
|
132
|
-
path: File path to load
|
|
133
|
-
retries: Number of retry attempts (default: 3)
|
|
134
|
-
backoff: Backoff multiplier between retries (default: 0.5)
|
|
135
|
-
min_age_seconds: Minimum file age before reading (default: 2.0)
|
|
136
|
-
min_size: Minimum file size in bytes (default: 100)
|
|
137
|
-
|
|
138
|
-
Returns:
|
|
139
|
-
Parsed JSON data or None if loading fails
|
|
140
|
-
"""
|
|
141
|
-
if not path.exists():
|
|
142
|
-
return None
|
|
143
|
-
|
|
144
|
-
# Check file size first
|
|
145
|
-
try:
|
|
146
|
-
if path.stat().st_size < min_size:
|
|
147
|
-
logger.debug(f"File {path} too small, likely incomplete")
|
|
148
|
-
return None
|
|
149
|
-
except OSError:
|
|
150
|
-
return None
|
|
151
|
-
|
|
152
|
-
# Try loading with retries
|
|
153
|
-
for attempt in range(retries):
|
|
154
|
-
# Wait for file to be stable (except on first attempt if file is already old)
|
|
155
|
-
if attempt > 0 or not is_file_stable(path, min_age_seconds):
|
|
156
|
-
wait_time = backoff * (2 ** attempt)
|
|
157
|
-
if attempt > 0:
|
|
158
|
-
time.sleep(wait_time)
|
|
159
|
-
elif not is_file_stable(path, min_age_seconds):
|
|
160
|
-
# First attempt but file is too new, wait a bit
|
|
161
|
-
time.sleep(min_age_seconds)
|
|
162
|
-
|
|
163
|
-
# Validate JSON before attempting to load
|
|
164
|
-
if not is_valid_json_file(path, min_size):
|
|
165
|
-
if attempt < retries - 1:
|
|
166
|
-
continue # Retry
|
|
167
|
-
logger.debug(f"File {path} is not valid JSON after {attempt + 1} attempts")
|
|
168
|
-
return None
|
|
169
|
-
|
|
170
|
-
# Try to load
|
|
171
|
-
try:
|
|
172
|
-
if path.suffix == ".gz" or path.name.endswith(".gz"):
|
|
173
|
-
with gzip.open(path, "rt", encoding="utf-8") as f:
|
|
174
|
-
return json.load(f)
|
|
175
|
-
else:
|
|
176
|
-
with open(path, encoding="utf-8") as f:
|
|
177
|
-
return json.load(f)
|
|
178
|
-
except (json.JSONDecodeError, OSError, ValueError) as e:
|
|
179
|
-
if attempt < retries - 1:
|
|
180
|
-
logger.debug(f"Failed to load {path} (attempt {attempt + 1}/{retries}): {e}")
|
|
181
|
-
continue # Retry
|
|
182
|
-
logger.debug(f"Failed to load {path} after {retries} attempts: {e}")
|
|
183
|
-
return None
|
|
184
|
-
|
|
185
|
-
return None
|
|
186
|
-
|
|
1
|
+
"""
|
|
2
|
+
File utilities for atomic writing and safe reading of JSON files.
|
|
3
|
+
|
|
4
|
+
Provides utilities to prevent race conditions when reading/writing state files.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import gzip
|
|
9
|
+
import time
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def atomic_write_json(
|
|
18
|
+
path: Path,
|
|
19
|
+
data: dict[str, Any],
|
|
20
|
+
compress: bool = False,
|
|
21
|
+
indent: int = 2,
|
|
22
|
+
**kwargs,
|
|
23
|
+
) -> None:
|
|
24
|
+
"""
|
|
25
|
+
Atomically write JSON data to a file using temp-then-rename pattern.
|
|
26
|
+
|
|
27
|
+
This ensures the file is only visible when fully written, preventing
|
|
28
|
+
race conditions where readers see incomplete files.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
path: Target file path
|
|
32
|
+
data: Data to write as JSON
|
|
33
|
+
compress: Whether to compress with gzip
|
|
34
|
+
indent: JSON indentation level
|
|
35
|
+
**kwargs: Additional arguments for json.dump()
|
|
36
|
+
"""
|
|
37
|
+
path = Path(path)
|
|
38
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
|
|
40
|
+
# Write to temp file first
|
|
41
|
+
temp_path = path.with_suffix(path.suffix + ".tmp")
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
if compress:
|
|
45
|
+
with gzip.open(temp_path, "wt", encoding="utf-8") as f:
|
|
46
|
+
json.dump(data, f, indent=indent, **kwargs)
|
|
47
|
+
else:
|
|
48
|
+
with open(temp_path, "w", encoding="utf-8") as f:
|
|
49
|
+
json.dump(data, f, indent=indent, **kwargs)
|
|
50
|
+
|
|
51
|
+
# Atomic rename (works on most filesystems)
|
|
52
|
+
temp_path.replace(path)
|
|
53
|
+
except Exception:
|
|
54
|
+
# Clean up temp file on error
|
|
55
|
+
if temp_path.exists():
|
|
56
|
+
try:
|
|
57
|
+
temp_path.unlink()
|
|
58
|
+
except Exception:
|
|
59
|
+
pass
|
|
60
|
+
raise
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def is_valid_json_file(path: Path, min_size: int = 100) -> bool:
|
|
64
|
+
"""
|
|
65
|
+
Check if a file contains valid JSON and meets minimum size.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
path: File path to check
|
|
69
|
+
min_size: Minimum file size in bytes (default: 100)
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
True if file is valid JSON and meets size requirement
|
|
73
|
+
"""
|
|
74
|
+
if not path.exists():
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
# Check file size
|
|
78
|
+
try:
|
|
79
|
+
if path.stat().st_size < min_size:
|
|
80
|
+
return False
|
|
81
|
+
except OSError:
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
# Try to parse JSON
|
|
85
|
+
try:
|
|
86
|
+
if path.suffix == ".gz" or path.name.endswith(".gz"):
|
|
87
|
+
with gzip.open(path, "rt", encoding="utf-8") as f:
|
|
88
|
+
json.load(f)
|
|
89
|
+
else:
|
|
90
|
+
with open(path, encoding="utf-8") as f:
|
|
91
|
+
json.load(f)
|
|
92
|
+
return True
|
|
93
|
+
except (json.JSONDecodeError, OSError, ValueError):
|
|
94
|
+
return False
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def is_file_stable(path: Path, min_age_seconds: float = 2.0) -> bool:
|
|
98
|
+
"""
|
|
99
|
+
Check if a file has been stable (not modified recently).
|
|
100
|
+
|
|
101
|
+
This helps avoid reading files that are still being written.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
path: File path to check
|
|
105
|
+
min_age_seconds: Minimum age in seconds (default: 2.0)
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
True if file exists and hasn't been modified recently
|
|
109
|
+
"""
|
|
110
|
+
if not path.exists():
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
mtime = path.stat().st_mtime
|
|
115
|
+
age = time.time() - mtime
|
|
116
|
+
return age >= min_age_seconds
|
|
117
|
+
except OSError:
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def safe_load_json(
|
|
122
|
+
path: Path,
|
|
123
|
+
retries: int = 3,
|
|
124
|
+
backoff: float = 0.5,
|
|
125
|
+
min_age_seconds: float = 2.0,
|
|
126
|
+
min_size: int = 100,
|
|
127
|
+
) -> dict[str, Any] | None:
|
|
128
|
+
"""
|
|
129
|
+
Safely load JSON from a file with retry logic and validation.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
path: File path to load
|
|
133
|
+
retries: Number of retry attempts (default: 3)
|
|
134
|
+
backoff: Backoff multiplier between retries (default: 0.5)
|
|
135
|
+
min_age_seconds: Minimum file age before reading (default: 2.0)
|
|
136
|
+
min_size: Minimum file size in bytes (default: 100)
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Parsed JSON data or None if loading fails
|
|
140
|
+
"""
|
|
141
|
+
if not path.exists():
|
|
142
|
+
return None
|
|
143
|
+
|
|
144
|
+
# Check file size first
|
|
145
|
+
try:
|
|
146
|
+
if path.stat().st_size < min_size:
|
|
147
|
+
logger.debug(f"File {path} too small, likely incomplete")
|
|
148
|
+
return None
|
|
149
|
+
except OSError:
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
# Try loading with retries
|
|
153
|
+
for attempt in range(retries):
|
|
154
|
+
# Wait for file to be stable (except on first attempt if file is already old)
|
|
155
|
+
if attempt > 0 or not is_file_stable(path, min_age_seconds):
|
|
156
|
+
wait_time = backoff * (2 ** attempt)
|
|
157
|
+
if attempt > 0:
|
|
158
|
+
time.sleep(wait_time)
|
|
159
|
+
elif not is_file_stable(path, min_age_seconds):
|
|
160
|
+
# First attempt but file is too new, wait a bit
|
|
161
|
+
time.sleep(min_age_seconds)
|
|
162
|
+
|
|
163
|
+
# Validate JSON before attempting to load
|
|
164
|
+
if not is_valid_json_file(path, min_size):
|
|
165
|
+
if attempt < retries - 1:
|
|
166
|
+
continue # Retry
|
|
167
|
+
logger.debug(f"File {path} is not valid JSON after {attempt + 1} attempts")
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
# Try to load
|
|
171
|
+
try:
|
|
172
|
+
if path.suffix == ".gz" or path.name.endswith(".gz"):
|
|
173
|
+
with gzip.open(path, "rt", encoding="utf-8") as f:
|
|
174
|
+
return json.load(f)
|
|
175
|
+
else:
|
|
176
|
+
with open(path, encoding="utf-8") as f:
|
|
177
|
+
return json.load(f)
|
|
178
|
+
except (json.JSONDecodeError, OSError, ValueError) as e:
|
|
179
|
+
if attempt < retries - 1:
|
|
180
|
+
logger.debug(f"Failed to load {path} (attempt {attempt + 1}/{retries}): {e}")
|
|
181
|
+
continue # Retry
|
|
182
|
+
logger.debug(f"Failed to load {path} after {retries} attempts: {e}")
|
|
183
|
+
return None
|
|
184
|
+
|
|
185
|
+
return None
|
|
186
|
+
|