devsquad 4.3.0__tar.gz → 4.3.1__tar.gz
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.
- {devsquad-4.3.0 → devsquad-4.3.1}/PKG-INFO +2 -2
- {devsquad-4.3.0 → devsquad-4.3.1}/README.md +1 -1
- {devsquad-4.3.0 → devsquad-4.3.1}/devsquad.egg-info/PKG-INFO +2 -2
- {devsquad-4.3.0 → devsquad-4.3.1}/devsquad.egg-info/SOURCES.txt +1 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/pyproject.toml +1 -1
- devsquad-4.3.1/scripts/collaboration/_version.py +1 -0
- devsquad-4.3.1/scripts/collaboration/benchmark_regression_checker.py +378 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/output_validator.py +93 -1
- {devsquad-4.3.0 → devsquad-4.3.1}/skills/__init__.py +1 -1
- devsquad-4.3.0/scripts/collaboration/_version.py +0 -1
- {devsquad-4.3.0 → devsquad-4.3.1}/LICENSE +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/devsquad.egg-info/dependency_links.txt +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/devsquad.egg-info/entry_points.txt +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/devsquad.egg-info/requires.txt +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/devsquad.egg-info/top_level.txt +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/__init__.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/api/__init__.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/api/models.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/api/rate_limit.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/api/routes/__init__.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/api/routes/dispatch.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/api/routes/lifecycle.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/api/routes/metrics.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/api/routes/metrics_gates.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/api/security.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/api_server.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/auth.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/benchmark_async_dispatch.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/benchmark_ponytail_smart.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/benchmark_real_llm.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/check_async_coverage.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/check_config_consistency.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/check_constructor_params.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/check_dependency_lock.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/check_dependency_sync.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/check_hidden_content.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/check_test_pyramid.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/check_test_quality.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/check_version_consistency.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/cli/__init__.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/cli/cli_visual.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/cli.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/cli_dispatch.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/cli_lifecycle.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/cli_utils.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/__init__.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/adaptive_role_selector.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/adversarial_verify.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/agent_briefing.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/ai_semantic_matcher.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/anchor_checker.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/anti_rationalization.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/async_adapter.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/async_coordinator.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/async_llm_backend.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/audit_logger.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/autonomous/__init__.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/autonomous/git_driver.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/autonomous/loop_controller.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/autonomous/notes_memory.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/autonomous/run_state.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/autonomous/sleep_guard.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/autonomous/smart_confirmation.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/batch_scheduler.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/cache_interface.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/ccr_store.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/checkpoint_manager.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/ci_feedback_adapter.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/cli_command_classifier.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/code_graph_query.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/code_graph_storage.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/code_knowledge_graph.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/code_map_generator.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/concern_pack_loader.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/confidence_score.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/config_manager.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/consensus.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/consensus_gate.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/constants.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/content_cache.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/content_crusher.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/context_compressor.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/coordinator.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dashboard_live_mode.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/debt_collector.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dependency_hallucination_checker.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/deployment_compliance_checker.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_audit.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_component_factory.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_hooks.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_lifecycle.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_models.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_performance.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_pre_steps.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_rbac.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_result_assembler.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_services.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_steps.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_steps_base.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_steps_consensus_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_steps_feedback_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_steps_mixins.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_steps_quality_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatch_steps_services_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatcher.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatcher_async_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatcher_audit_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatcher_base.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatcher_config.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatcher_error_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatcher_factory.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatcher_lifecycle_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatcher_mixins.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatcher_status_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dispatcher_utils_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/dual_layer_context.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/enhanced_worker.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/enterprise_feature.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/event_bus.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/execution_guard.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/feature_usage_tracker.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/feedback_control_loop.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/five_axis_consensus.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/input_validator.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/intent_workflow_mapper.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/judge_agent.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/language_parsers.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/learned_rule_store.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/lifecycle_gate.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/lifecycle_protocol.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/lifecycle_shortcut_adapter.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/lifecycle_shortcut_helpers.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/lifecycle_templates.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/llm_backend.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/llm_cache.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/llm_cache_async.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/llm_cache_base.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/llm_retry.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/llm_retry_async.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/llm_retry_base.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/loop_engineering/__init__.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/loop_engineering/discovery_probe.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/loop_engineering/handoff_adapter.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/loop_engineering/independent_evaluator.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/loop_engineering/kernel.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/loop_engineering/loop_scheduler.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/loop_engineering/models.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/loop_engineering/protocols.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/loop_engineering/rollback_strategy.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/loop_engineering/unified_memory.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/mce_adapter.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/memory_bridge.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/memory_claw_source.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/memory_forgetting.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/memory_index.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/memory_query.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/memory_serializer.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/memory_types.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/meta_skill_layering.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/micro_task_planner.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/models.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/models_base.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/models_dispatch.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/models_lifecycle.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/multi_host_adapter.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/multi_level_cache.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/multi_tenant.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/null_providers.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/operation_classifier.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/output_slicer.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/performance_fingerprint.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/performance_monitor.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/permission_guard.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/plugins/__init__.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/plugins/hot_loader.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/ponytail_rule_injector.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/prometheus_metrics.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/prompt_assembler.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/prompt_assembler_base.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/prompt_assembler_formatting_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/prompt_assembler_mixins.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/prompt_assembler_substitution_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/prompt_assembler_template_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/prompt_assembler_validation_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/prompt_dials.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/protocols.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/rbac_engine.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/redesign_auditor.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/redesign_checkers.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/redis_cache.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/report_formatter.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/requirement_tracer.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/retrospective.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/review_checkers.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/role_matcher.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/role_skill_loader.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/rule_collector.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/scratchpad.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/secret_patterns.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/severity_router.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/similar_task_recommender.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/skill_extractor.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/skill_registry.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/skill_storage.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/skillifier.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/standardized_role_template.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/task_completion_checker.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/tech_debt_manager.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/test_quality_guard.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/todo_drift_monitor.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/two_stage_review_gate.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/ue_test_accessibility_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/ue_test_framework.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/ue_test_framework_base.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/ue_test_heuristic_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/ue_test_journey_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/ue_test_mixins.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/ue_test_persona_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/unified_gate_engine.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/usage_tracker.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/user_friendly_error.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/verification_gate.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/warmup_manager.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/worker.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/workflow_engine.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/workflow_engine_base.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/workflow_engine_lifecycle_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/workflow_engine_mixins.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/workflow_engine_persistence_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/workflow_engine_state_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/workflow_engine_transition_mixin.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/collaboration/yagni_checker.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/dashboard/__init__.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/dashboard/app.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/dashboard/auth_views.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/dashboard/components.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/dashboard/dag_views.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/dashboard/dispatch_views.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/dashboard/i18n.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/dashboard/lifecycle_views.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/dashboard/metrics_views.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/dashboard/state.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/dashboard/v43_panels.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/dashboard.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/generate_benchmark_report.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/history_manager.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/mcp_server.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/qa/__init__.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/qa/deterministic_rule_engine.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/qa/models.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/qa/taste_dials.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/qa/uiux_analyzer.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/qa/uiux_subitems.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/qa/visual_regression.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/scan_pickle_payloads.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/utils/__init__.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/scripts/utils/_find_missing_hints.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/setup.cfg +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/skills/_version.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/skills/dispatch/handler.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/skills/intent/handler.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/skills/prototype/handler.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/skills/registry.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/skills/retrospective/__init__.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/skills/retrospective/handler.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/skills/review/handler.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/skills/security/handler.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/skills/teach/handler.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/skills/test/__init__.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/skills/test/handler.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_4pt_grid.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_adaptive_role_selector.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_adversarial_verify.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_agent_briefing.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_anchor_retrospective.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_anti_rationalization.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_antipattern_bans.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_api_budget_status_endpoint.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_api_security.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_api_server_v362.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_async_coordinator.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_async_modules.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_audit_logger.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_auth_phase5.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_autonomous.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_benchmark_ponytail_smart.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_cache_interface.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_ccr_store.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_check_async_coverage.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_check_config_consistency.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_check_constructor_params.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_check_dependency_lock.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_check_hidden_content.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_check_test_pyramid.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_check_test_quality.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_check_version_consistency.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_ci_feedback_adapter.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_cli_command_classifier.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_cli_deep_v362.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_cli_lifecycle.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_cli_phase5.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_code_knowledge_graph.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_claw_integration_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_context_compressor_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_core_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_dispatcher_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_dispatcher_ux_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_e2e_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_enhanced_e2e_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_llm_cache_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_llm_retry_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_mce_adapter_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_memory_bridge_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_performance_monitor_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_permission_guard_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_prompt_optimization_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_role_mapping_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_skillifier_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_test_quality_guard.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_test_quality_guard_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_upstream_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_collaboration_warmup_manager_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_confidence_score.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_config_injection.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_consensus_dissent.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_consensus_fatigue.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_consensus_gate.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_content_cache.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_content_crusher.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_coordinator_smart_compression.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_dag_views.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_dashboard_live_mode.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_dashboard_split.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_dashboard_ui_e2e.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_dashboard_v43_panels.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_data_backup.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_debt_collector.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_deep_shallow.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_deletion_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_dependency_hallucination_checker.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_dependency_lock.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_deployment_compliance_checker.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_deterministic_rule_engine.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_dispatch_audit.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_dispatch_performance.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_dispatch_rbac.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_dispatch_steps.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_dispatcher_mixins.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_dispatcher_phase5_core.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_docker_deployment.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_dual_layer_context.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_enhanced_worker.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_feedback_control_loop.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_five_axis_consensus.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_flow_standalone.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_full_lifecycle_adapter.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_git_guardrails.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_glossary_loader.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_grilling_mode.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_handoff_redaction.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_history_manager_v362.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_input_validator_phase5.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_input_validator_sensitive.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_intent_workflow_mapper.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_judge_agent.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_learned_rule_phase4.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_lifecycle_protocol.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_llm_auto_fallback.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_llm_cache_protocol.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_loop_engineering.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_loop_engineering_rollback.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_mcp_server_v362.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_memory_benchmarks.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_meta_skill_layering.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_micro_task_planner.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_multi_host_adapter.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_multi_tenant.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_multi_tenant_isolation_e2e.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_no_op_test.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_node_type_separation.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_null_providers.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_oklch_color.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_operation_classifier.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_output_slicer.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_output_validator_prompt_injection.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_performance_benchmarks.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_performance_fingerprint.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_performance_monitor_protocol.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_permission_guard_human_gate.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_permission_guard_phase5.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_phase4_ghost_feature_defense.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_pipeline_step19_20.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_plan_c_unified_architecture.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_plugins_hot_loader.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_ponytail_rule_injector.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_production_features.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_prometheus_metrics.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_prompt_dials.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_protocols.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_prototype_skill.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_qa_uiux_visual_regression.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_quick_bonus.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_rate_limit.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_rbac_engine.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_rbac_fail_closed.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_red_capable_gate.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_redesign_auditor.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_redis_cache.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_requirement_tracer.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_retry_jitter_strategies.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_role_skill_loader.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_rule_collector.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_rule_injection_security.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_secret_patterns.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_severity_router.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_similar_task_recommender.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_skill_registry_coverage.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_skill_security.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_sleep_guard.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_standardized_role_template.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_start_script.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_step_node_types.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_suggested_next_steps.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_task_completion_checker.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_taste_dials.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_tautological_seams.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_teach_skill.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_tech_debt_manager.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_todo_drift_monitor.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_token_budget_compressed_scratchpad.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_triage_label.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_two_stage_review_gate.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_ue_test_framework.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_uiux_subitems.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_usage_tracker_coverage.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_v39_e2e.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_v39_performance.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_v411_security_hardening.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_verification_gate.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_version.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_vertical_slice.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_workflow_engine.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_workflow_engine_base.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_workflow_engine_lifecycle.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_workflow_engine_state.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_workflow_engine_transition.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_workflow_persistence_coverage.py +0 -0
- {devsquad-4.3.0 → devsquad-4.3.1}/tests/test_yagni_checker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devsquad
|
|
3
|
-
Version: 4.3.
|
|
3
|
+
Version: 4.3.1
|
|
4
4
|
Summary: Production-Ready Multi-Role AI Task Orchestrator
|
|
5
5
|
Author-email: DevSquad Team <devsquad@example.com>
|
|
6
6
|
License: MIT
|
|
@@ -103,7 +103,7 @@ Dynamic: license-file
|
|
|
103
103
|
<img alt="Python" src="https://img.shields.io/badge/Python-3.10+-blue?logo=python&logoColor=white" />
|
|
104
104
|
<img alt="License" src="https://img.shields.io/badge/License-MIT-green" />
|
|
105
105
|
<img alt="Tests" src="https://img.shields.io/badge/Tests-7681%20passing-brightgreen" />
|
|
106
|
-
<img alt="Version" src="https://img.shields.io/badge/V4.3.
|
|
106
|
+
<img alt="Version" src="https://img.shields.io/badge/V4.3.1-success" />
|
|
107
107
|
<img alt="CI" src="https://img.shields.io/badge/CI-GitHub_Actions-blue?logo=githubactions" />
|
|
108
108
|
<img alt="Quality" src="https://img.shields.io/badge/Code%20Quality-4.3%2F5%20%E2%98%85%E2%98%85%E2%98%85%E2%98%85%E2%98%86-blue" />
|
|
109
109
|
<img alt="Security" src="https://img.shields.io/badge/Security-5%2F5%20%E2%98%85%E2%98%85%E2%98%85%E2%98%85%E2%98%85-success" />
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<img alt="Python" src="https://img.shields.io/badge/Python-3.10+-blue?logo=python&logoColor=white" />
|
|
11
11
|
<img alt="License" src="https://img.shields.io/badge/License-MIT-green" />
|
|
12
12
|
<img alt="Tests" src="https://img.shields.io/badge/Tests-7681%20passing-brightgreen" />
|
|
13
|
-
<img alt="Version" src="https://img.shields.io/badge/V4.3.
|
|
13
|
+
<img alt="Version" src="https://img.shields.io/badge/V4.3.1-success" />
|
|
14
14
|
<img alt="CI" src="https://img.shields.io/badge/CI-GitHub_Actions-blue?logo=githubactions" />
|
|
15
15
|
<img alt="Quality" src="https://img.shields.io/badge/Code%20Quality-4.3%2F5%20%E2%98%85%E2%98%85%E2%98%85%E2%98%85%E2%98%86-blue" />
|
|
16
16
|
<img alt="Security" src="https://img.shields.io/badge/Security-5%2F5%20%E2%98%85%E2%98%85%E2%98%85%E2%98%85%E2%98%85-success" />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devsquad
|
|
3
|
-
Version: 4.3.
|
|
3
|
+
Version: 4.3.1
|
|
4
4
|
Summary: Production-Ready Multi-Role AI Task Orchestrator
|
|
5
5
|
Author-email: DevSquad Team <devsquad@example.com>
|
|
6
6
|
License: MIT
|
|
@@ -103,7 +103,7 @@ Dynamic: license-file
|
|
|
103
103
|
<img alt="Python" src="https://img.shields.io/badge/Python-3.10+-blue?logo=python&logoColor=white" />
|
|
104
104
|
<img alt="License" src="https://img.shields.io/badge/License-MIT-green" />
|
|
105
105
|
<img alt="Tests" src="https://img.shields.io/badge/Tests-7681%20passing-brightgreen" />
|
|
106
|
-
<img alt="Version" src="https://img.shields.io/badge/V4.3.
|
|
106
|
+
<img alt="Version" src="https://img.shields.io/badge/V4.3.1-success" />
|
|
107
107
|
<img alt="CI" src="https://img.shields.io/badge/CI-GitHub_Actions-blue?logo=githubactions" />
|
|
108
108
|
<img alt="Quality" src="https://img.shields.io/badge/Code%20Quality-4.3%2F5%20%E2%98%85%E2%98%85%E2%98%85%E2%98%85%E2%98%86-blue" />
|
|
109
109
|
<img alt="Security" src="https://img.shields.io/badge/Security-5%2F5%20%E2%98%85%E2%98%85%E2%98%85%E2%98%85%E2%98%85-success" />
|
|
@@ -55,6 +55,7 @@ scripts/collaboration/async_coordinator.py
|
|
|
55
55
|
scripts/collaboration/async_llm_backend.py
|
|
56
56
|
scripts/collaboration/audit_logger.py
|
|
57
57
|
scripts/collaboration/batch_scheduler.py
|
|
58
|
+
scripts/collaboration/benchmark_regression_checker.py
|
|
58
59
|
scripts/collaboration/cache_interface.py
|
|
59
60
|
scripts/collaboration/ccr_store.py
|
|
60
61
|
scripts/collaboration/checkpoint_manager.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "4.3.1"
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""BenchmarkRegressionChecker -- V4.3.1 Phase 1 P1-1.
|
|
3
|
+
|
|
4
|
+
P11 lifecycle gate checker that detects performance benchmark
|
|
5
|
+
regressions by comparing a current snapshot against a baseline.
|
|
6
|
+
|
|
7
|
+
Architecture reference: docs/analysis/2026-07-25_V4.3.1_plan.md sec 3.1
|
|
8
|
+
Test plan: tests/unit/test_benchmark_regression_checker.py
|
|
9
|
+
|
|
10
|
+
Skill integration (anti-ghost feature):
|
|
11
|
+
- Integration point: ``unified_gate_engine.py`` P11 lifecycle gate
|
|
12
|
+
- Trigger: dispatcher auto-invokes via
|
|
13
|
+
``lifecycle_gate_check(phase="P11", ...)``
|
|
14
|
+
- User visibility: Markdown report "Benchmark Regression" section
|
|
15
|
+
- CI check: module call count > 0 (verified by
|
|
16
|
+
``check_module_activation.py``)
|
|
17
|
+
|
|
18
|
+
Regression model
|
|
19
|
+
----------------
|
|
20
|
+
For each metric present in both snapshots::
|
|
21
|
+
|
|
22
|
+
regression_percent = (current - baseline) / baseline * 100
|
|
23
|
+
|
|
24
|
+
Positive values indicate slowdown (regression); negative values
|
|
25
|
+
indicate speedup (improvement). A metric is flagged as regressed when
|
|
26
|
+
its regression percentage strictly exceeds ``threshold_percent``. The
|
|
27
|
+
report's ``regression_percent`` is the maximum across all comparable
|
|
28
|
+
metrics, so a single slow metric raises the overall number even if
|
|
29
|
+
others improved.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import logging
|
|
35
|
+
import time
|
|
36
|
+
from dataclasses import dataclass, field
|
|
37
|
+
from typing import Literal
|
|
38
|
+
|
|
39
|
+
from scripts.collaboration._version import __version__
|
|
40
|
+
|
|
41
|
+
logger = logging.getLogger(__name__)
|
|
42
|
+
|
|
43
|
+
RegressionLevel = Literal["none", "minor", "major", "critical"]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(slots=True)
|
|
47
|
+
class BenchmarkMetric:
|
|
48
|
+
"""A single benchmark measurement.
|
|
49
|
+
|
|
50
|
+
Attributes
|
|
51
|
+
----------
|
|
52
|
+
name:
|
|
53
|
+
Metric identifier (e.g. ``"dispatch_p95_ms"``,
|
|
54
|
+
``"memory_peak_mb"``).
|
|
55
|
+
value:
|
|
56
|
+
Measured value (float for latencies, int for counts).
|
|
57
|
+
unit:
|
|
58
|
+
Unit string (e.g. ``"ms"``, ``"MB"``, ``"ops/sec"``).
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
name: str
|
|
62
|
+
value: float
|
|
63
|
+
unit: str
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(slots=True)
|
|
67
|
+
class BenchmarkSnapshot:
|
|
68
|
+
"""A versioned benchmark snapshot.
|
|
69
|
+
|
|
70
|
+
Attributes
|
|
71
|
+
----------
|
|
72
|
+
version:
|
|
73
|
+
DevSquad version string (e.g. ``"4.3.0"``).
|
|
74
|
+
timestamp:
|
|
75
|
+
Unix timestamp of snapshot collection.
|
|
76
|
+
metrics:
|
|
77
|
+
List of :class:`BenchmarkMetric` instances.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
version: str
|
|
81
|
+
timestamp: float
|
|
82
|
+
metrics: list[BenchmarkMetric] = field(default_factory=list)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass(slots=True)
|
|
86
|
+
class BenchmarkReport:
|
|
87
|
+
"""Result of comparing current metrics against a baseline.
|
|
88
|
+
|
|
89
|
+
Attributes
|
|
90
|
+
----------
|
|
91
|
+
regression_detected:
|
|
92
|
+
``True`` if any metric regressed beyond its threshold.
|
|
93
|
+
regression_percent:
|
|
94
|
+
Maximum regression percentage across all comparable metrics
|
|
95
|
+
(``0.0`` when no metrics overlap). Positive value means
|
|
96
|
+
slowdown; negative means speedup.
|
|
97
|
+
regressed_metrics:
|
|
98
|
+
Names of metrics that exceeded the regression threshold.
|
|
99
|
+
baseline_version:
|
|
100
|
+
Version string of the baseline snapshot.
|
|
101
|
+
current_version:
|
|
102
|
+
Version string of the current snapshot.
|
|
103
|
+
threshold_percent:
|
|
104
|
+
Regression threshold in percent (default ``10.0``).
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
regression_detected: bool
|
|
108
|
+
regression_percent: float
|
|
109
|
+
regressed_metrics: list[str] = field(default_factory=list)
|
|
110
|
+
baseline_version: str = ""
|
|
111
|
+
current_version: str = ""
|
|
112
|
+
threshold_percent: float = 10.0
|
|
113
|
+
|
|
114
|
+
def to_markdown(self) -> str:
|
|
115
|
+
"""Render the report as a Markdown section.
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
119
|
+
str
|
|
120
|
+
Markdown text with a ``Benchmark Regression`` header and
|
|
121
|
+
fields for baseline/current version, threshold, max
|
|
122
|
+
regression percentage, regressed metric names, and a
|
|
123
|
+
status line.
|
|
124
|
+
"""
|
|
125
|
+
status = "REGRESSION DETECTED" if self.regression_detected else "OK"
|
|
126
|
+
regressed_str = (
|
|
127
|
+
", ".join(self.regressed_metrics) if self.regressed_metrics else "(none)"
|
|
128
|
+
)
|
|
129
|
+
lines = [
|
|
130
|
+
"## Benchmark Regression",
|
|
131
|
+
"",
|
|
132
|
+
f"- **Baseline version**: {self.baseline_version}",
|
|
133
|
+
f"- **Current version**: {self.current_version}",
|
|
134
|
+
f"- **Threshold**: {self.threshold_percent}%",
|
|
135
|
+
f"- **Max regression**: {self.regression_percent}%",
|
|
136
|
+
f"- **Regressed metrics**: {regressed_str}",
|
|
137
|
+
f"- **Status**: {status}",
|
|
138
|
+
]
|
|
139
|
+
return "\n".join(lines)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _compute_regression_percent(baseline_value: float, current_value: float) -> float:
|
|
143
|
+
"""Compute the regression percentage for a single metric.
|
|
144
|
+
|
|
145
|
+
Regression percent = ``(current - baseline) / baseline * 100``.
|
|
146
|
+
Positive means slowdown; negative means speedup. When the
|
|
147
|
+
baseline is ``0``, returns ``0.0`` to avoid division-by-zero
|
|
148
|
+
(a zero baseline carries no meaningful ratio).
|
|
149
|
+
|
|
150
|
+
Parameters
|
|
151
|
+
----------
|
|
152
|
+
baseline_value:
|
|
153
|
+
The baseline metric value.
|
|
154
|
+
current_value:
|
|
155
|
+
The current metric value.
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
float
|
|
160
|
+
The regression percentage.
|
|
161
|
+
"""
|
|
162
|
+
if baseline_value == 0:
|
|
163
|
+
return 0.0
|
|
164
|
+
return (current_value - baseline_value) / baseline_value * 100.0
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class BenchmarkRegressionChecker:
|
|
168
|
+
"""Detects performance benchmark regressions between two snapshots.
|
|
169
|
+
|
|
170
|
+
Compares a current :class:`BenchmarkSnapshot` against a baseline
|
|
171
|
+
and flags any metric whose value increased (slowed down) beyond a
|
|
172
|
+
configurable threshold.
|
|
173
|
+
|
|
174
|
+
Parameters
|
|
175
|
+
----------
|
|
176
|
+
threshold_percent:
|
|
177
|
+
Regression threshold in percent. A metric is flagged when its
|
|
178
|
+
regression percentage strictly exceeds this value. Defaults to
|
|
179
|
+
``10.0`` (a 10% slowdown is tolerated, but ``10.01%`` is
|
|
180
|
+
flagged).
|
|
181
|
+
|
|
182
|
+
Example
|
|
183
|
+
-------
|
|
184
|
+
>>> checker = BenchmarkRegressionChecker(threshold_percent=10.0)
|
|
185
|
+
>>> baseline = BenchmarkSnapshot("4.2.9", 0.0, [
|
|
186
|
+
... BenchmarkMetric("dispatch_p95_ms", 100.0, "ms"),
|
|
187
|
+
... ])
|
|
188
|
+
>>> current = BenchmarkSnapshot("4.3.0", 0.0, [
|
|
189
|
+
... BenchmarkMetric("dispatch_p95_ms", 125.0, "ms"),
|
|
190
|
+
... ])
|
|
191
|
+
>>> report = checker.compare(baseline, current)
|
|
192
|
+
>>> report.regression_detected
|
|
193
|
+
True
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
def __init__(self, threshold_percent: float = 10.0) -> None:
|
|
197
|
+
self.threshold_percent = threshold_percent
|
|
198
|
+
|
|
199
|
+
def compare(
|
|
200
|
+
self,
|
|
201
|
+
baseline: BenchmarkSnapshot,
|
|
202
|
+
current: BenchmarkSnapshot,
|
|
203
|
+
) -> BenchmarkReport:
|
|
204
|
+
"""Compare two snapshots and return a regression report.
|
|
205
|
+
|
|
206
|
+
Only metrics present in both snapshots are compared. The
|
|
207
|
+
report's ``regression_percent`` is the maximum regression
|
|
208
|
+
percentage across all comparable metrics (``0.0`` when no
|
|
209
|
+
metrics overlap). A metric is added to ``regressed_metrics``
|
|
210
|
+
when its regression percentage strictly exceeds
|
|
211
|
+
``threshold_percent``.
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
baseline:
|
|
216
|
+
The baseline snapshot to compare against.
|
|
217
|
+
current:
|
|
218
|
+
The current snapshot to evaluate.
|
|
219
|
+
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
BenchmarkReport
|
|
223
|
+
The regression report with version fields populated from
|
|
224
|
+
the snapshots.
|
|
225
|
+
"""
|
|
226
|
+
baseline_map = {m.name: m.value for m in baseline.metrics}
|
|
227
|
+
current_map = {m.name: m.value for m in current.metrics}
|
|
228
|
+
|
|
229
|
+
regressions: list[tuple[str, float]] = []
|
|
230
|
+
for name, current_value in current_map.items():
|
|
231
|
+
if name not in baseline_map:
|
|
232
|
+
continue
|
|
233
|
+
baseline_value = baseline_map[name]
|
|
234
|
+
regression = _compute_regression_percent(baseline_value, current_value)
|
|
235
|
+
regressions.append((name, regression))
|
|
236
|
+
|
|
237
|
+
if not regressions:
|
|
238
|
+
max_regression = 0.0
|
|
239
|
+
regressed_names: list[str] = []
|
|
240
|
+
else:
|
|
241
|
+
max_regression = max(r for _, r in regressions)
|
|
242
|
+
regressed_names = [
|
|
243
|
+
name for name, r in regressions if r > self.threshold_percent
|
|
244
|
+
]
|
|
245
|
+
|
|
246
|
+
return BenchmarkReport(
|
|
247
|
+
regression_detected=len(regressed_names) > 0,
|
|
248
|
+
regression_percent=max_regression,
|
|
249
|
+
regressed_metrics=regressed_names,
|
|
250
|
+
baseline_version=baseline.version,
|
|
251
|
+
current_version=current.version,
|
|
252
|
+
threshold_percent=self.threshold_percent,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
def run_live_benchmark(self) -> BenchmarkSnapshot:
|
|
256
|
+
"""Run a live benchmark and return the snapshot.
|
|
257
|
+
|
|
258
|
+
This is a mock implementation that returns representative
|
|
259
|
+
metrics. In production, this would invoke the actual benchmark
|
|
260
|
+
suite (e.g. ``pytest --benchmark``). The mock metrics model a
|
|
261
|
+
typical dispatch pipeline: ``dispatch_p95_ms`` and
|
|
262
|
+
``memory_peak_mb``.
|
|
263
|
+
|
|
264
|
+
Returns
|
|
265
|
+
-------
|
|
266
|
+
BenchmarkSnapshot
|
|
267
|
+
A snapshot tagged with the current package version, the
|
|
268
|
+
current wall-clock timestamp, and mock metrics
|
|
269
|
+
(``dispatch_p95_ms=120.0ms``, ``memory_peak_mb=200.0MB``).
|
|
270
|
+
"""
|
|
271
|
+
metrics = [
|
|
272
|
+
BenchmarkMetric("dispatch_p95_ms", 120.0, "ms"),
|
|
273
|
+
BenchmarkMetric("memory_peak_mb", 200.0, "MB"),
|
|
274
|
+
]
|
|
275
|
+
return BenchmarkSnapshot(
|
|
276
|
+
version=__version__,
|
|
277
|
+
timestamp=time.time(),
|
|
278
|
+
metrics=metrics,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _default_baseline(baseline_version: str) -> BenchmarkSnapshot:
|
|
283
|
+
"""Build the default baseline snapshot for lifecycle_gate_check.
|
|
284
|
+
|
|
285
|
+
Parameters
|
|
286
|
+
----------
|
|
287
|
+
baseline_version:
|
|
288
|
+
Version string to embed in the baseline snapshot.
|
|
289
|
+
|
|
290
|
+
Returns
|
|
291
|
+
-------
|
|
292
|
+
BenchmarkSnapshot
|
|
293
|
+
Baseline with ``dispatch_p95_ms=100.0ms`` and
|
|
294
|
+
``memory_peak_mb=200.0MB``.
|
|
295
|
+
"""
|
|
296
|
+
return BenchmarkSnapshot(
|
|
297
|
+
version=baseline_version,
|
|
298
|
+
timestamp=0.0,
|
|
299
|
+
metrics=[
|
|
300
|
+
BenchmarkMetric("dispatch_p95_ms", 100.0, "ms"),
|
|
301
|
+
BenchmarkMetric("memory_peak_mb", 200.0, "MB"),
|
|
302
|
+
],
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def lifecycle_gate_check(
|
|
307
|
+
phase: str,
|
|
308
|
+
baseline_version: str,
|
|
309
|
+
current_version: str | None = None,
|
|
310
|
+
threshold_percent: float = 10.0,
|
|
311
|
+
baseline_snapshot: BenchmarkSnapshot | None = None,
|
|
312
|
+
current_snapshot: BenchmarkSnapshot | None = None,
|
|
313
|
+
) -> BenchmarkReport:
|
|
314
|
+
"""P11 lifecycle gate entry point.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
phase: Lifecycle phase, must be ``"P11"``.
|
|
318
|
+
baseline_version: Version string of the baseline.
|
|
319
|
+
current_version: Current version (defaults to package
|
|
320
|
+
``__version__``).
|
|
321
|
+
threshold_percent: Regression threshold in percent.
|
|
322
|
+
baseline_snapshot: Injected baseline (for testing). If
|
|
323
|
+
``None``, uses the default baseline (100ms / 200MB).
|
|
324
|
+
current_snapshot: Injected current metrics (for testing). If
|
|
325
|
+
``None``, runs the live benchmark.
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
BenchmarkReport with ``regression_detected`` flag. The
|
|
329
|
+
``baseline_version`` / ``current_version`` fields are set from
|
|
330
|
+
the function arguments (parameters take precedence over
|
|
331
|
+
snapshot-embedded versions).
|
|
332
|
+
|
|
333
|
+
Raises:
|
|
334
|
+
ValueError: If ``phase != "P11"``.
|
|
335
|
+
|
|
336
|
+
Example
|
|
337
|
+
-------
|
|
338
|
+
>>> report = lifecycle_gate_check(
|
|
339
|
+
... phase="P11",
|
|
340
|
+
... baseline_version="4.2.9",
|
|
341
|
+
... current_version="4.3.0",
|
|
342
|
+
... )
|
|
343
|
+
>>> isinstance(report, BenchmarkReport)
|
|
344
|
+
True
|
|
345
|
+
"""
|
|
346
|
+
if phase != "P11":
|
|
347
|
+
raise ValueError("BenchmarkRegressionChecker only supports P11 phase")
|
|
348
|
+
|
|
349
|
+
resolved_current_version = (
|
|
350
|
+
current_version if current_version is not None else __version__
|
|
351
|
+
)
|
|
352
|
+
baseline = (
|
|
353
|
+
baseline_snapshot
|
|
354
|
+
if baseline_snapshot is not None
|
|
355
|
+
else _default_baseline(baseline_version)
|
|
356
|
+
)
|
|
357
|
+
current = (
|
|
358
|
+
current_snapshot
|
|
359
|
+
if current_snapshot is not None
|
|
360
|
+
else BenchmarkRegressionChecker().run_live_benchmark()
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
checker = BenchmarkRegressionChecker(threshold_percent)
|
|
364
|
+
report = checker.compare(baseline, current)
|
|
365
|
+
# Function parameters take precedence over snapshot-embedded versions.
|
|
366
|
+
report.baseline_version = baseline_version
|
|
367
|
+
report.current_version = resolved_current_version
|
|
368
|
+
return report
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
__all__ = [
|
|
372
|
+
"BenchmarkMetric",
|
|
373
|
+
"BenchmarkSnapshot",
|
|
374
|
+
"BenchmarkReport",
|
|
375
|
+
"RegressionLevel",
|
|
376
|
+
"lifecycle_gate_check",
|
|
377
|
+
"BenchmarkRegressionChecker",
|
|
378
|
+
]
|
|
@@ -29,6 +29,7 @@ deferred to Phase 3.
|
|
|
29
29
|
|
|
30
30
|
from __future__ import annotations
|
|
31
31
|
|
|
32
|
+
import base64
|
|
32
33
|
import logging
|
|
33
34
|
import re
|
|
34
35
|
from dataclasses import dataclass, field
|
|
@@ -37,7 +38,14 @@ from typing import Literal
|
|
|
37
38
|
logger = logging.getLogger(__name__)
|
|
38
39
|
|
|
39
40
|
|
|
40
|
-
FindingCategory = Literal[
|
|
41
|
+
FindingCategory = Literal[
|
|
42
|
+
"code_injection",
|
|
43
|
+
"sensitive_info",
|
|
44
|
+
"path_leak",
|
|
45
|
+
"prompt_injection",
|
|
46
|
+
"base64_encoded_leak",
|
|
47
|
+
"unicode_homoglyph",
|
|
48
|
+
]
|
|
41
49
|
FindingSeverity = Literal["low", "medium", "high"]
|
|
42
50
|
|
|
43
51
|
|
|
@@ -213,11 +221,44 @@ class OutputValidator:
|
|
|
213
221
|
(r"(?i)\bshutdown\s+(?:now|immediately|-h\s+now)\b", "destructive_shutdown", "high"),
|
|
214
222
|
]
|
|
215
223
|
|
|
224
|
+
# V4.3.1: Base64-encoded sensitive content leakage detection.
|
|
225
|
+
# Strategy: detect JWT-like 3-segment base64 (eyJ prefix) and long
|
|
226
|
+
# base64 blobs (>=64 chars). For long blobs, attempt to decode and
|
|
227
|
+
# check if decoded content contains known sensitive patterns.
|
|
228
|
+
BASE64_ENCODED_LEAK_PATTERNS: list[tuple[str, str, FindingSeverity]] = [
|
|
229
|
+
# JWT-like 3-segment base64 (eyJ prefix)
|
|
230
|
+
(r"\beyJ[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\.[A-Za-z0-9_\-]{8,}\b",
|
|
231
|
+
"base64_jwt_token", "high"),
|
|
232
|
+
# Long base64 string (>=64 chars, may encode keys/credentials)
|
|
233
|
+
(r"\b[A-Za-z0-9+/]{64,}={0,2}\b", "base64_long_blob", "medium"),
|
|
234
|
+
]
|
|
235
|
+
|
|
236
|
+
# V4.3.1: Unicode homoglyph attack detection.
|
|
237
|
+
# Strategy: detect common confusable characters (Cyrillic/Greek
|
|
238
|
+
# substitutes for Latin letters) using a static whitelist (no
|
|
239
|
+
# external dependency on a confusables database).
|
|
240
|
+
UNICODE_HOMOGLYPH_PATTERNS: list[tuple[str, str, FindingSeverity]] = [
|
|
241
|
+
# Cyrillic 'a' (U+0430 lowercase, U+0410 uppercase) for Latin 'a'
|
|
242
|
+
(r"[\u0430\u0410]", "homoglyph_cyrillic_a", "medium"),
|
|
243
|
+
# Cyrillic 'e' (U+0435 lowercase, U+0415 uppercase) for Latin 'e'
|
|
244
|
+
(r"[\u0435\u0415]", "homoglyph_cyrillic_e", "medium"),
|
|
245
|
+
# Cyrillic 'o' (U+043E lowercase, U+041E uppercase) for Latin 'o'
|
|
246
|
+
(r"[\u043e\u041e]", "homoglyph_cyrillic_o", "medium"),
|
|
247
|
+
# Cyrillic 'p' (U+0440 lowercase, U+0420 uppercase) for Latin 'p'
|
|
248
|
+
(r"[\u0440\u0420]", "homoglyph_cyrillic_p", "medium"),
|
|
249
|
+
# Cyrillic 'c' (U+0441 lowercase, U+0421 uppercase) for Latin 'c'
|
|
250
|
+
(r"[\u0441\u0421]", "homoglyph_cyrillic_c", "medium"),
|
|
251
|
+
# Greek 'o' (U+03BF lowercase, U+039F uppercase) for Latin 'o'
|
|
252
|
+
(r"[\u03bf\u039f]", "homoglyph_greek_o", "medium"),
|
|
253
|
+
]
|
|
254
|
+
|
|
216
255
|
# Compiled pattern cache (class-level for reuse).
|
|
217
256
|
_COMPILED_CODE_INJECTION: list[tuple[re.Pattern[str], str, FindingSeverity]] = []
|
|
218
257
|
_COMPILED_SENSITIVE_INFO: list[tuple[re.Pattern[str], str, FindingSeverity]] = []
|
|
219
258
|
_COMPILED_PATH_LEAK: list[tuple[re.Pattern[str], str, FindingSeverity]] = []
|
|
220
259
|
_COMPILED_PROMPT_INJECTION: list[tuple[re.Pattern[str], str, FindingSeverity]] = []
|
|
260
|
+
_COMPILED_BASE64: list[tuple[re.Pattern[str], str, FindingSeverity]] = []
|
|
261
|
+
_COMPILED_HOMOGLYPH: list[tuple[re.Pattern[str], str, FindingSeverity]] = []
|
|
221
262
|
|
|
222
263
|
def __init__(self) -> None:
|
|
223
264
|
# Compile once at instance construction (deferred from class-level
|
|
@@ -235,6 +276,12 @@ class OutputValidator:
|
|
|
235
276
|
OutputValidator._COMPILED_PROMPT_INJECTION = [
|
|
236
277
|
(re.compile(p), name, sev) for p, name, sev in self.PROMPT_INJECTION_PATTERNS
|
|
237
278
|
]
|
|
279
|
+
OutputValidator._COMPILED_BASE64 = [
|
|
280
|
+
(re.compile(p), name, sev) for p, name, sev in self.BASE64_ENCODED_LEAK_PATTERNS
|
|
281
|
+
]
|
|
282
|
+
OutputValidator._COMPILED_HOMOGLYPH = [
|
|
283
|
+
(re.compile(p), name, sev) for p, name, sev in self.UNICODE_HOMOGLYPH_PATTERNS
|
|
284
|
+
]
|
|
238
285
|
|
|
239
286
|
# ------------------------------------------------------------------
|
|
240
287
|
# Public API
|
|
@@ -256,6 +303,8 @@ class OutputValidator:
|
|
|
256
303
|
findings.extend(self._scan_sensitive_info(text))
|
|
257
304
|
findings.extend(self._scan_path_leak(text))
|
|
258
305
|
findings.extend(self._scan_prompt_injection(text))
|
|
306
|
+
findings.extend(self._scan_base64(text))
|
|
307
|
+
findings.extend(self._scan_unicode_homoglyph(text))
|
|
259
308
|
|
|
260
309
|
# Sort by span start so redaction offsets are processed left-to-right.
|
|
261
310
|
findings.sort(key=lambda f: f.span[0])
|
|
@@ -288,6 +337,49 @@ class OutputValidator:
|
|
|
288
337
|
def _scan_prompt_injection(self, text: str) -> list[OutputFinding]:
|
|
289
338
|
return self._scan(text, self._COMPILED_PROMPT_INJECTION, "prompt_injection")
|
|
290
339
|
|
|
340
|
+
def _scan_base64(self, text: str) -> list[OutputFinding]:
|
|
341
|
+
"""Scan for base64-encoded sensitive content.
|
|
342
|
+
|
|
343
|
+
For long base64 blobs, attempt to decode and check if the decoded
|
|
344
|
+
content contains known sensitive patterns (sk-/password=/AKIA...).
|
|
345
|
+
If decoded content matches sensitive patterns, escalate severity
|
|
346
|
+
from medium to high.
|
|
347
|
+
|
|
348
|
+
Fail-secure: if base64 decode fails, keep medium severity.
|
|
349
|
+
"""
|
|
350
|
+
findings = self._scan(text, self._COMPILED_BASE64, "base64_encoded_leak")
|
|
351
|
+
escalated: list[OutputFinding] = []
|
|
352
|
+
for f in findings:
|
|
353
|
+
if f.pattern_name == "base64_long_blob" and f.severity == "medium":
|
|
354
|
+
try:
|
|
355
|
+
decoded_bytes = base64.b64decode(f.matched_text, validate=True)
|
|
356
|
+
decoded = decoded_bytes.decode("utf-8", errors="ignore")
|
|
357
|
+
if (
|
|
358
|
+
re.search(r"sk-[A-Za-z0-9]{20,}", decoded)
|
|
359
|
+
or re.search(r"(?i)password\s*[=:]", decoded)
|
|
360
|
+
or re.search(r"AKIA[0-9A-Z]{16}", decoded)
|
|
361
|
+
):
|
|
362
|
+
escalated.append(
|
|
363
|
+
OutputFinding(
|
|
364
|
+
category=f.category,
|
|
365
|
+
severity="high",
|
|
366
|
+
pattern_name=f.pattern_name + "_sensitive",
|
|
367
|
+
matched_text=f.matched_text,
|
|
368
|
+
redacted_text=f.redacted_text,
|
|
369
|
+
span=f.span,
|
|
370
|
+
)
|
|
371
|
+
)
|
|
372
|
+
continue
|
|
373
|
+
except Exception:
|
|
374
|
+
# Fail-secure: keep medium severity if decode fails.
|
|
375
|
+
pass
|
|
376
|
+
escalated.append(f)
|
|
377
|
+
return escalated
|
|
378
|
+
|
|
379
|
+
def _scan_unicode_homoglyph(self, text: str) -> list[OutputFinding]:
|
|
380
|
+
"""Scan for Unicode homoglyph attacks."""
|
|
381
|
+
return self._scan(text, self._COMPILED_HOMOGLYPH, "unicode_homoglyph")
|
|
382
|
+
|
|
291
383
|
@staticmethod
|
|
292
384
|
def _scan(
|
|
293
385
|
text: str,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "4.3.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|